diff --git a/htmlx/element.go b/htmlx/element.go new file mode 100644 index 0000000..8f437ae --- /dev/null +++ b/htmlx/element.go @@ -0,0 +1,97 @@ +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 +} + +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, ">") + + for _, child := range el.Children { + if child == nil { + continue + } + fmt.Fprint(&b, child.String()) + } + + fmt.Fprintf(&b, "", el.Tag) + return b.String() +} + +func (el *Element) Body(els ...*Element) *Element { + el.Children = els + return el +} + +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: func( + args ...tengo.Object, + ) (tengo.Object, 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 el.Body(s...), nil + }, + }, nil + } + return nil, nil +} + + + diff --git a/htmlx/html.go b/htmlx/html.go new file mode 100644 index 0000000..00219a3 --- /dev/null +++ b/htmlx/html.go @@ -0,0 +1,86 @@ +package htmlx + +import "github.com/d5/tengo/v2" + +type HTML struct{ + tengo.ObjectImpl +} + +/* + +html.div({ + id: "some-el-id", + value: "shit value" +}).body( + html.raw("cock "), + html.strong("something") +) + +*/ + +func (html *HTML) IndexGet( + index tengo.Object, +) (tengo.Object, error) { + str, ok := tengo.ToString(index) + if !ok { + return nil, tengo.ErrInvalidIndexValueType + } + + fn := func(args ...tengo.Object) (tengo.Object, error) { + if len(args) > 1 { + return nil, tengo.ErrWrongNumArguments + } + var arg tengo.Object + if len(args) == 1 { + arg = args[0] + } + + if arg == nil { + return &Element{ + Tag: str, + }, nil + } + + if can := arg.CanIterate() ; !can { + return nil, tengo.ErrInvalidArgumentType{ + Name: "first", + Expected: "iterable", + Found: arg.TypeName(), + } + } + attr := map[string] string{} + iter := arg.Iterate() + for iter.Next() { + key, val := iter.Key(), iter.Value() + skey, ok := tengo.ToString(key) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "attribute(key)", + Expected: "stringer", + Found: key.TypeName(), + } + } + sval, ok := tengo.ToString(val) + if !ok { + return nil, tengo.ErrInvalidArgumentType{ + Name: "attribute(value)", + Expected: "stringer", + Found: val.TypeName(), + } + } + attr[skey] = sval + } + return &Element{ + Tag: str, + Attr: attr, + }, nil + } + + return &tengo.UserFunction{ + Name: str, + Value: fn, + }, nil +} + + + diff --git a/httpx/handler.go b/httpx/handler.go index c0b0161..9358f3e 100644 --- a/httpx/handler.go +++ b/httpx/handler.go @@ -3,6 +3,7 @@ package httpx import ( //"github.com/d5/tengo/v2" "surdeus.su/util/tpp/mdx" + "surdeus.su/util/tpp/htmlx" "surdeus.su/util/tpp" "path/filepath" "net/http" @@ -36,6 +37,7 @@ type Handler struct { // between requests. global any md *mdx.Markdown + html *htmlx.HTML } // Returns the new Handler with @@ -68,6 +70,7 @@ func DefaultPP(mod string) *tpp.Preprocessor { s.Add("__http_request__", ctx.Value(KeyRequest)) s.Add("__global__", ctx.Value(KeyGlobal)) s.Add("__markdown__", ctx.Value(KeyMarkdown)) + s.Add("__html__", ctx.Value(KeyHTML)) }).SetPreCode(func(ctx context.Context) []byte { return []byte(` markdown := func(...args) { @@ -76,9 +79,11 @@ func DefaultPP(mod string) *tpp.Preprocessor { http := immutable({ request : __http_request__ }) + html := __html__ context.http = http context.pp = pp context.global = __global__ + context.html = html import("./pre")(context) `) }).SetPostCode(func(ctx context.Context) []byte { @@ -94,6 +99,10 @@ func (h *Handler) SetMD(md *mdx.Markdown) *Handler { h.md = md return h } +func (h *Handler) SetHTML(html *htmlx.HTML) *Handler { + h.html = html + return h +} func (h *Handler) ServeHTTP( w http.ResponseWriter, @@ -149,6 +158,12 @@ func (h *Handler) ServeHTTP( h.md, ) + ctx = context.WithValue( + ctx, + KeyHTML, + h.html, + ) + // Setting before the code to let it change own // content type. contentType := mime.TypeByExtension(urlExt) diff --git a/httpx/tool.go b/httpx/tool.go index 5600f1b..08a60f9 100644 --- a/httpx/tool.go +++ b/httpx/tool.go @@ -18,6 +18,7 @@ const ( KeyRequest CKey = "http-request" KeyGlobal = "global" KeyMarkdown = "markdown" + KeyHTML = "html" ) // Simple PHP-like server implementation. diff --git a/mdx/main.go b/mdx/main.go index d5241da..a5bcd63 100644 --- a/mdx/main.go +++ b/mdx/main.go @@ -46,12 +46,15 @@ func (md *Markdown) Call( ) (tengo.Object, error) { var b bytes.Buffer for _, arg := range args { - bts, ok := tengo.ToByteSlice(arg) + str, ok := tengo.ToString(arg) if !ok { return nil, tengo.ErrInvalidArgumentType{ + Name: "v", + Expected: "stringer", + Found: arg.TypeName(), } } - b.Write(bts) + b.Write([]byte(str)) } rendered, err := md.Render(b.Bytes()) diff --git a/src/main.htm.tpp b/src/main.htm.tpp index 70fee3c..437e465 100644 --- a/src/main.htm.tpp +++ b/src/main.htm.tpp @@ -52,6 +52,26 @@ } }} +{{ + vals := ["die", "with", "them", "as", "you", 1, "could", "do"] + pp.print(html.div({ + id: "the-uniq-shit" + }).body( + html.ul( + ).body( + func(){ + ret := [] + for i:=0 ; i
diff --git a/src/page.htm.tpp b/src/page.htm.tpp index 1be8346..9bedc46 100644 --- a/src/page.htm.tpp +++ b/src/page.htm.tpp @@ -31,4 +31,5 @@ And even more text Some shit +#### `, 135, ` `)}}