diff options
author | Markus Wüstenberg | 2020-12-10 13:00:23 +0100 |
---|---|---|
committer | GitHub | 2020-12-10 13:00:23 +0100 |
commit | a76262652b227c95ce140f3698c46f59b79354ac (patch) | |
tree | 15987ad4a4a4f830f0df9fc110faf667fe83514a /html | |
parent | a7e24c6cddaafb98091c3989c9da7779eeba30b5 (diff) | |
download | gomponents-a76262652b227c95ce140f3698c46f59b79354ac.tar.lz gomponents-a76262652b227c95ce140f3698c46f59b79354ac.tar.zst gomponents-a76262652b227c95ce140f3698c46f59b79354ac.zip |
Move elements and attributes into html package (#52)
This makes it easier to use dot-imports. Also updated the readme and examples with new usage, and move the `Classes` helper into the `components` package.
Diffstat (limited to 'html')
-rw-r--r-- | html/attributes.go | 161 | ||||
-rw-r--r-- | html/attributes_test.go | 73 | ||||
-rw-r--r-- | html/elements.go | 447 | ||||
-rw-r--r-- | html/elements_test.go | 212 |
4 files changed, 893 insertions, 0 deletions
diff --git a/html/attributes.go b/html/attributes.go new file mode 100644 index 0000000..3db8584 --- /dev/null +++ b/html/attributes.go @@ -0,0 +1,161 @@ +package html + +import ( + g "github.com/maragudk/gomponents" +) + +func Async() g.Node { + return g.Attr("async") +} + +func AutoFocus() g.Node { + return g.Attr("autofocus") +} + +func AutoPlay() g.Node { + return g.Attr("autoplay") +} + +func Controls() g.Node { + return g.Attr("controls") +} + +func Defer() g.Node { + return g.Attr("defer") +} + +func Disabled() g.Node { + return g.Attr("disabled") +} + +func Multiple() g.Node { + return g.Attr("multiple") +} + +func ReadOnly() g.Node { + return g.Attr("readonly") +} + +func Required() g.Node { + return g.Attr("required") +} + +func Selected() g.Node { + return g.Attr("selected") +} + +func Accept(v string) g.Node { + return g.Attr("accept", v) +} + +func AutoComplete(v string) g.Node { + return g.Attr("autocomplete", v) +} + +func Charset(v string) g.Node { + return g.Attr("charset", v) +} + +func Class(v string) g.Node { + return g.Attr("class", v) +} + +func Cols(v string) g.Node { + return g.Attr("cols", v) +} + +func Content(v string) g.Node { + return g.Attr("content", v) +} + +func FormAttr(v string) g.Node { + return g.Attr("form", v) +} + +func Height(v string) g.Node { + return g.Attr("height", v) +} + +func Href(v string) g.Node { + return g.Attr("href", v) +} + +func ID(v string) g.Node { + return g.Attr("id", v) +} + +func Lang(v string) g.Node { + return g.Attr("lang", v) +} + +func Max(v string) g.Node { + return g.Attr("max", v) +} + +func MaxLength(v string) g.Node { + return g.Attr("maxlength", v) +} + +func Min(v string) g.Node { + return g.Attr("min", v) +} + +func MinLength(v string) g.Node { + return g.Attr("minlength", v) +} + +func Name(v string) g.Node { + return g.Attr("name", v) +} + +func Pattern(v string) g.Node { + return g.Attr("pattern", v) +} + +func Preload(v string) g.Node { + return g.Attr("preload", v) +} + +func Placeholder(v string) g.Node { + return g.Attr("placeholder", v) +} + +func Rel(v string) g.Node { + return g.Attr("rel", v) +} + +func Rows(v string) g.Node { + return g.Attr("rows", v) +} + +func Src(v string) g.Node { + return g.Attr("src", v) +} + +func StyleAttr(v string) g.Node { + return g.Attr("style", v) +} + +func TabIndex(v string) g.Node { + return g.Attr("tabindex", v) +} + +func Target(v string) g.Node { + return g.Attr("target", v) +} + +func TitleAttr(v string) g.Node { + return g.Attr("title", v) +} + +func Type(v string) g.Node { + return g.Attr("type", v) +} + +func Value(v string) g.Node { + return g.Attr("value", v) +} + +func Width(v string) g.Node { + return g.Attr("width", v) +} diff --git a/html/attributes_test.go b/html/attributes_test.go new file mode 100644 index 0000000..7bf45bb --- /dev/null +++ b/html/attributes_test.go @@ -0,0 +1,73 @@ +package html_test + +import ( + "fmt" + "testing" + + g "github.com/maragudk/gomponents" + "github.com/maragudk/gomponents/assert" + . "github.com/maragudk/gomponents/html" +) + +func TestBooleanAttributes(t *testing.T) { + cases := map[string]func() g.Node{ + "async": Async, + "autofocus": AutoFocus, + "autoplay": AutoPlay, + "controls": Controls, + "defer": Defer, + "disabled": Disabled, + "multiple": Multiple, + "readonly": ReadOnly, + "required": Required, + "selected": Selected, + } + + for name, fn := range cases { + t.Run(fmt.Sprintf("should output %v", name), func(t *testing.T) { + n := g.El("div", fn()) + assert.Equal(t, fmt.Sprintf(`<div %v></div>`, name), n) + }) + } +} + +func TestSimpleAttributes(t *testing.T) { + cases := map[string]func(string) g.Node{ + "accept": Accept, + "autocomplete": AutoComplete, + "charset": Charset, + "class": Class, + "cols": Cols, + "content": Content, + "form": FormAttr, + "height": Height, + "href": Href, + "id": ID, + "lang": Lang, + "max": Max, + "maxlength": MaxLength, + "min": Min, + "minlength": MinLength, + "name": Name, + "pattern": Pattern, + "preload": Preload, + "placeholder": Placeholder, + "rel": Rel, + "rows": Rows, + "src": Src, + "style": StyleAttr, + "tabindex": TabIndex, + "target": Target, + "title": TitleAttr, + "type": Type, + "value": Value, + "width": Width, + } + + for name, fn := range cases { + t.Run(fmt.Sprintf(`should output %v="hat"`, name), func(t *testing.T) { + n := g.El("div", fn("hat")) + assert.Equal(t, fmt.Sprintf(`<div %v="hat"></div>`, name), n) + }) + } +} diff --git a/html/elements.go b/html/elements.go new file mode 100644 index 0000000..8045561 --- /dev/null +++ b/html/elements.go @@ -0,0 +1,447 @@ +// Package html provides common HTML elements and attributes. +// See https://developer.mozilla.org/en-US/docs/Web/HTML/Element for a list of elements. +// See https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes for a list of attributes. +package html + +import ( + "fmt" + "io" + + g "github.com/maragudk/gomponents" +) + +func A(href string, children ...g.Node) g.NodeFunc { + return g.El("a", g.Attr("href", href), g.Group(children)) +} + +// Document returns an special kind of Node that prefixes its child with the string "<!doctype html>". +func Document(child g.Node) g.NodeFunc { + return func(w io.Writer) error { + if _, err := w.Write([]byte("<!doctype html>")); err != nil { + return err + } + return child.Render(w) + } +} + +// FormEl returns an element with name "form", the given action and method attributes, and the given children. +func FormEl(action, method string, children ...g.Node) g.NodeFunc { + return g.El("form", g.Attr("action", action), g.Attr("method", method), g.Group(children)) +} + +func Img(src, alt string, children ...g.Node) g.NodeFunc { + return g.El("img", g.Attr("src", src), g.Attr("alt", alt), g.Group(children)) +} + +// Input returns an element with name "input", the given type and name attributes, and the given children. +// Note that "type" is a keyword in Go, so the parameter is called typ. +func Input(typ, name string, children ...g.Node) g.NodeFunc { + return g.El("input", g.Attr("type", typ), g.Attr("name", name), g.Group(children)) +} + +// Label returns an element with name "label", the given for attribute, and the given children. +// Note that "for" is a keyword in Go, so the parameter is called forr. +func Label(forr string, children ...g.Node) g.NodeFunc { + return g.El("label", g.Attr("for", forr), g.Group(children)) +} + +// Option returns an element with name "option", the given text content and value attribute, and the given children. +func Option(text, value string, children ...g.Node) g.NodeFunc { + return g.El("option", g.Attr("value", value), g.Text(text), g.Group(children)) +} + +// Progress returns an element with name "progress", the given value and max attributes, and the given children. +func Progress(value, max float64, children ...g.Node) g.NodeFunc { + return g.El("progress", + g.Attr("value", fmt.Sprintf("%v", value)), + g.Attr("max", fmt.Sprintf("%v", max)), + g.Group(children)) +} + +// Select returns an element with name "select", the given name attribute, and the given children. +func Select(name string, children ...g.Node) g.NodeFunc { + return g.El("select", g.Attr("name", name), g.Group(children)) +} + +// Textarea returns an element with name "textarea", the given name attribute, and the given children. +func Textarea(name string, children ...g.Node) g.NodeFunc { + return g.El("textarea", g.Attr("name", name), g.Group(children)) +} + +func Address(children ...g.Node) g.NodeFunc { + return g.El("address", children...) +} + +func Area(children ...g.Node) g.NodeFunc { + return g.El("area", children...) +} + +func Article(children ...g.Node) g.NodeFunc { + return g.El("article", children...) +} + +func Aside(children ...g.Node) g.NodeFunc { + return g.El("aside", children...) +} + +func Audio(children ...g.Node) g.NodeFunc { + return g.El("audio", children...) +} + +func Base(children ...g.Node) g.NodeFunc { + return g.El("base", children...) +} + +func BlockQuote(children ...g.Node) g.NodeFunc { + return g.El("blockquote", children...) +} + +func Body(children ...g.Node) g.NodeFunc { + return g.El("body", children...) +} + +func Br(children ...g.Node) g.NodeFunc { + return g.El("br", children...) +} + +func Button(children ...g.Node) g.NodeFunc { + return g.El("button", children...) +} + +func Canvas(children ...g.Node) g.NodeFunc { + return g.El("canvas", children...) +} + +func Cite(children ...g.Node) g.NodeFunc { + return g.El("cite", children...) +} + +func Code(children ...g.Node) g.NodeFunc { + return g.El("code", children...) +} + +func Col(children ...g.Node) g.NodeFunc { + return g.El("col", children...) +} + +func ColGroup(children ...g.Node) g.NodeFunc { + return g.El("colgroup", children...) +} + +func Data(children ...g.Node) g.NodeFunc { + return g.El("data", children...) +} + +func DataList(children ...g.Node) g.NodeFunc { + return g.El("datalist", children...) +} + +func Details(children ...g.Node) g.NodeFunc { + return g.El("details", children...) +} + +func Dialog(children ...g.Node) g.NodeFunc { + return g.El("dialog", children...) +} + +func Div(children ...g.Node) g.NodeFunc { + return g.El("div", children...) +} + +func Dl(children ...g.Node) g.NodeFunc { + return g.El("dl", children...) +} + +func Embed(children ...g.Node) g.NodeFunc { + return g.El("embed", children...) +} + +func FieldSet(children ...g.Node) g.NodeFunc { + return g.El("fieldset", children...) +} + +func Figure(children ...g.Node) g.NodeFunc { + return g.El("figure", children...) +} + +func Footer(children ...g.Node) g.NodeFunc { + return g.El("footer", children...) +} + +func Head(children ...g.Node) g.NodeFunc { + return g.El("head", children...) +} + +func Header(children ...g.Node) g.NodeFunc { + return g.El("header", children...) +} + +func HGroup(children ...g.Node) g.NodeFunc { + return g.El("hgroup", children...) +} + +func Hr(children ...g.Node) g.NodeFunc { + return g.El("hr", children...) +} + +func HTML(children ...g.Node) g.NodeFunc { + return g.El("html", children...) +} + +func IFrame(children ...g.Node) g.NodeFunc { + return g.El("iframe", children...) +} + +func Legend(children ...g.Node) g.NodeFunc { + return g.El("legend", children...) +} + +func Li(children ...g.Node) g.NodeFunc { + return g.El("li", children...) +} + +func Link(children ...g.Node) g.NodeFunc { + return g.El("link", children...) +} + +func Main(children ...g.Node) g.NodeFunc { + return g.El("main", children...) +} + +func Menu(children ...g.Node) g.NodeFunc { + return g.El("menu", children...) +} + +func Meta(children ...g.Node) g.NodeFunc { + return g.El("meta", children...) +} + +func Meter(children ...g.Node) g.NodeFunc { + return g.El("meter", children...) +} + +func Nav(children ...g.Node) g.NodeFunc { + return g.El("nav", children...) +} + +func NoScript(children ...g.Node) g.NodeFunc { + return g.El("noscript", children...) +} + +func Object(children ...g.Node) g.NodeFunc { + return g.El("object", children...) +} + +func Ol(children ...g.Node) g.NodeFunc { + return g.El("ol", children...) +} + +func OptGroup(children ...g.Node) g.NodeFunc { + return g.El("optgroup", children...) +} + +func P(children ...g.Node) g.NodeFunc { + return g.El("p", children...) +} + +func Param(children ...g.Node) g.NodeFunc { + return g.El("param", children...) +} + +func Picture(children ...g.Node) g.NodeFunc { + return g.El("picture", children...) +} + +func Pre(children ...g.Node) g.NodeFunc { + return g.El("pre", children...) +} + +func Script(children ...g.Node) g.NodeFunc { + return g.El("script", children...) +} + +func Section(children ...g.Node) g.NodeFunc { + return g.El("section", children...) +} + +func Source(children ...g.Node) g.NodeFunc { + return g.El("source", children...) +} + +func Span(children ...g.Node) g.NodeFunc { + return g.El("span", children...) +} + +func StyleEl(children ...g.Node) g.NodeFunc { + return g.El("style", children...) +} + +func Summary(children ...g.Node) g.NodeFunc { + return g.El("summary", children...) +} + +func SVG(children ...g.Node) g.NodeFunc { + return g.El("svg", children...) +} + +func Table(children ...g.Node) g.NodeFunc { + return g.El("table", children...) +} + +func TBody(children ...g.Node) g.NodeFunc { + return g.El("tbody", children...) +} + +func Td(children ...g.Node) g.NodeFunc { + return g.El("td", children...) +} + +func TFoot(children ...g.Node) g.NodeFunc { + return g.El("tfoot", children...) +} + +func Th(children ...g.Node) g.NodeFunc { + return g.El("th", children...) +} + +func THead(children ...g.Node) g.NodeFunc { + return g.El("thead", children...) +} + +func Tr(children ...g.Node) g.NodeFunc { + return g.El("tr", children...) +} + +func Ul(children ...g.Node) g.NodeFunc { + return g.El("ul", children...) +} + +func Wbr(children ...g.Node) g.NodeFunc { + return g.El("wbr", children...) +} + +func Abbr(text string, children ...g.Node) g.NodeFunc { + return g.El("abbr", g.Text(text), g.Group(children)) +} + +func B(text string, children ...g.Node) g.NodeFunc { + return g.El("b", g.Text(text), g.Group(children)) +} + +func Caption(text string, children ...g.Node) g.NodeFunc { + return g.El("caption", g.Text(text), g.Group(children)) +} + +func Dd(text string, children ...g.Node) g.NodeFunc { + return g.El("dd", g.Text(text), g.Group(children)) +} + +func Del(text string, children ...g.Node) g.NodeFunc { + return g.El("del", g.Text(text), g.Group(children)) +} + +func Dfn(text string, children ...g.Node) g.NodeFunc { + return g.El("dfn", g.Text(text), g.Group(children)) +} + +func Dt(text string, children ...g.Node) g.NodeFunc { + return g.El("dt", g.Text(text), g.Group(children)) +} + +func Em(text string, children ...g.Node) g.NodeFunc { + return g.El("em", g.Text(text), g.Group(children)) +} + +func FigCaption(text string, children ...g.Node) g.NodeFunc { + return g.El("figcaption", g.Text(text), g.Group(children)) +} + +// H1 returns an element with name "h1", the given text content, and the given children. +func H1(text string, children ...g.Node) g.NodeFunc { + return g.El("h1", g.Text(text), g.Group(children)) +} + +// H2 returns an element with name "h2", the given text content, and the given children. +func H2(text string, children ...g.Node) g.NodeFunc { + return g.El("h2", g.Text(text), g.Group(children)) +} + +// H3 returns an element with name "h3", the given text content, and the given children. +func H3(text string, children ...g.Node) g.NodeFunc { + return g.El("h3", g.Text(text), g.Group(children)) +} + +// H4 returns an element with name "h4", the given text content, and the given children. +func H4(text string, children ...g.Node) g.NodeFunc { + return g.El("h4", g.Text(text), g.Group(children)) +} + +// H5 returns an element with name "h5", the given text content, and the given children. +func H5(text string, children ...g.Node) g.NodeFunc { + return g.El("h5", g.Text(text), g.Group(children)) +} + +// H6 returns an element with name "h6", the given text content, and the given children. +func H6(text string, children ...g.Node) g.NodeFunc { + return g.El("h6", g.Text(text), g.Group(children)) +} + +func I(text string, children ...g.Node) g.NodeFunc { + return g.El("i", g.Text(text), g.Group(children)) +} + +func Ins(text string, children ...g.Node) g.NodeFunc { + return g.El("ins", g.Text(text), g.Group(children)) +} + +func Kbd(text string, children ...g.Node) g.NodeFunc { + return g.El("kbd", g.Text(text), g.Group(children)) +} + +func Mark(text string, children ...g.Node) g.NodeFunc { + return g.El("mark", g.Text(text), g.Group(children)) +} + +func Q(text string, children ...g.Node) g.NodeFunc { + return g.El("q", g.Text(text), g.Group(children)) +} + +func S(text string, children ...g.Node) g.NodeFunc { + return g.El("s", g.Text(text), g.Group(children)) +} + +func Samp(text string, children ...g.Node) g.NodeFunc { + return g.El("samp", g.Text(text), g.Group(children)) +} + +func Small(text string, children ...g.Node) g.NodeFunc { + return g.El("small", g.Text(text), g.Group(children)) +} + +func Strong(text string, children ...g.Node) g.NodeFunc { + return g.El("strong", g.Text(text), g.Group(children)) +} + +func Sub(text string, children ...g.Node) g.NodeFunc { + return g.El("sub", g.Text(text), g.Group(children)) +} + +func Sup(text string, children ...g.Node) g.NodeFunc { + return g.El("sup", g.Text(text), g.Group(children)) +} + +func Time(text string, children ...g.Node) g.NodeFunc { + return g.El("time", g.Text(text), g.Group(children)) +} + +func TitleEl(title string, children ...g.Node) g.NodeFunc { + return g.El("title", g.Text(title), g.Group(children)) +} + +func U(text string, children ...g.Node) g.NodeFunc { + return g.El("u", g.Text(text), g.Group(children)) +} + +func Var(text string, children ...g.Node) g.NodeFunc { + return g.El("var", g.Text(text), g.Group(children)) +} diff --git a/html/elements_test.go b/html/elements_test.go new file mode 100644 index 0000000..e550bfe --- /dev/null +++ b/html/elements_test.go @@ -0,0 +1,212 @@ +package html_test + +import ( + "errors" + "fmt" + "testing" + + g "github.com/maragudk/gomponents" + "github.com/maragudk/gomponents/assert" + . "github.com/maragudk/gomponents/html" +) + +type erroringWriter struct{} + +func (w *erroringWriter) Write(p []byte) (n int, err error) { + return 0, errors.New("don't want to write") +} + +func TestDocument(t *testing.T) { + t.Run("returns doctype and children", func(t *testing.T) { + assert.Equal(t, `<!doctype html><html></html>`, Document(g.El("html"))) + }) + + t.Run("errors on write error in Render", func(t *testing.T) { + err := Document(g.El("html")).Render(&erroringWriter{}) + assert.Error(t, err) + }) +} + +func TestFormEl(t *testing.T) { + t.Run("returns a form element with action and method attributes", func(t *testing.T) { + assert.Equal(t, `<form action="/" method="post"></form>`, FormEl("/", "post")) + }) +} + +func TestInput(t *testing.T) { + t.Run("returns an input element with attributes type and name", func(t *testing.T) { + assert.Equal(t, `<input type="text" name="hat">`, Input("text", "hat")) + }) +} + +func TestLabel(t *testing.T) { + t.Run("returns a label element with attribute for", func(t *testing.T) { + assert.Equal(t, `<label for="hat">Hat</label>`, Label("hat", g.Text("Hat"))) + }) +} + +func TestOption(t *testing.T) { + t.Run("returns an option element with attribute label and content", func(t *testing.T) { + assert.Equal(t, `<option value="hat">Hat</option>`, Option("Hat", "hat")) + }) +} + +func TestProgress(t *testing.T) { + t.Run("returns a progress element with attributes value and max", func(t *testing.T) { + assert.Equal(t, `<progress value="5.5" max="10"></progress>`, Progress(5.5, 10)) + }) +} + +func TestSelect(t *testing.T) { + t.Run("returns a select element with attribute name", func(t *testing.T) { + assert.Equal(t, `<select name="hat"><option value="partyhat">Partyhat</option></select>`, + Select("hat", Option("Partyhat", "partyhat"))) + }) +} + +func TestTextarea(t *testing.T) { + t.Run("returns a textarea element with attribute name", func(t *testing.T) { + assert.Equal(t, `<textarea name="hat"></textarea>`, Textarea("hat")) + }) +} + +func TestA(t *testing.T) { + t.Run("returns an a element with a href attribute", func(t *testing.T) { + assert.Equal(t, `<a href="#">hat</a>`, A("#", g.Text("hat"))) + }) +} + +func TestImg(t *testing.T) { + t.Run("returns an img element with href and alt attributes", func(t *testing.T) { + assert.Equal(t, `<img src="hat.png" alt="hat" id="image">`, Img("hat.png", "hat", g.Attr("id", "image"))) + }) +} + +func TestSimpleElements(t *testing.T) { + cases := map[string]func(...g.Node) g.NodeFunc{ + "address": Address, + "article": Article, + "aside": Aside, + "audio": Audio, + "blockquote": BlockQuote, + "body": Body, + "button": Button, + "canvas": Canvas, + "cite": Cite, + "code": Code, + "colgroup": ColGroup, + "data": Data, + "datalist": DataList, + "details": Details, + "dialog": Dialog, + "div": Div, + "dl": Dl, + "fieldset": FieldSet, + "figure": Figure, + "footer": Footer, + "head": Head, + "header": Header, + "hgroup": HGroup, + "html": HTML, + "iframe": IFrame, + "legend": Legend, + "li": Li, + "main": Main, + "menu": Menu, + "meter": Meter, + "nav": Nav, + "noscript": NoScript, + "object": Object, + "ol": Ol, + "optgroup": OptGroup, + "p": P, + "picture": Picture, + "pre": Pre, + "script": Script, + "section": Section, + "span": Span, + "style": StyleEl, + "summary": Summary, + "svg": SVG, + "table": Table, + "tbody": TBody, + "td": Td, + "tfoot": TFoot, + "th": Th, + "thead": THead, + "tr": Tr, + "ul": Ul, + } + + for name, fn := range cases { + t.Run(fmt.Sprintf("should output %v", name), func(t *testing.T) { + n := fn(g.Attr("id", "hat")) + assert.Equal(t, fmt.Sprintf(`<%v id="hat"></%v>`, name, name), n) + }) + } +} + +func TestSimpleVoidKindElements(t *testing.T) { + cases := map[string]func(...g.Node) g.NodeFunc{ + "area": Area, + "base": Base, + "br": Br, + "col": Col, + "embed": Embed, + "hr": Hr, + "link": Link, + "meta": Meta, + "param": Param, + "source": Source, + "wbr": Wbr, + } + + for name, fn := range cases { + t.Run(fmt.Sprintf("should output %v", name), func(t *testing.T) { + n := fn(g.Attr("id", "hat")) + assert.Equal(t, fmt.Sprintf(`<%v id="hat">`, name), n) + }) + } +} + +func TestTextElements(t *testing.T) { + cases := map[string]func(string, ...g.Node) g.NodeFunc{ + "abbr": Abbr, + "b": B, + "caption": Caption, + "dd": Dd, + "del": Del, + "dfn": Dfn, + "dt": Dt, + "em": Em, + "figcaption": FigCaption, + "h1": H1, + "h2": H2, + "h3": H3, + "h4": H4, + "h5": H5, + "h6": H6, + "i": I, + "ins": Ins, + "kbd": Kbd, + "mark": Mark, + "q": Q, + "s": S, + "samp": Samp, + "small": Small, + "strong": Strong, + "sub": Sub, + "sup": Sup, + "time": Time, + "title": TitleEl, + "u": U, + "var": Var, + } + + for name, fn := range cases { + t.Run(fmt.Sprintf("should output %v", name), func(t *testing.T) { + n := fn("hat", g.Attr("id", "hat")) + assert.Equal(t, fmt.Sprintf(`<%v id="hat">hat</%v>`, name, name), n) + }) + } +} |