package htmlx import ( "github.com/d5/tengo/v2" "strings" "html" "fmt" ) const RawTag = "raw" // The type implements basic // way to structrize HTML elements. type Element struct { tengo.ObjectImpl Tag string Attr map[string] string Children []*Element // The value makes sense only if // the tag is the "raw" Content string Final bool } func (el *Element) TypeName() string { return "*HTMLElement" } // The method renders the element to it's // HTML representation. func (el *Element) String() string { if el.Tag == RawTag { return html.EscapeString(el.Content) } var b strings.Builder fmt.Fprintf(&b, "<%s", el.Tag) for k, v := range el.Attr { fmt.Fprintf(&b, " %s=%q", k, v) } fmt.Fprint(&b, ">") if el.Final { return b.String() } for _, child := range el.Children { if child == nil { continue } fmt.Fprint(&b, child.String()) } fmt.Fprintf(&b, "", el.Tag) return b.String() } func MakeElements(args ...tengo.Object) ([]*Element, error) { s := []*Element{} for _, arg := range args { el, ok := arg.(*Element) if !ok { str, ok := tengo.ToString(arg) if ok { s = append(s, &Element{ Tag: RawTag, Content: str, }) } continue } s = append(s, el) } return s, nil } func (el *Element) SetBody( args ...tengo.Object, ) (tengo.Object, error) { els, err := MakeElements(args...) if err != nil { return nil, err } el.Children = els return el, nil } func (el *Element) Add( args ...tengo.Object, ) (tengo.Object, error) { s, err := MakeElements(args...) if err != nil { return nil, err } el.Children = append(el.Children, s...) return el, nil } func (el *Element) SetFinal( args ...tengo.Object, ) (tengo.Object, error) { if len(args) > 0 { return nil, tengo.ErrWrongNumArguments } el.Final = true return el, nil } func (el *Element) IndexGet( index tengo.Object, ) (tengo.Object, error) { arg, ok := tengo.ToString(index) if !ok { return nil, tengo.ErrInvalidIndexValueType } switch arg { case "body" : return &tengo.UserFunction{ Name: "element.body", Value: el.SetBody, }, nil case "final" : return &tengo.UserFunction{ Name: "element.final", Value: el.SetFinal, }, nil case "add" : return &tengo.UserFunction{ Name: "element.add", Value: el.Add, }, nil } return nil, nil }