1 // Compile-time empty type detection 2 assert(elem!"input" == "<input/>"); 3 assert(elem!"hr" == "<hr/>"); 4 assert(elem!"p" == "<p></p>"); 5 6 // Content 7 assert(elem!"p"("Hello, World!") == "<p>Hello, World!</p>"); 8 9 // Compile-time attributes — variant A 10 assert( 11 12 elem!("a", [ "href": "about:blank", "title": "Destroy this page" ])("Hello, World!") 13 14 == `<a href="about:blank" title="Destroy this page">Hello, World!</a>` 15 16 ); 17 18 // Compile-time attributes — variant B 19 assert( 20 21 elem!("a", q{ 22 href="about:blank" 23 title="Destroy this page" })( 24 "Hello, World!" 25 ) 26 == `<a href="about:blank" title="Destroy this page">Hello, World!</a>` 27 28 ); 29 30 // Nesting and input sanitization 31 assert( 32 33 elem!"div"( 34 elem!"p"("Hello, World!"), 35 "-> Sanitized" 36 ) 37 38 == "<div><p>Hello, World!</p>-> Sanitized</div>" 39 40 ); 41 42 // Sanitized user input in attributes 43 assert( 44 elem!"input"( 45 attr("type") = "text", 46 attr("value") = `"XSS!"` 47 ) == `<input type="text" value=""XSS!""/>` 48 ); 49 50 assert( 51 52 elem!"input"(["type": "text", "value": `"XSS!"`]) 53 == `<input type="text" value=""XSS!""/>` 54 55 ); 56 assert( 57 elem!("input", q{ type="text" })(["value": `"XSS!"`]) 58 == `<input type="text" value=""XSS!""/>` 59 ); 60 61 // Alternative method of nesting 62 assert( 63 64 elem!("div", q{ style="background:#500" }) 65 .add!"p"("Hello, World!") 66 .add("-> Sanitized") 67 .add( 68 " and", 69 " clear" 70 ) 71 72 == `<div style="background:#500"><p>Hello, World!</p>-> Sanitized and clear</div>` 73 74 ); 75 76 import std.range : repeat; 77 78 // Adding elements by ranges 79 assert( 80 elem!"ul"( 81 "element".elem!"li".repeat(3) 82 ) 83 == "<ul><li>element</li><li>element</li><li>element</li></ul>" 84 85 ); 86 87 // Significant whitespace 88 assert(elem!"span"(" Foo ") == "<span> Foo </span>"); 89 90 // Also with tilde 91 auto myElem = elem!"div"; 92 myElem ~= elem!"span"("Sample"); 93 myElem ~= " "; 94 myElem ~= elem!"span"("Text"); 95 myElem ~= attr("class") = "test"; 96 97 assert( 98 myElem == `<div class="test"><span>Sample</span> <span>Text</span></div>` 99 );
A general example page
1 import std.stdio : writeln; 2 import std.base64 : Base64; 3 import std.array : split, join; 4 import std.algorithm : map, filter; 5 6 enum page = Element.HTMLDoctype ~ elem!"html"( 7 8 elem!"head"( 9 10 elem!("title")("An example document"), 11 12 // Metadata 13 Element.MobileViewport, 14 Element.EncodingUTF8, 15 16 elem!("style")(` 17 18 html, body { 19 height: 100%; 20 font-family: sans-serif; 21 padding: 0; 22 margin: 0; 23 } 24 .header { 25 background: #f7a; 26 font-size: 1.5em; 27 margin: 0; 28 padding: 5px; 29 } 30 .article { 31 padding-left: 2em; 32 } 33 34 `.split("\n").map!"a.strip".filter!"a.length".join), 35 36 ), 37 38 elem!"body"( 39 40 elem!("header", q{ class="header" })( 41 elem!"h1"("Example website") 42 ), 43 44 elem!"h1"("Welcome to my website!"), 45 elem!"p"("Hello there,", elem!"br", "may you want to read some of my articles?"), 46 47 elem!("div", q{ class="article" })( 48 elem!"h2"("Stuff"), 49 elem!"p"("Description") 50 ) 51 52 ) 53 54 ); 55 56 enum target = cast(string) Base64.decode([ 57 `PCFET0NUWVBFIGh0bWw+PGh0bWw+PGhlYWQ+PHRpdGxlPkFuIGV4YW1wbGUgZG9jdW1lbnQ8L3Rp`, 58 `dGxlPjxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIGlu`, 59 `aXRpYWwtc2NhbGU9MSIvPjxtZXRhIGNoYXJzZXQ9InV0Zi04Ii8+PHN0eWxlPmh0bWwsIGJvZHkg`, 60 `e2hlaWdodDogMTAwJTtmb250LWZhbWlseTogc2Fucy1zZXJpZjtwYWRkaW5nOiAwO21hcmdpbjog`, 61 `MDt9LmhlYWRlciB7YmFja2dyb3VuZDogI2Y3YTtmb250LXNpemU6IDEuNWVtO21hcmdpbjogMDtw`, 62 `YWRkaW5nOiA1cHg7fS5hcnRpY2xlIHtwYWRkaW5nLWxlZnQ6IDJlbTt9PC9zdHlsZT48L2hlYWQ+`, 63 `PGJvZHk+PGhlYWRlciBjbGFzcz0iaGVhZGVyIj48aDE+RXhhbXBsZSB3ZWJzaXRlPC9oMT48L2hl`, 64 `YWRlcj48aDE+V2VsY29tZSB0byBteSB3ZWJzaXRlITwvaDE+PHA+SGVsbG8gdGhlcmUsPGJyLz5t`, 65 `YXkgeW91IHdhbnQgdG8gcmVhZCBzb21lIG9mIG15IGFydGljbGVzPzwvcD48ZGl2IGNsYXNzPSJh`, 66 `cnRpY2xlIj48aDI+U3R1ZmY8L2gyPjxwPkRlc2NyaXB0aW9uPC9wPjwvZGl2PjwvYm9keT48L2h0`, 67 `bWw+`, 68 ].join); 69 70 assert(page == target);