diff --git a/sui/api/request.go b/sui/api/request.go
index 767bbb13..2a402a89 100644
--- a/sui/api/request.go
+++ b/sui/api/request.go
@@ -123,7 +123,12 @@ func (r *Request) Render() (string, int, error) {
data["$global"] = global
}
- parser := core.NewTemplateParser(data, nil)
+ printData := false
+ if r.Query != nil && r.Query.Has("__sui_print_data") {
+ printData = true
+ }
+
+ parser := core.NewTemplateParser(data, &core.ParserOption{PrintData: printData})
html, err := parser.Render(c.HTML)
if err != nil {
return "", 500, fmt.Errorf("render error, please re-complie the page %s", err.Error())
diff --git a/sui/core/build.go b/sui/core/build.go
index 7d7d6851..e92bade0 100644
--- a/sui/core/build.go
+++ b/sui/core/build.go
@@ -52,13 +52,25 @@ func (page *Page) Build(option *BuildOption) (*goquery.Document, []string, error
}
// BuildForImport build the page for import
-func (page *Page) BuildForImport(option *BuildOption, slots map[string]interface{}) (string, string, string, []string, error) {
+func (page *Page) BuildForImport(option *BuildOption, slots map[string]interface{}, attrs map[string]string) (string, string, string, []string, error) {
warnings := []string{}
html, err := page.BuildHTML(option)
if err != nil {
warnings = append(warnings, err.Error())
}
+ data := map[string]interface{}{}
+ if slots != nil {
+ for k, v := range slots {
+ data[k] = v
+ }
+ }
+
+ if attrs != nil {
+ data["$prop"] = attrs
+ page.Attrs = attrs
+ }
+
// Add Style & Script & Warning
doc, err := NewDocument([]byte(html))
if err != nil {
@@ -83,7 +95,6 @@ func (page *Page) BuildForImport(option *BuildOption, slots map[string]interface
}
body := doc.Selection.Find("body")
-
if body.Length() > 1 {
body.SetHtml("
" + html + "
")
}
@@ -96,7 +107,7 @@ func (page *Page) BuildForImport(option *BuildOption, slots map[string]interface
}
// Replace the slots
- html, _ = Data(slots).ReplaceUse(slotRe, html)
+ html, _ = Data(data).ReplaceUse(slotRe, html)
return html, style, script, warnings, nil
}
@@ -122,6 +133,7 @@ func (page *Page) parse(doc *goquery.Document, option *BuildOption, warnings []s
continue
}
+ sel.SetAttr("parsed", "true")
ipage, err := tmpl.Page(name)
if err != nil {
sel.ReplaceWith(fmt.Sprintf("", err.Error()))
@@ -136,6 +148,7 @@ func (page *Page) parse(doc *goquery.Document, option *BuildOption, warnings []s
continue
}
+ // Set the parent
slots := map[string]interface{}{}
for _, slot := range sel.Find("slot").Nodes {
slotSel := goquery.NewDocumentFromNode(slot)
@@ -150,15 +163,37 @@ func (page *Page) parse(doc *goquery.Document, option *BuildOption, warnings []s
slots[slotName] = strings.TrimSpace(slotHTML)
}
+ // Set Attrs
+ attrs := map[string]string{}
+ if sel.Length() > 0 {
+ if page.Attrs != nil {
+ parentProps := Data{"$prop": page.Attrs}
+ for k, v := range page.Attrs {
+ if k == "is" {
+ continue
+ }
+ attrs[k], _ = parentProps.ReplaceUse(slotRe, v)
+ }
+ } else {
+ for _, attr := range sel.Nodes[0].Attr {
+ if attr.Key == "is" {
+ continue
+ }
+ attrs[attr.Key] = attr.Val
+ }
+ }
+ }
+
+ p := ipage.Get()
namespace := fmt.Sprintf("__page_%s_%d", strings.ReplaceAll(name, "/", "_"), idx)
- html, style, script, warns, err := ipage.Get().BuildForImport(&BuildOption{
+ html, style, script, warns, err := p.BuildForImport(&BuildOption{
SSR: option.SSR,
AssetRoot: option.AssetRoot,
IgnoreAssetRoot: option.IgnoreAssetRoot,
KeepPageTag: option.KeepPageTag,
IgnoreDocument: true,
Namespace: namespace,
- }, slots)
+ }, slots, attrs)
if err != nil {
sel.ReplaceWith(fmt.Sprintf("", err.Error()))
@@ -184,6 +219,11 @@ func (page *Page) parse(doc *goquery.Document, option *BuildOption, warnings []s
}
sel.SetAttr("s:slots", slotsAttr)
+
+ // Set Attrs
+ for k, v := range attrs {
+ sel.SetAttr(k, v)
+ }
continue
}
sel.ReplaceWithHtml(fmt.Sprintf("\n%s\n%s\n%s\n", style, html, script))
diff --git a/sui/core/data.go b/sui/core/data.go
index e94359d8..e7cc67d0 100644
--- a/sui/core/data.go
+++ b/sui/core/data.go
@@ -28,6 +28,9 @@ var options = []expr.Option{
func (data Data) New(stmt string) (*vm.Program, error) {
stmt = strings.TrimSpace(strings.TrimRight(strings.TrimLeft(stmt, "{{ "), "}}"))
stmt = strings.TrimSpace(strings.TrimRight(strings.TrimLeft(stmt, "[{ "), "}]"))
+ // ' => ' " => "
+ stmt = strings.ReplaceAll(stmt, "'", "'")
+ stmt = strings.ReplaceAll(stmt, """, "\"")
return expr.Compile(stmt, append([]expr.Option{expr.Env(data)}, options...)...)
}
diff --git a/sui/core/parser.go b/sui/core/parser.go
index 84738df8..28a34971 100644
--- a/sui/core/parser.go
+++ b/sui/core/parser.go
@@ -6,6 +6,7 @@ import (
"strings"
"github.com/PuerkitoBio/goquery"
+ jsoniter "github.com/json-iterator/go"
"golang.org/x/net/html"
)
@@ -28,8 +29,9 @@ type Mapping struct {
// ParserOption parser option
type ParserOption struct {
- Editor bool `json:"editor,omitempty"`
- Preview bool `json:"preview,omitempty"`
+ Editor bool `json:"editor,omitempty"`
+ Preview bool `json:"preview,omitempty"`
+ PrintData bool `json:"print_data,omitempty"`
}
// NewTemplateParser create a new template parser
@@ -50,12 +52,18 @@ func NewTemplateParser(data Data, option *ParserOption) *TemplateParser {
// Render parses and renders the HTML template
func (parser *TemplateParser) Render(html string) (string, error) {
- reader := bytes.NewReader([]byte(fmt.Sprintf("%s", html)))
+
+ if !strings.Contains(html, "%s`, html)
+ }
+
+ reader := bytes.NewReader([]byte(html))
doc, err := goquery.NewDocumentFromReader(reader)
if err != nil {
return "", err
}
- root := doc.Selection.Find("root")
+
+ root := doc.Selection.Find("html")
parser.parseNode(root.Nodes[0])
// Replace the nodes
@@ -64,9 +72,36 @@ func (parser *TemplateParser) Render(html string) (string, error) {
delete(parser.replace, sel)
}
- // fmt.Println(root.Html())
+ // Print the data
+ jsPrintData := ""
+ if parser.option != nil && parser.option.PrintData {
+ jsPrintData = "console.log(__sui_data);\n"
+ }
+
+ // Append the data to the body
+ body := doc.Find("body")
+ if body.Length() > 0 {
+ data, err := jsoniter.MarshalToString(parser.data)
+ if err != nil {
+ data, _ = jsoniter.MarshalToString(map[string]string{"error": err.Error()})
+ }
+ body.AppendHtml("\n",
+ )
+ }
+
+ // For editor
+ if parser.option != nil && parser.option.Editor {
+ return doc.Find("body").Html()
+ }
+
+ // fmt.Println(doc.Html())
// fmt.Println(parser.errors)
- return root.Html()
+ return doc.Html()
}
// Parse parses and renders the HTML template
@@ -108,6 +143,37 @@ func (parser *TemplateParser) parseElementNode(sel *goquery.Selection) {
if _, exist := sel.Attr("s:for"); exist {
parser.forStatementNode(sel)
}
+
+ // Parse the attributes
+ parser.parseElementAttrs(sel)
+}
+
+func (parser *TemplateParser) parseElementAttrs(sel *goquery.Selection) {
+ if len(sel.Nodes) < 0 {
+ return
+ }
+
+ if sel.AttrOr("parsed", "false") == "true" {
+ return
+ }
+
+ attrs := sel.Nodes[0].Attr
+ for _, attr := range attrs {
+ parser.sequence = parser.sequence + 1
+ res, hasStmt := parser.data.Replace(attr.Val)
+ if hasStmt {
+ bindings := strings.TrimSpace(attr.Val)
+ key := fmt.Sprintf("%v", parser.sequence)
+ parser.mapping[attr.Key] = Mapping{
+ Key: key,
+ Type: "attr",
+ Value: bindings,
+ }
+ sel.SetAttr(attr.Key, res)
+ bindname := fmt.Sprintf("s:bind:%s", attr.Key)
+ sel.SetAttr(bindname, bindings)
+ }
+ }
}
func (parser *TemplateParser) parseTextNode(node *html.Node) {
diff --git a/sui/core/types.go b/sui/core/types.go
index 3e779c29..3f0ecd34 100644
--- a/sui/core/types.go
+++ b/sui/core/types.go
@@ -25,15 +25,16 @@ type Setting struct {
// Page is the struct for the page
type Page struct {
- Route string `json:"route"`
- Name string `json:"name,omitempty"`
- TemplateID string `json:"-"`
- SuiID string `json:"-"`
- Config *PageConfig `json:"-"`
- Path string `json:"-"`
- Codes SourceCodes `json:"-"`
- Document []byte `json:"-"`
- GlobalData []byte `json:"-"`
+ Route string `json:"route"`
+ Name string `json:"name,omitempty"`
+ TemplateID string `json:"-"`
+ SuiID string `json:"-"`
+ Config *PageConfig `json:"-"`
+ Path string `json:"-"`
+ Codes SourceCodes `json:"-"`
+ Document []byte `json:"-"`
+ GlobalData []byte `json:"-"`
+ Attrs map[string]string `json:"-"`
}
// PageTreeNode is the struct for the page tree node