1+ /*
2+ * This file is part of takenaka, licensed under the Apache License, Version 2.0 (the "License").
3+ *
4+ * Copyright (c) 2023-2024 Matous Kucera
5+ *
6+ * You may not use this file except in compliance with the License.
7+ * You may obtain a copy of the License at
8+ *
9+ * http://www.apache.org/licenses/LICENSE-2.0
10+ *
11+ * Unless required by applicable law or agreed to in writing, software
12+ * distributed under the License is distributed on an "AS IS" BASIS,
13+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+ * See the License for the specific language governing permissions and
15+ * limitations under the License.
16+ */
17+
118package me.kcra.takenaka.generator.web.util
219
20+ /* *
21+ * A `<` (less than) character encoded for HTML usage.
22+ */
23+ const val HTML_LT = " <"
24+
25+ /* *
26+ * A `>` (greater than) character encoded for HTML usage.
27+ */
28+ const val HTML_GT = " >"
29+
30+ /* *
31+ * Escapes `<` and `>` characters in a string for HTML usage.
32+ *
33+ * @return the escaped string
34+ */
35+ fun String.escapeHtml (): String = replace(" <" , HTML_LT ).replace(" >" , HTML_GT )
36+
37+ /* *
38+ * A `"` (double quotation mark) character encoded for HTML usage.
39+ */
40+ const val HTML_QUOT = " ""
41+
42+ /* *
43+ * Escapes the `"` character in a string for usage in an HTML tag attribute value.
44+ *
45+ * @return the escaped string
46+ */
47+ fun String.escapeHtmlAttribute (): String = replace(" \" " , HTML_QUOT )
48+
349/* *
450 * Void element tag names.
551 *
@@ -12,21 +58,24 @@ val VOID_ELEMENTS = setOf("area", "base", "br", "col", "embed", "hr", "img", "in
1258 */
1359typealias HTMLBuilder = StringBuilder
1460
61+ /* *
62+ * An HTML builder action.
63+ */
64+ typealias HTMLBuilderBlock = HTMLBuilder .() -> Unit
65+
1566/* *
1667 * Builds an HTML document.
1768 *
1869 * @param docType whether a DOCTYPE should be prepended
1970 * @param block the builder action
2071 * @return the built document
2172 */
22- inline fun buildHTML (docType : Boolean = true, block : HTMLBuilder .() -> Unit ): String {
23- return buildString {
24- if (docType) {
25- append(" <!DOCTYPE html>" )
26- }
27-
28- block(this )
73+ inline fun buildHTML (docType : Boolean = true, block : HTMLBuilderBlock ): String = buildString {
74+ if (docType) {
75+ append(" <!DOCTYPE html>" )
2976 }
77+
78+ block(this )
3079}
3180
3281/* *
@@ -39,12 +88,15 @@ inline fun buildHTML(docType: Boolean = true, block: HTMLBuilder.() -> Unit): St
3988inline fun HTMLBuilder.tag (
4089 name : String ,
4190 attrs : Map <String , Any ?> = emptyMap(),
42- block : HTMLBuilder .() -> Unit = {},
91+ block : HTMLBuilderBlock = {},
4392) {
44- append(" <${ name} " )
93+ append(" <$name " )
4594 attrs.forEach { (key, value) ->
46- if (value != null ) {
47- append(" ${key} =\" ${value} \" " )
95+ if (value == null || value == false ) return @forEach
96+
97+ append(" $key " ) // https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML
98+ if (value !is Boolean ) {
99+ append(" =\" ${value.toString().escapeHtmlAttribute()} \" " )
48100 }
49101 }
50102
@@ -54,7 +106,7 @@ inline fun HTMLBuilder.tag(
54106 append(" >" )
55107
56108 block(this )
57- append(" </${ name} >" )
109+ append(" </$name >" )
58110 }
59111}
60112
@@ -64,57 +116,57 @@ inline fun HTMLBuilder.tag(
64116 * @param lang the `lang` attribute
65117 * @param block the builder action
66118 */
67- inline fun HTMLBuilder.html (lang : String? = null, block : HTMLBuilder .() -> Unit = {}) =
119+ inline fun HTMLBuilder.html (lang : String? = null, block : HTMLBuilderBlock = {}) =
68120 tag(" html" , mapOf (" lang" to lang), block)
69121
70122/* *
71123 * Appends a `<head>` tag.
72124 *
73125 * @param block the builder action
74126 */
75- inline fun HTMLBuilder.head (block : HTMLBuilder .() -> Unit = {}) = tag(" head" , block = block)
127+ inline fun HTMLBuilder.head (block : HTMLBuilderBlock = {}) = tag(" head" , block = block)
76128
77129/* *
78130 * Appends a `<body>` tag.
79131 *
80132 * @param block the builder action
81133 */
82- inline fun HTMLBuilder.body (block : HTMLBuilder .() -> Unit = {}) = tag(" body" , block = block)
134+ inline fun HTMLBuilder.body (block : HTMLBuilderBlock = {}) = tag(" body" , block = block)
83135
84136/* *
85137 * Appends a `<title>` tag.
86138 *
87139 * @param block the builder action
88140 */
89- inline fun HTMLBuilder.title (block : HTMLBuilder .() -> Unit = {}) = tag(" title" , block = block)
141+ inline fun HTMLBuilder.title (block : HTMLBuilderBlock = {}) = tag(" title" , block = block)
90142
91143/* *
92144 * Appends a `<main>` tag.
93145 *
94146 * @param block the builder action
95147 */
96- inline fun HTMLBuilder.main (block : HTMLBuilder .() -> Unit = {}) = tag(" main" , block = block)
148+ inline fun HTMLBuilder.main (block : HTMLBuilderBlock = {}) = tag(" main" , block = block)
97149
98150/* *
99151 * Appends a `<h1>` tag.
100152 *
101153 * @param block the builder action
102154 */
103- inline fun HTMLBuilder.h1 (block : HTMLBuilder .() -> Unit = {}) = tag(" h1" , block = block)
155+ inline fun HTMLBuilder.h1 (block : HTMLBuilderBlock = {}) = tag(" h1" , block = block)
104156
105157/* *
106158 * Appends a `<h3>` tag.
107159 *
108160 * @param block the builder action
109161 */
110- inline fun HTMLBuilder.h3 (block : HTMLBuilder .() -> Unit = {}) = tag(" h3" , block = block)
162+ inline fun HTMLBuilder.h3 (block : HTMLBuilderBlock = {}) = tag(" h3" , block = block)
111163
112164/* *
113165 * Appends a `<h4>` tag.
114166 *
115167 * @param block the builder action
116168 */
117- inline fun HTMLBuilder.h4 (block : HTMLBuilder .() -> Unit = {}) = tag(" h4" , block = block)
169+ inline fun HTMLBuilder.h4 (block : HTMLBuilderBlock = {}) = tag(" h4" , block = block)
118170
119171/* *
120172 * Appends a `<p>` tag.
@@ -124,52 +176,52 @@ inline fun HTMLBuilder.h4(block: HTMLBuilder.() -> Unit = {}) = tag("h4", block
124176 * @param style the `style` attribute
125177 * @param block the builder action
126178 */
127- inline fun HTMLBuilder.p (onClick : String? = null, classes : String? = null, style : String? = null, block : HTMLBuilder .() -> Unit = {}) =
179+ inline fun HTMLBuilder.p (onClick : String? = null, classes : String? = null, style : String? = null, block : HTMLBuilderBlock = {}) =
128180 tag(" p" , mapOf (" onclick" to onClick, " class" to classes, " style" to style), block)
129181
130182/* *
131183 * Appends a `<pre>` tag.
132184 *
133185 * @param block the builder action
134186 */
135- inline fun HTMLBuilder.pre (block : HTMLBuilder .() -> Unit = {}) = tag(" pre" , block = block)
187+ inline fun HTMLBuilder.pre (block : HTMLBuilderBlock = {}) = tag(" pre" , block = block)
136188
137189/* *
138190 * Appends a `<table>` tag.
139191 *
140192 * @param classes the `class` attribute
141193 * @param block the builder action
142194 */
143- inline fun HTMLBuilder.table (classes : String? = null, block : HTMLBuilder .() -> Unit = {}) =
195+ inline fun HTMLBuilder.table (classes : String? = null, block : HTMLBuilderBlock = {}) =
144196 tag(" table" , mapOf (" class" to classes), block)
145197
146198/* *
147199 * Appends a `<thead>` tag.
148200 *
149201 * @param block the builder action
150202 */
151- inline fun HTMLBuilder.thead (block : HTMLBuilder .() -> Unit = {}) = tag(" thead" , block = block)
203+ inline fun HTMLBuilder.thead (block : HTMLBuilderBlock = {}) = tag(" thead" , block = block)
152204
153205/* *
154206 * Appends a `<tbody>` tag.
155207 *
156208 * @param block the builder action
157209 */
158- inline fun HTMLBuilder.tbody (block : HTMLBuilder .() -> Unit = {}) = tag(" tbody" , block = block)
210+ inline fun HTMLBuilder.tbody (block : HTMLBuilderBlock = {}) = tag(" tbody" , block = block)
159211
160212/* *
161213 * Appends a `<tr>` tag.
162214 *
163215 * @param block the builder action
164216 */
165- inline fun HTMLBuilder.tr (block : HTMLBuilder .() -> Unit = {}) = tag(" tr" , block = block)
217+ inline fun HTMLBuilder.tr (block : HTMLBuilderBlock = {}) = tag(" tr" , block = block)
166218
167219/* *
168220 * Appends a `<th>` tag.
169221 *
170222 * @param block the builder action
171223 */
172- inline fun HTMLBuilder.th (block : HTMLBuilder .() -> Unit = {}) = tag(" th" , block = block)
224+ inline fun HTMLBuilder.th (block : HTMLBuilderBlock = {}) = tag(" th" , block = block)
173225
174226/* *
175227 * Appends a `<td>` tag.
@@ -178,7 +230,7 @@ inline fun HTMLBuilder.th(block: HTMLBuilder.() -> Unit = {}) = tag("th", block
178230 * @param style the `style` attribute
179231 * @param block the builder action
180232 */
181- inline fun HTMLBuilder.td (classes : String? = null, style : String? = null, block : HTMLBuilder .() -> Unit = {}) =
233+ inline fun HTMLBuilder.td (classes : String? = null, style : String? = null, block : HTMLBuilderBlock = {}) =
182234 tag(" td" , mapOf (" class" to classes, " style" to style), block)
183235
184236/* *
@@ -190,7 +242,7 @@ inline fun HTMLBuilder.td(classes: String? = null, style: String? = null, block:
190242 * @param style the `style` attribute
191243 * @param block the builder action
192244 */
193- inline fun HTMLBuilder.div (id : String? = null, onClick : String? = null, classes : String? = null, style : String? = null, block : HTMLBuilder .() -> Unit = {}) =
245+ inline fun HTMLBuilder.div (id : String? = null, onClick : String? = null, classes : String? = null, style : String? = null, block : HTMLBuilderBlock = {}) =
194246 tag(" div" , mapOf (" id" to id, " onclick" to onClick, " class" to classes, " style" to style), block)
195247
196248/* *
@@ -200,7 +252,7 @@ inline fun HTMLBuilder.div(id: String? = null, onClick: String? = null, classes:
200252 * @param style the `style` attribute
201253 * @param block the builder action
202254 */
203- inline fun HTMLBuilder.span (classes : String? = null, style : String? = null, block : HTMLBuilder .() -> Unit = {}) =
255+ inline fun HTMLBuilder.span (classes : String? = null, style : String? = null, block : HTMLBuilderBlock = {}) =
204256 tag(" span" , mapOf (" class" to classes, " style" to style), block)
205257
206258/* *
@@ -212,7 +264,7 @@ inline fun HTMLBuilder.span(classes: String? = null, style: String? = null, bloc
212264 * @param ariaLabel the `aria-label` attribute
213265 * @param block the builder action
214266 */
215- inline fun HTMLBuilder.a (id : String? = null, href : String? = null, classes : String? = null, ariaLabel : String? = null, block : HTMLBuilder .() -> Unit = {}) =
267+ inline fun HTMLBuilder.a (id : String? = null, href : String? = null, classes : String? = null, ariaLabel : String? = null, block : HTMLBuilderBlock = {}) =
216268 tag(" a" , mapOf (" id" to id, " href" to href, " class" to classes, " aria-label" to ariaLabel), block)
217269
218270/* *
@@ -224,22 +276,22 @@ inline fun HTMLBuilder.a(id: String? = null, href: String? = null, classes: Stri
224276 * @param spellCheck the `spellcheck` attribute
225277 * @param placeholder the `placeholder` attribute
226278 */
227- fun HTMLBuilder.input (id : String? = null, type : String? = null, classes : String? = null, spellCheck : Boolean? = null , placeholder : String? = null) =
279+ fun HTMLBuilder.input (id : String? = null, type : String? = null, classes : String? = null, spellCheck : Boolean = false , placeholder : String? = null) =
228280 tag(" input" , mapOf (" id" to id, " type" to type, " class" to classes, " spellcheck" to spellCheck, " placeholder" to placeholder))
229281
230282/* *
231283 * Appends a `<nav>` tag.
232284 *
233285 * @param block the builder action
234286 */
235- inline fun HTMLBuilder.nav (block : HTMLBuilder .() -> Unit = {}) = tag(" nav" , block = block)
287+ inline fun HTMLBuilder.nav (block : HTMLBuilderBlock = {}) = tag(" nav" , block = block)
236288
237289/* *
238290 * Appends a `<footer>` tag.
239291 *
240292 * @param block the builder action
241293 */
242- inline fun HTMLBuilder.footer (block : HTMLBuilder .() -> Unit = {}) = tag(" footer" , block = block)
294+ inline fun HTMLBuilder.footer (block : HTMLBuilderBlock = {}) = tag(" footer" , block = block)
243295
244296/* *
245297 * Appends a `<meta>` tag.
@@ -256,7 +308,7 @@ fun HTMLBuilder.meta(name: String? = null, content: String? = null) =
256308 * @param src the `src` attribute
257309 * @param block the builder action
258310 */
259- inline fun HTMLBuilder.script (src : String? = null, block : HTMLBuilder .() -> Unit = {}) =
311+ inline fun HTMLBuilder.script (src : String? = null, block : HTMLBuilderBlock = {}) =
260312 tag(" script" , mapOf (" src" to src), block)
261313
262314/* *
0 commit comments