--- /dev/null
+<template id="example-counter-template">
+ <slot name="text">Count:</slot>
+</template>
+<script>
+class ExampleCounter extends HTMLElement {
+ constructor() {
+ super();
+ const shadowRoot = this.attachShadow({ mode: "open" });
+ const template = document.getElementById("example-counter-template");
+
+ shadowRoot.appendChild(template.content.cloneNode(true));
+ }
+}
+customElements.define('example-counter', ExampleCounter);
+</script>
--- /dev/null
+// Copyright (C) 2024 early (LGPL)
+package ex07
+
+import (
+ "git.earlybird.gay/mast-engine/cmd/run-mast-examples/parts"
+ "git.earlybird.gay/mast-engine/component"
+ "git.earlybird.gay/mast-engine/page"
+)
+
+var Page = page.New("ex01", "page.html", page.Includes(parts.ExampleNav, counter))
+var counter = component.New("example-counter", "counter.html")
--- /dev/null
+<!DOCTYPE html>
+<html>
+ <header>
+ <title>Example 1</title>
+ </header>
+ <body>
+ <h1>Web Components</h1>
+
+ <example-counter>
+ <span slot="text">Example:</span>
+ </example-counter>
+ <!-- Ignore me!! I'll be explained in a second! -->
+ <example-nav>
+ <a href="ex02" slot="next">Parts</a>
+ </example-nav>
+ </body>
+</html>
\ No newline at end of file
<li><a href="/ex04">Go Templates</a></li>
<li>N/A</li>
<li><a href="/ex06">Passing Data to Parts</a></li>
+ <li><a href="/ex07">Web Components</a></li>
</ol>
</body>
</html>
\ No newline at end of file
"git.earlybird.gay/mast-engine/cmd/run-mast-examples/ex03-slots"
"git.earlybird.gay/mast-engine/cmd/run-mast-examples/ex04-templates"
"git.earlybird.gay/mast-engine/cmd/run-mast-examples/ex06-data"
+ "git.earlybird.gay/mast-engine/cmd/run-mast-examples/ex07-components"
"git.earlybird.gay/mast-engine/page"
)
mux.Handle("GET /ex03", ex03.Page)
mux.Handle("GET /ex04", ex04.Page)
mux.Handle("GET /ex06", ex06.Page)
+ mux.Handle("GET /ex07", ex07.Page)
http.ListenAndServe("0.0.0.0:3000", mux)
}
--- /dev/null
+// Copyright (C) 2024 early (LGPL)
+package component
+
+import (
+ "git.earlybird.gay/mast-engine/include"
+ "git.earlybird.gay/mast-engine/internal/compile"
+)
+
+type Component struct {
+ name string
+ source include.Opener
+
+ includes []compile.Source
+}
+
+type Config func(*Component)
+
+func Includes(includes ...compile.Source) Config {
+ return func(p *Component) {
+ p.includes = includes
+ }
+}
+
+func New(name string, source string, optional ...func(*Component)) *Component {
+ p := new(Component)
+ // Assign basic parameters
+ p.name = name
+ p.source = include.File(source, "git.earlybird.gay/mast-engine/component")
+ // Run optional arguments
+ for _, of := range optional {
+ of(p)
+ }
+ return p
+}
+
+func (p *Component) Name() string {
+ return p.name
+}
+
+func (p *Component) File() include.Opener {
+ return p.source
+}
+
+func (p *Component) Includes() []compile.Source {
+ return p.includes
+}
import (
"errors"
- "fmt"
"html/template"
"maps"
"strings"
- "git.earlybird.gay/mast-engine/htmltree"
"git.earlybird.gay/mast-engine/include"
"git.earlybird.gay/mast-engine/render"
"golang.org/x/net/html"
type Source interface {
Name() string
File() include.Opener
+ Includes() []Source
+}
+
+type TemplateSource interface {
+ Source
TemplateFuncs() template.FuncMap
OnLoad() render.OnLoadFunc
- Includes() []Source
}
type Sources map[string]Source
TemplateDataLoader render.Loader
}
-func insertSubSource(subSource Source, context *html.Node) (*computeNode, error) {
- insertedId := render.ID()
- computedName := fmt.Sprintf("%s_%s", snakeCase(subSource.Name()), insertedId)
- // ===== CONTEXT PREPROCESSING =====
- // Raise any fields pre-existing in the context to .parent.field.
- raiseFields := make([]string, 0)
- htmltree.Walk(context.FirstChild, func(n *html.Node) (bool, error) {
- raiseFields = append(raiseFields, replaceTemplateFields(n)...)
- return false, nil
- })
- // Remove :data attributes from the root of context, and prepare them
- // to be used as data for the computeNode.
- asData := removeDataAttrs(context)
- // ===== SUBSOURCE PROCESSING =====
- // Parse the subSource in the context of context.
- // Require that subSource be contained in a template.
- reader, err := subSource.File().Open()
- if err != nil {
- return nil, err
- }
- innerHTML, err := html.ParseFragment(reader, context)
- if err != nil {
- return nil, err
- }
- root := innerHTML[0]
- if root.Type != html.ElementNode || root.Data != "template" {
- return nil, errors.New("fragments must be contained in a template")
- }
-
- // Walk the tree to do a couple of things:
- // - Replace template functions with namespaced functions.
- // - Find slots in the subSource.
- slots := make(map[string]*html.Node)
- usedSlot := make(map[string]bool)
- htmltree.Walk(root, func(n *html.Node) (bool, error) {
- // Replace template functions with namespaced functions.
- replaceTemplateFuncs(subSource, n)
- // Find slots in the subSource.
- if n.Type == html.ElementNode && n.Data == "slot" {
- slotName := htmltree.GetAttr(n, "name")
- if _, ok := slots[slotName]; ok {
- return true, fmt.Errorf("found multiple slots named '%s'", slotName)
- } else {
- slots[slotName] = n
- usedSlot[slotName] = false
- }
- }
- return false, nil
- })
-
- // Mix stuff from the context into the template using slots...
- n := context.FirstChild
- for n != nil {
- next := n.NextSibling
- context.RemoveChild(n)
- // Do not slot any non-slottable nodes, or comments, because I do not
- // like them
- if n.Type == html.CommentNode {
- n = next
- continue
- }
- slotName := htmltree.GetAttr(n, "slot")
- slot := slots[slotName]
- if slot != nil {
- slot.Parent.InsertBefore(n, slot)
- usedSlot[slotName] = true
- } else {
- root.AppendChild(n)
- }
- n = next
- }
-
- // ...then move content from the template to the context...
- n = root.FirstChild
- for n != nil {
- next := n.NextSibling
- root.RemoveChild(n)
- context.AppendChild(n)
- n = next
- }
-
- // ...then remove any slots that were used.
- // For slots that aren't used, move up their data first.
- htmltree.Walk(context, func(n *html.Node) (bool, error) {
- if n.Type == html.ElementNode && n.Data == "slot" {
- slotName := htmltree.GetAttr(n, "name")
- slot := slots[slotName]
- used := usedSlot[slotName]
- if !used {
- n = slot.FirstChild
- for n != nil {
- next := n.NextSibling
- slot.RemoveChild(n)
- slot.Parent.InsertBefore(n, slot)
- n = next
- }
- }
- slot.Parent.RemoveChild(slot)
- }
- return false, nil
- })
-
- // Generate a computeNode for this part.
- compute := &computeNode{
- name: computedName,
- compute: subSource.OnLoad(),
- asDataFromParent: asData,
- raiseFromParent: raiseFields,
- }
-
- // Set ID and computed context, then generate a function that fulfills it
- htmltree.SetAttr(context, "id", insertedId)
- context.InsertBefore(&html.Node{
- Type: html.TextNode,
- Data: fmt.Sprintf("{{- with .computed.%s }}", computedName),
- }, context.FirstChild)
- context.InsertBefore(&html.Node{
- Type: html.TextNode,
- Data: "{{ end -}}",
- }, nil)
- return compute, nil
-}
-
-func Compile(root Source, transform ...func(root *html.Node)) (Result, error) {
+func Compile(root TemplateSource, transform ...func(root *html.Node)) (Result, error) {
var result Result
reader, err := root.File().Open()
if err != nil {
compute: root.OnLoad(),
}
+ // Insert component sources into document
+ for name, subSource := range fullDependencies {
+ // Easiest way to tell right now is what isn't a template source,
+ // but this bears re-evaluating later
+ if _, ok := subSource.(TemplateSource); ok {
+ continue
+ }
+ err := insertComponentSource(subSource, document)
+ if err != nil {
+ return result, err
+ }
+ delete(fullDependencies, name)
+ }
+
// gather global template funcs
// Start with the root's template, then add template funcs for all subsource
// with a namespace
siblingComputeNode := c
if n.Type == html.ElementNode {
if subSource, ok := fullDependencies[n.Data]; ok {
- // Add template funcs
- for fname, f := range subSource.TemplateFuncs() {
- templateFuncs[snakeCase(n.Data)+"_"+fname] = f
- }
- // Parse HTML fragment and replace the content of the node with it
- computeSubSource, err := insertSubSource(subSource, n)
- if err != nil {
- return err
+ if templateSource, ok := subSource.(TemplateSource); ok {
+ // Template sources (parts) are inserted inline
+ // Add template funcs
+ for fname, f := range templateSource.TemplateFuncs() {
+ templateFuncs[snakeCase(n.Data)+"_"+fname] = f
+ }
+ // Parse HTML fragment and replace the content of the node with it
+ computeSubSource, err := insertTemplateSource(templateSource, n)
+ if err != nil {
+ return err
+ }
+ c.children = append(c.children, computeSubSource)
+ childComputeNode = computeSubSource
}
- c.children = append(c.children, computeSubSource)
- childComputeNode = computeSubSource
}
}
var procErr error
--- /dev/null
+package compile
+
+import (
+ "errors"
+ "fmt"
+
+ "git.earlybird.gay/mast-engine/htmltree"
+ "golang.org/x/net/html"
+ "golang.org/x/net/html/atom"
+)
+
+var ErrBadComponentFormat = errors.New("web components must either be a script or a template and script")
+
+// insertComponentSource inserts a web component subSource into a document.
+// Unlike insertTemplateSource, this expects the root document and not the
+// context where the web component is being included.
+func insertComponentSource(subSource Source, document *html.Node) error {
+ // ===== SUBSOURCE PROCESSING =====
+ // Parse the subSource in the context of a node named subSource.Name().
+ // subSource should be:
+ // <template> and <script>
+ // <script>
+ reader, err := subSource.File().Open()
+ if err != nil {
+ return err
+ }
+ innerHTML, err := html.ParseFragment(reader, &html.Node{
+ Type: html.ElementNode,
+ Data: "div",
+ DataAtom: atom.Div,
+ })
+ if err != nil {
+ return err
+ }
+ htmltree.Minify()(innerHTML[0])
+
+ var template, script, body *html.Node
+
+ for _, node := range innerHTML {
+ fmt.Printf("%+v\n", node)
+ if node.Type == html.ElementNode && node.DataAtom == atom.Template {
+ template = node
+ }
+ if node.Type == html.ElementNode && node.DataAtom == atom.Script {
+ script = node
+ }
+ }
+
+ if script == nil {
+ return ErrBadComponentFormat
+ }
+
+ // Get body of document
+ htmltree.Walk(document, func(n *html.Node) (bool, error) {
+ if n.Type == html.ElementNode && n.Data == "body" {
+ body = n
+ return true, nil
+ }
+ return false, nil
+ })
+
+ // Insert template and script at start of body
+ body.InsertBefore(script, body.FirstChild)
+ if template != nil {
+ body.InsertBefore(template, body.FirstChild)
+ }
+
+ return nil
+}
+++ /dev/null
-// Copyright (C) 2024 early (LGPL)
-package compile
-
-import (
- "regexp"
- "strings"
-
- "git.earlybird.gay/mast-engine/htmltree"
- "golang.org/x/net/html"
-)
-
-const (
- tokenOpenTemplate = "{{"
- tokenCloseTemplate = "}}"
-)
-
-var funcRegexp = regexp.MustCompile(`^[a-zA-Z]\w*$`)
-var fieldRegexp = regexp.MustCompile(`^(?:.[a-zA-Z]\w*)+$`)
-
-func isFunc(token string) bool {
- return funcRegexp.MatchString(token)
-}
-
-func isField(token string) bool {
- return fieldRegexp.MatchString(token)
-}
-
-func replaceTemplateFuncs(source Source, n *html.Node) {
- var done, inTemplate bool
- var datas []string
- var sets []func(string)
-
- switch n.Type {
- case html.TextNode:
- datas = append(datas, n.Data)
- sets = append(sets, func(s string) {
- n.Data = s
- })
- case html.ElementNode:
- for _, attr := range n.Attr {
- datas = append(datas, attr.Val)
- sets = append(sets, func(s string) {
- htmltree.SetAttr(n, attr.Key, s)
- })
- }
- }
- for i, data := range datas {
- var output []string
- for {
- endToken := strings.Index(data, " ")
- if endToken == -1 {
- endToken = len(data)
- done = true
- }
- token := data[:endToken]
-
- // Track when we're in a template using open/close brackets
- if token == tokenOpenTemplate {
- inTemplate = true
- }
- if token == tokenCloseTemplate {
- inTemplate = false
- }
- // If we're in a template and found a function identifier, replace it
- // with a namespaced version
- if inTemplate && isFunc(token) {
- // Skip anything that the target source doesn't have as a templatefunc
- if _, ok := source.TemplateFuncs()[token]; ok {
- token = snakeCase(source.Name()) + "_" + token
- }
- }
-
- output = append(output, token)
- if done {
- break
- }
- data = data[endToken+1:]
- }
- sets[i](strings.Join(output, " "))
- }
-}
-
-// Sets template fields (.field) in any template strings in this node to
-// .parent.field.
-// Returns a list of raised fields.
-func replaceTemplateFields(n *html.Node) []string {
- var done, inTemplate, inAttrs bool
- var datas []string
- var isDataAttr []bool
- var sets []func(string)
- var raised []string
-
- switch n.Type {
- case html.TextNode:
- datas = append(datas, n.Data)
- sets = append(sets, func(s string) {
- n.Data = s
- })
- case html.ElementNode:
- inAttrs = true
- for _, attr := range n.Attr {
- datas = append(datas, attr.Val)
- isDataAttr = append(isDataAttr, attr.Key[0] == ':')
- sets = append(sets, func(s string) {
- htmltree.SetAttr(n, attr.Key, s)
- })
- }
- }
- for i, data := range datas {
- var output []string
- for {
- endToken := strings.Index(data, " ")
- if endToken == -1 {
- endToken = len(data)
- done = true
- }
- token := data[:endToken]
-
- // Track when we're in a template using open/close brackets
- if token == tokenOpenTemplate {
- inTemplate = true
- }
- if token == tokenCloseTemplate {
- inTemplate = false
- }
- // If we're in a template and found a function identifier, replace it
- // with a namespaced version
- if inTemplate && isField(token) ||
- inAttrs && isDataAttr[i] {
- raised = append(raised, strings.Split(token, ".")[1])
- token = ".parent" + token
- }
-
- output = append(output, token)
- if done {
- break
- }
- data = data[endToken+1:]
- }
- sets[i](strings.Join(output, " "))
- }
-
- return raised
-}
-
-// Removes data attributes :name.
-// Returns a map attr->val of the removed data attributes (: removed).
-func removeDataAttrs(n *html.Node) map[string]string {
- if n.Type != html.ElementNode {
- return nil
- }
- dataAttrs := make(map[string]string)
- for i := 0; i < len(n.Attr); {
- attr := n.Attr[i]
- if attr.Key[0] == ':' {
- dataAttrs[attr.Key[1:]] = attr.Val
- n.Attr = append(n.Attr[:i], n.Attr[i+1:]...)
- } else {
- i++
- }
- }
- return dataAttrs
-}
--- /dev/null
+// Copyright (C) 2024 early (LGPL)
+package compile
+
+import (
+ "errors"
+ "fmt"
+ "regexp"
+ "strings"
+
+ "git.earlybird.gay/mast-engine/htmltree"
+ "git.earlybird.gay/mast-engine/render"
+ "golang.org/x/net/html"
+)
+
+const (
+ tokenOpenTemplate = "{{"
+ tokenCloseTemplate = "}}"
+)
+
+var funcRegexp = regexp.MustCompile(`^[a-zA-Z]\w*$`)
+var fieldRegexp = regexp.MustCompile(`^(?:.[a-zA-Z]\w*)+$`)
+
+func isFunc(token string) bool {
+ return funcRegexp.MatchString(token)
+}
+
+func isField(token string) bool {
+ return fieldRegexp.MatchString(token)
+}
+
+func replaceTemplateFuncs(source TemplateSource, n *html.Node) {
+ var done, inTemplate bool
+ var datas []string
+ var sets []func(string)
+
+ switch n.Type {
+ case html.TextNode:
+ datas = append(datas, n.Data)
+ sets = append(sets, func(s string) {
+ n.Data = s
+ })
+ case html.ElementNode:
+ for _, attr := range n.Attr {
+ datas = append(datas, attr.Val)
+ sets = append(sets, func(s string) {
+ htmltree.SetAttr(n, attr.Key, s)
+ })
+ }
+ }
+ for i, data := range datas {
+ var output []string
+ for {
+ endToken := strings.Index(data, " ")
+ if endToken == -1 {
+ endToken = len(data)
+ done = true
+ }
+ token := data[:endToken]
+
+ // Track when we're in a template using open/close brackets
+ if token == tokenOpenTemplate {
+ inTemplate = true
+ }
+ if token == tokenCloseTemplate {
+ inTemplate = false
+ }
+ // If we're in a template and found a function identifier, replace it
+ // with a namespaced version
+ if inTemplate && isFunc(token) {
+ // Skip anything that the target source doesn't have as a templatefunc
+ if _, ok := source.TemplateFuncs()[token]; ok {
+ token = snakeCase(source.Name()) + "_" + token
+ }
+ }
+
+ output = append(output, token)
+ if done {
+ break
+ }
+ data = data[endToken+1:]
+ }
+ sets[i](strings.Join(output, " "))
+ }
+}
+
+// Sets template fields (.field) in any template strings in this node to
+// .parent.field.
+// Returns a list of raised fields.
+func replaceTemplateFields(n *html.Node) []string {
+ var done, inTemplate, inAttrs bool
+ var datas []string
+ var isDataAttr []bool
+ var sets []func(string)
+ var raised []string
+
+ switch n.Type {
+ case html.TextNode:
+ datas = append(datas, n.Data)
+ sets = append(sets, func(s string) {
+ n.Data = s
+ })
+ case html.ElementNode:
+ inAttrs = true
+ for _, attr := range n.Attr {
+ datas = append(datas, attr.Val)
+ isDataAttr = append(isDataAttr, attr.Key[0] == ':')
+ sets = append(sets, func(s string) {
+ htmltree.SetAttr(n, attr.Key, s)
+ })
+ }
+ }
+ for i, data := range datas {
+ var output []string
+ for {
+ endToken := strings.Index(data, " ")
+ if endToken == -1 {
+ endToken = len(data)
+ done = true
+ }
+ token := data[:endToken]
+
+ // Track when we're in a template using open/close brackets
+ if token == tokenOpenTemplate {
+ inTemplate = true
+ }
+ if token == tokenCloseTemplate {
+ inTemplate = false
+ }
+ // If we're in a template and found a function identifier, replace it
+ // with a namespaced version
+ if inTemplate && isField(token) ||
+ inAttrs && isDataAttr[i] {
+ raised = append(raised, strings.Split(token, ".")[1])
+ token = ".parent" + token
+ }
+
+ output = append(output, token)
+ if done {
+ break
+ }
+ data = data[endToken+1:]
+ }
+ sets[i](strings.Join(output, " "))
+ }
+
+ return raised
+}
+
+// Removes data attributes :name.
+// Returns a map attr->val of the removed data attributes (: removed).
+func removeDataAttrs(n *html.Node) map[string]string {
+ if n.Type != html.ElementNode {
+ return nil
+ }
+ dataAttrs := make(map[string]string)
+ for i := 0; i < len(n.Attr); {
+ attr := n.Attr[i]
+ if attr.Key[0] == ':' {
+ dataAttrs[attr.Key[1:]] = attr.Val
+ n.Attr = append(n.Attr[:i], n.Attr[i+1:]...)
+ } else {
+ i++
+ }
+ }
+ return dataAttrs
+}
+
+func insertTemplateSource(subSource TemplateSource, context *html.Node) (*computeNode, error) {
+ insertedId := render.ID()
+ computedName := fmt.Sprintf("%s_%s", snakeCase(subSource.Name()), insertedId)
+ // ===== CONTEXT PREPROCESSING =====
+ // Raise any fields pre-existing in the context to .parent.field.
+ raiseFields := make([]string, 0)
+ htmltree.Walk(context.FirstChild, func(n *html.Node) (bool, error) {
+ raiseFields = append(raiseFields, replaceTemplateFields(n)...)
+ return false, nil
+ })
+ // Remove :data attributes from the root of context, and prepare them
+ // to be used as data for the computeNode.
+ asData := removeDataAttrs(context)
+ // ===== SUBSOURCE PROCESSING =====
+ // Parse the subSource in the context of context.
+ // Require that subSource be contained in a template.
+ reader, err := subSource.File().Open()
+ if err != nil {
+ return nil, err
+ }
+ innerHTML, err := html.ParseFragment(reader, context)
+ if err != nil {
+ return nil, err
+ }
+ root := innerHTML[0]
+ if root.Type != html.ElementNode || root.Data != "template" {
+ return nil, errors.New("fragments must be contained in a template")
+ }
+
+ // Walk the tree to do a couple of things:
+ // - Replace template functions with namespaced functions.
+ // - Find slots in the subSource.
+ slots := make(map[string]*html.Node)
+ usedSlot := make(map[string]bool)
+ htmltree.Walk(root, func(n *html.Node) (bool, error) {
+ // Replace template functions with namespaced functions.
+ replaceTemplateFuncs(subSource, n)
+ // Find slots in the subSource.
+ if n.Type == html.ElementNode && n.Data == "slot" {
+ slotName := htmltree.GetAttr(n, "name")
+ if _, ok := slots[slotName]; ok {
+ return true, fmt.Errorf("found multiple slots named '%s'", slotName)
+ } else {
+ slots[slotName] = n
+ usedSlot[slotName] = false
+ }
+ }
+ return false, nil
+ })
+
+ // Mix stuff from the context into the template using slots...
+ n := context.FirstChild
+ for n != nil {
+ next := n.NextSibling
+ context.RemoveChild(n)
+ // Do not slot any non-slottable nodes, or comments, because I do not
+ // like them
+ if n.Type == html.CommentNode {
+ n = next
+ continue
+ }
+ slotName := htmltree.GetAttr(n, "slot")
+ slot := slots[slotName]
+ if slot != nil {
+ slot.Parent.InsertBefore(n, slot)
+ usedSlot[slotName] = true
+ } else {
+ root.AppendChild(n)
+ }
+ n = next
+ }
+
+ // ...then move content from the template to the context...
+ n = root.FirstChild
+ for n != nil {
+ next := n.NextSibling
+ root.RemoveChild(n)
+ context.AppendChild(n)
+ n = next
+ }
+
+ // ...then remove any slots that were used.
+ // For slots that aren't used, move up their data first.
+ htmltree.Walk(context, func(n *html.Node) (bool, error) {
+ if n.Type == html.ElementNode && n.Data == "slot" {
+ slotName := htmltree.GetAttr(n, "name")
+ slot := slots[slotName]
+ used := usedSlot[slotName]
+ if !used {
+ n = slot.FirstChild
+ for n != nil {
+ next := n.NextSibling
+ slot.RemoveChild(n)
+ slot.Parent.InsertBefore(n, slot)
+ n = next
+ }
+ }
+ slot.Parent.RemoveChild(slot)
+ }
+ return false, nil
+ })
+
+ // Generate a computeNode for this part.
+ compute := &computeNode{
+ name: computedName,
+ compute: subSource.OnLoad(),
+ asDataFromParent: asData,
+ raiseFromParent: raiseFields,
+ }
+
+ // Set ID and computed context, then generate a function that fulfills it
+ htmltree.SetAttr(context, "id", insertedId)
+ context.InsertBefore(&html.Node{
+ Type: html.TextNode,
+ Data: fmt.Sprintf("{{- with .computed.%s }}", computedName),
+ }, context.FirstChild)
+ context.InsertBefore(&html.Node{
+ Type: html.TextNode,
+ Data: "{{ end -}}",
+ }, nil)
+ return compute, nil
+}
"git.earlybird.gay/mast-engine/htmltree"
"git.earlybird.gay/mast-engine/include"
"git.earlybird.gay/mast-engine/internal/compile"
- "git.earlybird.gay/mast-engine/part"
"git.earlybird.gay/mast-engine/render"
)
source include.Opener
raw string
- parts []*part.Part
- onLoad render.OnLoadFunc
- indent string
+ includes []compile.Source
+ onLoad render.OnLoadFunc
+ indent string
template *template.Template
templateFuncs template.FuncMap
}
}
-func Includes(parts ...*part.Part) Config {
+func Includes(includes ...compile.Source) Config {
return func(p *Page) {
- p.parts = parts
+ p.includes = includes
}
}
}
func (p *Page) Includes() []compile.Source {
- cast := make([]compile.Source, len(p.parts))
- for i, child := range p.parts {
- cast[i] = child
- }
- return cast
+ return p.includes
}
func (p *Page) Raw() string {
name string
source include.Opener
- parts []*Part
+ includes []compile.Source
onLoad render.OnLoadFunc
templateFuncs template.FuncMap
}
}
}
-func Includes(parts ...*Part) Config {
+func Includes(includes ...compile.Source) Config {
return func(p *Part) {
- p.parts = parts
+ p.includes = includes
}
}
}
func (p *Part) Includes() []compile.Source {
- cast := make([]compile.Source, len(p.parts))
- for i, child := range p.parts {
- cast[i] = child
- }
- return cast
+ return p.includes
}
+++ /dev/null
-// Copyright (C) 2024 early (LGPL)
-package part
-
-type Parts []*Part
-
-func (p Parts) GetChild(i int) *Part {
- if i >= 0 && i < len(p) {
- return p[i]
- }
- return nil
-}