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, "%s>", 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
}