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