Consolidate DOM manipulation utilities to eliminate code duplication
- Move addClass and setAttribute from ContentEngine/Injector to utils.go - Remove duplicate hasInsertrClass implementation - Add RemoveClass and HasClass utilities for completeness - Eliminates 74+ lines of exact duplication across files
This commit is contained in:
@@ -106,7 +106,7 @@ func (e *ContentEngine) ProcessContent(input ContentInput) (*ContentResult, erro
|
|||||||
collectionID := e.idGenerator.Generate(collectionElem.Node, input.FilePath)
|
collectionID := e.idGenerator.Generate(collectionElem.Node, input.FilePath)
|
||||||
|
|
||||||
// Add data-collection-id attribute to the collection container
|
// Add data-collection-id attribute to the collection container
|
||||||
e.setAttribute(collectionElem.Node, "data-collection-id", collectionID)
|
SetAttribute(collectionElem.Node, "data-collection-id", collectionID)
|
||||||
|
|
||||||
// Process collection during enhancement or content injection
|
// Process collection during enhancement or content injection
|
||||||
if input.Mode == Enhancement || input.Mode == ContentInjection {
|
if input.Mode == Enhancement || input.Mode == ContentInjection {
|
||||||
@@ -161,7 +161,7 @@ func (e *ContentEngine) findEditableElements(doc *html.Node) ([]InsertrElement,
|
|||||||
// First pass: find all .insertr and .insertr-add elements
|
// First pass: find all .insertr and .insertr-add elements
|
||||||
e.walkNodes(doc, func(n *html.Node) {
|
e.walkNodes(doc, func(n *html.Node) {
|
||||||
if n.Type == html.ElementNode {
|
if n.Type == html.ElementNode {
|
||||||
if e.hasInsertrClass(n) {
|
if hasInsertrClass(n) {
|
||||||
if isContainer(n) {
|
if isContainer(n) {
|
||||||
// Container element - mark for transformation
|
// Container element - mark for transformation
|
||||||
containersToTransform = append(containersToTransform, n)
|
containersToTransform = append(containersToTransform, n)
|
||||||
@@ -189,7 +189,7 @@ func (e *ContentEngine) findEditableElements(doc *html.Node) ([]InsertrElement,
|
|||||||
// Find viable children and add .insertr class to them
|
// Find viable children and add .insertr class to them
|
||||||
viableChildren := FindViableChildren(container)
|
viableChildren := FindViableChildren(container)
|
||||||
for _, child := range viableChildren {
|
for _, child := range viableChildren {
|
||||||
e.addClass(child, "insertr")
|
AddClass(child, "insertr")
|
||||||
insertrElements = append(insertrElements, InsertrElement{
|
insertrElements = append(insertrElements, InsertrElement{
|
||||||
Node: child,
|
Node: child,
|
||||||
})
|
})
|
||||||
@@ -207,12 +207,6 @@ func (e *ContentEngine) walkNodes(n *html.Node, fn func(*html.Node)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasInsertrClass checks if node has class="insertr"
|
|
||||||
func (e *ContentEngine) hasInsertrClass(node *html.Node) bool {
|
|
||||||
classes := GetClasses(node)
|
|
||||||
return slices.Contains(classes, "insertr")
|
|
||||||
}
|
|
||||||
|
|
||||||
// hasInsertrAddClass checks if node has class="insertr-add" (collection)
|
// hasInsertrAddClass checks if node has class="insertr-add" (collection)
|
||||||
func (e *ContentEngine) hasInsertrAddClass(node *html.Node) bool {
|
func (e *ContentEngine) hasInsertrAddClass(node *html.Node) bool {
|
||||||
classes := GetClasses(node)
|
classes := GetClasses(node)
|
||||||
@@ -222,63 +216,7 @@ func (e *ContentEngine) hasInsertrAddClass(node *html.Node) bool {
|
|||||||
// addContentAttributes adds data-content-id attribute only
|
// addContentAttributes adds data-content-id attribute only
|
||||||
func (e *ContentEngine) addContentAttributes(node *html.Node, contentID string) {
|
func (e *ContentEngine) addContentAttributes(node *html.Node, contentID string) {
|
||||||
// Add data-content-id attribute
|
// Add data-content-id attribute
|
||||||
e.setAttribute(node, "data-content-id", contentID)
|
SetAttribute(node, "data-content-id", contentID)
|
||||||
}
|
|
||||||
|
|
||||||
// setAttribute sets an attribute on a HTML node
|
|
||||||
func (e *ContentEngine) setAttribute(node *html.Node, key, value string) {
|
|
||||||
// Remove existing attribute if it exists
|
|
||||||
for i, attr := range node.Attr {
|
|
||||||
if attr.Key == key {
|
|
||||||
node.Attr[i].Val = value
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add new attribute
|
|
||||||
node.Attr = append(node.Attr, html.Attribute{
|
|
||||||
Key: key,
|
|
||||||
Val: value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// addClass safely adds a class to an HTML node
|
|
||||||
func (e *ContentEngine) addClass(node *html.Node, className string) {
|
|
||||||
var classAttr *html.Attribute
|
|
||||||
var classIndex int = -1
|
|
||||||
|
|
||||||
// Find existing class attribute
|
|
||||||
for idx, attr := range node.Attr {
|
|
||||||
if attr.Key == "class" {
|
|
||||||
classAttr = &attr
|
|
||||||
classIndex = idx
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var classes []string
|
|
||||||
if classAttr != nil {
|
|
||||||
classes = strings.Fields(classAttr.Val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if class already exists
|
|
||||||
if slices.Contains(classes, className) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add new class
|
|
||||||
classes = append(classes, className)
|
|
||||||
newClassValue := strings.Join(classes, " ")
|
|
||||||
|
|
||||||
if classIndex >= 0 {
|
|
||||||
// Update existing class attribute
|
|
||||||
node.Attr[classIndex].Val = newClassValue
|
|
||||||
} else {
|
|
||||||
// Add new class attribute
|
|
||||||
node.Attr = append(node.Attr, html.Attribute{
|
|
||||||
Key: "class",
|
|
||||||
Val: newClassValue,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeClass safely removes a class from an HTML node
|
// removeClass safely removes a class from an HTML node
|
||||||
@@ -367,7 +305,7 @@ func (e *ContentEngine) extractOriginalTemplate(node *html.Node) string {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractCleanTemplate extracts a clean template without data-content-id attributes and with placeholder content
|
// extractCleanTemplate extracts a clean template without data-content-id attributes and with placeholder content. Used for collection template variants.
|
||||||
func (e *ContentEngine) extractCleanTemplate(node *html.Node) string {
|
func (e *ContentEngine) extractCleanTemplate(node *html.Node) string {
|
||||||
// Clone the node to avoid modifying the original
|
// Clone the node to avoid modifying the original
|
||||||
clonedNode := e.cloneNode(node)
|
clonedNode := e.cloneNode(node)
|
||||||
@@ -406,7 +344,7 @@ func (e *ContentEngine) extractCleanTemplate(node *html.Node) string {
|
|||||||
func (e *ContentEngine) removeAttribute(n *html.Node, key string) {
|
func (e *ContentEngine) removeAttribute(n *html.Node, key string) {
|
||||||
for i, attr := range n.Attr {
|
for i, attr := range n.Attr {
|
||||||
if attr.Key == key {
|
if attr.Key == key {
|
||||||
n.Attr = append(n.Attr[:i], n.Attr[i+1:]...)
|
n.Attr = slices.Delete(n.Attr, i, i+1)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -605,7 +543,7 @@ func (e *ContentEngine) reconstructCollectionItems(collectionNode *html.Node, co
|
|||||||
|
|
||||||
// Inject data-item-id attribute for collection item identification
|
// Inject data-item-id attribute for collection item identification
|
||||||
if structuralChild.Type == html.ElementNode {
|
if structuralChild.Type == html.ElementNode {
|
||||||
e.setAttribute(structuralChild, "data-item-id", item.ItemID)
|
SetAttribute(structuralChild, "data-item-id", item.ItemID)
|
||||||
}
|
}
|
||||||
|
|
||||||
collectionNode.AppendChild(structuralChild)
|
collectionNode.AppendChild(structuralChild)
|
||||||
@@ -665,7 +603,7 @@ func (e *ContentEngine) generateStructuralTemplateFromChild(childElement *html.N
|
|||||||
if n.Type == html.ElementNode && e.hasClass(n, "insertr") {
|
if n.Type == html.ElementNode && e.hasClass(n, "insertr") {
|
||||||
if entryIndex < len(contentEntries) {
|
if entryIndex < len(contentEntries) {
|
||||||
// Set the data-content-id attribute
|
// Set the data-content-id attribute
|
||||||
e.setAttribute(n, "data-content-id", contentEntries[entryIndex].ID)
|
SetAttribute(n, "data-content-id", contentEntries[entryIndex].ID)
|
||||||
|
|
||||||
// Clear content - this will be hydrated during reconstruction
|
// Clear content - this will be hydrated during reconstruction
|
||||||
for child := n.FirstChild; child != nil; {
|
for child := n.FirstChild; child != nil; {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/insertr/insertr/internal/config"
|
"github.com/insertr/insertr/internal/config"
|
||||||
"github.com/insertr/insertr/internal/db"
|
"github.com/insertr/insertr/internal/db"
|
||||||
"golang.org/x/net/html"
|
"golang.org/x/net/html"
|
||||||
"slices"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Injector handles content injection into HTML elements
|
// Injector handles content injection into HTML elements
|
||||||
@@ -168,8 +167,8 @@ func (i *Injector) findElementByTag(node *html.Node, tag string) *html.Node {
|
|||||||
|
|
||||||
// AddContentAttributes adds necessary data attributes and insertr class for editor functionality
|
// AddContentAttributes adds necessary data attributes and insertr class for editor functionality
|
||||||
func (i *Injector) AddContentAttributes(node *html.Node, contentID string, contentType string) {
|
func (i *Injector) AddContentAttributes(node *html.Node, contentID string, contentType string) {
|
||||||
i.setAttribute(node, "data-content-id", contentID)
|
SetAttribute(node, "data-content-id", contentID)
|
||||||
i.addClass(node, "insertr")
|
AddClass(node, "insertr")
|
||||||
}
|
}
|
||||||
|
|
||||||
// InjectEditorAssets adds editor JavaScript to HTML document and injects demo gate if needed
|
// InjectEditorAssets adds editor JavaScript to HTML document and injects demo gate if needed
|
||||||
@@ -200,63 +199,6 @@ func (i *Injector) findHeadElement(node *html.Node) *html.Node {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setAttribute safely sets an attribute on an HTML node
|
|
||||||
func (i *Injector) setAttribute(node *html.Node, key, value string) {
|
|
||||||
// Remove existing attribute if present
|
|
||||||
for idx, attr := range node.Attr {
|
|
||||||
if attr.Key == key {
|
|
||||||
node.Attr = slices.Delete(node.Attr, idx, idx+1)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add new attribute
|
|
||||||
node.Attr = append(node.Attr, html.Attribute{
|
|
||||||
Key: key,
|
|
||||||
Val: value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// addClass safely adds a class to an HTML node
|
|
||||||
func (i *Injector) addClass(node *html.Node, className string) {
|
|
||||||
var classAttr *html.Attribute
|
|
||||||
var classIndex int = -1
|
|
||||||
|
|
||||||
// Find existing class attribute
|
|
||||||
for idx, attr := range node.Attr {
|
|
||||||
if attr.Key == "class" {
|
|
||||||
classAttr = &attr
|
|
||||||
classIndex = idx
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var classes []string
|
|
||||||
if classAttr != nil {
|
|
||||||
classes = strings.Fields(classAttr.Val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if class already exists
|
|
||||||
if slices.Contains(classes, className) {
|
|
||||||
return // Class already exists
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add new class
|
|
||||||
classes = append(classes, className)
|
|
||||||
newClassValue := strings.Join(classes, " ")
|
|
||||||
|
|
||||||
if classIndex >= 0 {
|
|
||||||
// Update existing class attribute
|
|
||||||
node.Attr[classIndex].Val = newClassValue
|
|
||||||
} else {
|
|
||||||
// Add new class attribute
|
|
||||||
node.Attr = append(node.Attr, html.Attribute{
|
|
||||||
Key: "class",
|
|
||||||
Val: newClassValue,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Element represents a parsed HTML element with metadata
|
// Element represents a parsed HTML element with metadata
|
||||||
type Element struct {
|
type Element struct {
|
||||||
Node *html.Node
|
Node *html.Node
|
||||||
|
|||||||
@@ -32,6 +32,112 @@ func GetAttribute(node *html.Node, key string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAttribute sets an attribute on an HTML node
|
||||||
|
func SetAttribute(node *html.Node, key, value string) {
|
||||||
|
// Check for existing attribute and update in place
|
||||||
|
for i, attr := range node.Attr {
|
||||||
|
if attr.Key == key {
|
||||||
|
node.Attr[i].Val = value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add new attribute if not found
|
||||||
|
node.Attr = append(node.Attr, html.Attribute{
|
||||||
|
Key: key,
|
||||||
|
Val: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddClass safely adds a class to an HTML node
|
||||||
|
func AddClass(node *html.Node, className string) {
|
||||||
|
var classAttr *html.Attribute
|
||||||
|
var classIndex int = -1
|
||||||
|
|
||||||
|
// Find existing class attribute
|
||||||
|
for idx, attr := range node.Attr {
|
||||||
|
if attr.Key == "class" {
|
||||||
|
classAttr = &attr
|
||||||
|
classIndex = idx
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var classes []string
|
||||||
|
if classAttr != nil {
|
||||||
|
classes = strings.Fields(classAttr.Val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if class already exists
|
||||||
|
if slices.Contains(classes, className) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new class
|
||||||
|
classes = append(classes, className)
|
||||||
|
newClassValue := strings.Join(classes, " ")
|
||||||
|
|
||||||
|
if classIndex >= 0 {
|
||||||
|
// Update existing class attribute
|
||||||
|
node.Attr[classIndex].Val = newClassValue
|
||||||
|
} else {
|
||||||
|
// Add new class attribute
|
||||||
|
node.Attr = append(node.Attr, html.Attribute{
|
||||||
|
Key: "class",
|
||||||
|
Val: newClassValue,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveClass safely removes a class from an HTML node
|
||||||
|
func RemoveClass(node *html.Node, className string) {
|
||||||
|
var classIndex int = -1
|
||||||
|
|
||||||
|
// Find existing class attribute
|
||||||
|
for idx, attr := range node.Attr {
|
||||||
|
if attr.Key == "class" {
|
||||||
|
classIndex = idx
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if classIndex == -1 {
|
||||||
|
return // No class attribute found
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse existing classes
|
||||||
|
classes := strings.Fields(node.Attr[classIndex].Val)
|
||||||
|
|
||||||
|
// Filter out the target class
|
||||||
|
var newClasses []string
|
||||||
|
for _, class := range classes {
|
||||||
|
if class != className {
|
||||||
|
newClasses = append(newClasses, class)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update or remove class attribute
|
||||||
|
if len(newClasses) == 0 {
|
||||||
|
// Remove class attribute entirely if no classes remain
|
||||||
|
node.Attr = slices.Delete(node.Attr, classIndex, classIndex+1)
|
||||||
|
} else {
|
||||||
|
// Update class attribute with remaining classes
|
||||||
|
node.Attr[classIndex].Val = strings.Join(newClasses, " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasClass checks if a node has a specific class
|
||||||
|
func HasClass(node *html.Node, className string) bool {
|
||||||
|
for _, attr := range node.Attr {
|
||||||
|
if attr.Key == "class" {
|
||||||
|
classes := strings.Fields(attr.Val)
|
||||||
|
if slices.Contains(classes, className) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Inline formatting elements that are safe for editing
|
// Inline formatting elements that are safe for editing
|
||||||
var inlineFormattingTags = map[string]bool{
|
var inlineFormattingTags = map[string]bool{
|
||||||
"strong": true,
|
"strong": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user