Html Generator
(1) By Olaf Ritter von Ruppert (oruppert) on 2025-02-06 09:54:32 [source]
Hello all, I have a small html generator that I would like to share with you. It consists of one global variable and one function. The function html tag_name attributes... appends the html for the given tag and attributes to the html global variable. There are 3 special attributes: - text: appends escaped text to the body of the html element. - html: appends unescaped text to the body of the html element. - body: executes body script before appending the closing tag. The html output is somewhat formatted and html entities are currently not escaped. I will clean it up and document it if someone is interested. Here and example: html html { html body { html hr html h1 text "Hello, World!" style { color: green } } } Generates: <html> <body> <hr> <h1 style="color: green">Hello, World!</h1> </body> </html> And here is the source code: set html "" proc html {tag args} { global html # Normalize Implicit Script if {[llength $args] % 2 == 1} { set args [linsert $args end-1 body] } # Generate Attributes String set attributes {} foreach {name value} $args { switch $name { {html} {} {text} {} {body} {} {default} { # Collapse Whitespace set value [regsub -all {\s+} $value { }] # Escape Quotes set value [string map {\" "} $value] # Trim Whitespace set value [string trim $value] # Skip Empty Attributes if {$value == {}} { continue } # Append Attribute set attributes [format {%s %s="%s"} $attributes $name $value] } } } # Append Opening Tag switch $tag { {} {} {default} { # Maybe Append Newline switch [string index $html end] { {} {} \n {} default { append html \n } } # Append the actual Tag append html <$tag$attributes> } } # Process Special Attributes foreach {name value} $args { switch $name { {html} { append html $value } {text} { append html [string map {< <} $value] } {body} { uplevel 1 $value } } } # Append Closing Tag switch $tag { {} {} {area} {} {base} {} {br} {} {col} {} {embed} {} {hr} {} {img} {} {input} {} {link} {} {meta} {} {param} {} {source} {} {track} {} {wbr} {} {default} { append html </$tag>\n } } }
(2) By ufko.org on 2025-02-06 14:25:38 in reply to 1 [link] [source]
Hey Olaf.
Very nice! I really like how you've created this simple DSL for generating HTML. It's a great way to speed up the process and simplify writing HTML code. I'll definitely give it a try. Thanks for sharing!
(3) By Olaf Ritter von Ruppert (oruppert) on 2025-02-25 19:31:50 in reply to 2 [link] [source]
Thank you for your kind words! I have written a few prototypes and it seems to work fine. I like that it allows me to use inline styles and decompose my application into small components, e.g. proc render-panel {name body} { html div style { display: flex; flex-direction: column; gap: 2rem; padding: 2rem; border-radius: 1rem; background-color: #fafaf9; } { html strong text $name uplevel 1 $body } } Here a little trick is used: I do not like to write 0.25rem or 0.5rem all the time, so I set the font-size of html to 25% and and the font-size of body to 400%. so 1rem is actually 0.25em. If you want to try it, here is a Version that writes to the wapp .reply string: proc html {tag args} { global wapp # Normalize Implicit Script if {[llength $args] % 2 == 1} { set args [linsert $args end-1 body] } # Generate Attributes String set attributes {} foreach {name value} $args { switch $name { {html} {} {text} {} {body} {} {default} { # Collapse Whitespace set value [regsub -all {\s+} $value { }] # Escape Quotes set value [string map {\" "} $value] # Trim Whitespace set value [string trim $value] # Skip Empty Attributes if {$value == {}} { continue } # Append Attribute set attributes [format {%s %s="%s"} $attributes $name $value] } } } # Append Opening Tag switch $tag { {} {} {default} { # Maybe Append Newline switch [string index [dict get $wapp .reply] end] { {} {} \n {} default { dict append wapp .reply \n } } # Append the actual Tag dict append wapp .reply <$tag$attributes> } } # Process Special Attributes foreach {name value} $args { switch $name { {html} { dict append wapp .reply $value } {text} { dict append wapp .reply [string map {< <} $value] } {body} { uplevel 1 $value } } } # Append Closing Tag switch $tag { {} {} {area} {} {base} {} {br} {} {col} {} {embed} {} {hr} {} {img} {} {input} {} {link} {} {meta} {} {param} {} {source} {} {track} {} {wbr} {} {default} { dict append wapp .reply </$tag>\n } } }
(4.3) By ufko.org on 2025-02-26 05:23:43 edited from 4.2 in reply to 3 [link] [source]
Olaf,
I hope you don’t mind, but I renamed your procedure to proc olaf in my code—I just had to! 😄 Your approach is really elegant, and I appreciate how simple yet powerful it is.
By the way, have you considered publishing your source code on Tcl wiki? I think it deserves more attention, and others in the Tcl community might find it useful or even contribute improvements.
You can create new page here: https://wiki.tcl-lang.org/_new
Thanks again for sharing your work!
To make proc olaf more compact, without calling set olaf ""
outside of the procedure:
Note:
- Now
olaf
is something like static variable in other languages. - I was thinking, it might be a good idea to rename the global variable (
single purpose in this context
) to something a bit more unique, like ___olaf (with multiple underscores) or something similarly unconventional, to avoid accidental clash.
proc olaf {tag args} {
global olaf
# init only if variable olaf doesn't exist
if {![info exists olaf]} { set olaf "" }
# Normalize Implicit Script
if {[llength $args] % 2 == 1} {
set args [linsert $args end-1 body]
}
# Generate Attributes String
set attributes {}
foreach {name value} $args {
switch $name {
{olaf} {}
{text} {}
{body} {}
{default} {
# Collapse Whitespace
set value [regsub -all {\s+} $value { }]
# Escape Quotes
set value [string map {\" "} $value]
# Trim Whitespace
set value [string trim $value]
# Skip Empty Attributes
if {$value == {}} {
continue
}
# Append Attribute
set attributes [format {%s %s="%s"} $attributes $name $value]
}
}
}
# Append Opening Tag
switch $tag {
{} {}
{default} {
# Maybe Append Newline
switch [string index $olaf end] {
{} {}
\n {}
default {
append olaf \n
}
}
# Append the actual Tag
append olaf <$tag$attributes>
}
}
# Process Special Attributes
foreach {name value} $args {
switch $name {
{olaf} { append olaf $value }
{text} { append olaf [string map {< <} $value] }
{body} { uplevel 1 $value }
}
}
# Append Closing Tag
switch $tag {
{} {}
{area} {}
{base} {}
{br} {}
{col} {}
{embed} {}
{hr} {}
{img} {}
{input} {}
{link} {}
{meta} {}
{param} {}
{source} {}
{track} {}
{wbr} {}
{default} {
append olaf </$tag>\n
}
}
}
(5) By Olaf Ritter von Ruppert (oruppert) on 2025-03-07 16:55:04 in reply to 4.3 [link] [source]
Thank you for your kind words. I apologize for my late reply, I did not forget you. It's just that my queue is full. You can call the proc olaf if you want to. But I prefer the name html. I created a repository on github: https://github.com/oruppert/html.tcl I added some of your changes there and will add your name in the comments if that is fine with you. Thank you for your link to the tcl wiki. I will write a page in the next week or so. Would you want me to include some tricks I learned writing dumb old webpages? Sometimes I have the feeling people have forgotten how to write html based web applications, like this forum: Simple, fast, useful. Take care, Olaf
(6) By ufko.org on 2025-03-08 11:02:21 in reply to 5 [link] [source]
I'm fine with mentions. Already follow your project on GitHub :) Vanilla HTML's my thing.