if token == tokenCloseTemplate {
inTemplate = false
}
- // If we're in a template and found a function identifier, replace it
+ // If we're in a template and found a template value identifier, replace it
// with a namespaced version
if inTemplate && isField(token) ||
inAttrs && isDataAttr[i] {
- raised = append(raised, strings.Split(token, ".")[1])
+ raised = append(raised, token[1:])
token = ".parent" + token
}
// ===== CONTEXT PREPROCESSING =====
// Raise any fields pre-existing in the context to .parent.field.
raiseFields := make([]string, 0)
+ raiseDepth := 0
htmltree.Walk(context.FirstChild, func(n *html.Node) (bool, error) {
- raiseFields = append(raiseFields, replaceTemplateFields(n)...)
+ if raiseDepth == 0 {
+ raiseFields = append(raiseFields, replaceTemplateFields(n)...)
+ }
+ if n.Type == html.TextNode {
+ if scopesUp(n.Data) {
+ raiseDepth++
+ } else if scopesDown(n.Data) {
+ raiseDepth--
+ }
+ }
return false, nil
})
// Remove :data attributes from the root of context, and prepare them
// Mix stuff from the context into the template using slots...
n := context.FirstChild
+ // slot context is the nodes currently considered candidates for slotting.
+ // Being in the slotCtx doesn't mean a node *will* be slotted, just that it
+ // might be, and if it is it needs to have its order preserved with every
+ // other node in the ctx.
+ slotCtx := make([]*html.Node, 0)
+ // nonslotCtx is a copy of slotCtx, but elements with no slot go here instead.
+ nonslotCtx := make([]*html.Node, 0)
+ var slotUsed, nonslotUsed bool
+ var targetSlot *html.Node
+ // slotCtxDepth is the depth of any template scope biz that surrounds a
+ // slotted node.
+ slotCtxDepth := 0
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 {
- if !isShadow {
- slot.Parent.InsertBefore(n, slot)
+ switch n.Type {
+ case html.TextNode:
+ // Raise/lower the slot context depth if scope goes up or down.
+ if scopesUp(n.Data) {
+ slotCtxDepth++
+ } else if scopesDown(n.Data) {
+ slotCtxDepth--
+ }
+ // Add the node to slotCtx, and a copy to nonslotCtx
+ slotCtx = append(slotCtx, n)
+ nonslotCtx = append(nonslotCtx, &html.Node{
+ Type: n.Type,
+ Data: n.Data,
+ })
+ case html.ElementNode:
+ slotName := htmltree.GetAttr(n, "slot")
+ slot, ok := slots[slotName]
+ if slotName != "" && ok {
+ // Add this element to the current slottable context.
+ slotCtx = append(slotCtx, n)
+ slotUsed = true
+ targetSlot = slot
+ // Mark the slot as used in usedSlot so its default content
+ // is removed.
+ usedSlot[slotName] = true
} else {
- context.AppendChild(n)
+ // Add it to the nonslottable context.
+ nonslotCtx = append(nonslotCtx, n)
+ nonslotUsed = true
+ }
+ }
+ if slotCtxDepth == 0 {
+ if slotUsed {
+ for _, slotCtxNode := range slotCtx {
+ targetSlot.Parent.InsertBefore(slotCtxNode, targetSlot)
+ }
+ slotCtx = make([]*html.Node, 0)
+ slotUsed = false
+ }
+ if nonslotUsed {
+ for _, nonslotCtxNode := range nonslotCtx {
+ root.AppendChild(nonslotCtxNode)
+ }
+ nonslotCtx = make([]*html.Node, 0)
+ nonslotUsed = false
}
- usedSlot[slotName] = true
- } else {
- root.AppendChild(n)
}
n = next
}