Fixes #178 - Send context to parseHtml

This commit is contained in:
David Wilkins
2020-09-25 18:45:19 +02:00
committed by thiemok
parent c942deb265
commit 99c8f1ecd5
3 changed files with 174 additions and 18 deletions
+119 -18
View File
@@ -39,8 +39,14 @@ func (s *Selection) AfterSelection(sel *Selection) *Selection {
// AfterHtml parses the html and inserts it after the set of matched elements.
//
// This follows the same rules as Selection.Append.
func (s *Selection) AfterHtml(html string) *Selection {
return s.AfterNodes(parseHtml(html)...)
func (s *Selection) AfterHtml(htmlStr string) *Selection {
return s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) {
for _, n := range nodes {
if node.Parent != nil {
node.Parent.InsertBefore(n, node.NextSibling)
}
}
})
}
// AfterNodes inserts the nodes after each element in the set of matched elements.
@@ -85,8 +91,12 @@ func (s *Selection) AppendSelection(sel *Selection) *Selection {
}
// AppendHtml parses the html and appends it to the set of matched elements.
func (s *Selection) AppendHtml(html string) *Selection {
return s.AppendNodes(parseHtml(html)...)
func (s *Selection) AppendHtml(htmlStr string) *Selection {
return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) {
for _, n := range nodes {
node.AppendChild(n)
}
})
}
// AppendNodes appends the specified nodes to each node in the set of matched elements.
@@ -123,8 +133,14 @@ func (s *Selection) BeforeSelection(sel *Selection) *Selection {
// BeforeHtml parses the html and inserts it before the set of matched elements.
//
// This follows the same rules as Selection.Append.
func (s *Selection) BeforeHtml(html string) *Selection {
return s.BeforeNodes(parseHtml(html)...)
func (s *Selection) BeforeHtml(htmlStr string) *Selection {
return s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) {
for _, n := range nodes {
if node.Parent != nil {
node.Parent.InsertBefore(n, node)
}
}
})
}
// BeforeNodes inserts the nodes before each element in the set of matched elements.
@@ -184,8 +200,12 @@ func (s *Selection) PrependSelection(sel *Selection) *Selection {
}
// PrependHtml parses the html and prepends it to the set of matched elements.
func (s *Selection) PrependHtml(html string) *Selection {
return s.PrependNodes(parseHtml(html)...)
func (s *Selection) PrependHtml(htmlStr string) *Selection {
return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) {
for _, n := range nodes {
node.InsertBefore(n, node.FirstChild)
}
})
}
// PrependNodes prepends the specified nodes to each node in the set of
@@ -261,8 +281,15 @@ func (s *Selection) ReplaceWithSelection(sel *Selection) *Selection {
// It returns the removed elements.
//
// This follows the same rules as Selection.Append.
func (s *Selection) ReplaceWithHtml(html string) *Selection {
return s.ReplaceWithNodes(parseHtml(html)...)
func (s *Selection) ReplaceWithHtml(htmlStr string) *Selection {
s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) {
for _, n := range nodes {
if node.Parent != nil {
node.Parent.InsertBefore(n, node.NextSibling)
}
}
})
return s.Remove()
}
// ReplaceWithNodes replaces each element in the set of matched elements with
@@ -277,8 +304,17 @@ func (s *Selection) ReplaceWithNodes(ns ...*html.Node) *Selection {
// SetHtml sets the html content of each element in the selection to
// specified html string.
func (s *Selection) SetHtml(html string) *Selection {
return setHtmlNodes(s, parseHtml(html)...)
func (s *Selection) SetHtml(htmlStr string) *Selection {
for _, context := range s.Nodes {
for c := context.FirstChild; c != nil; c = context.FirstChild {
context.RemoveChild(c)
}
}
return s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) {
for _, n := range nodes {
node.AppendChild(n)
}
})
}
// SetText sets the content of each element in the selection to specified content.
@@ -334,8 +370,23 @@ func (s *Selection) WrapSelection(sel *Selection) *Selection {
// most child of the given HTML.
//
// It returns the original set of elements.
func (s *Selection) WrapHtml(html string) *Selection {
return s.wrapNodes(parseHtml(html)...)
func (s *Selection) WrapHtml(htmlStr string) *Selection {
nodesMap := make(map[html.NodeType][]*html.Node)
var parent *html.Node
for _, context := range s.Nodes {
if context.Parent != nil {
parent = context.Parent
} else {
parent = &html.Node{Type: html.ElementNode}
}
nodes, found := nodesMap[parent.Type]
if !found {
nodes = parseHtmlWithContext(htmlStr, parent)
nodesMap[parent.Type] = nodes
}
newSingleSelection(context, s.document).wrapAllNodes(cloneNodes(nodes)...)
}
return s
}
// WrapNode wraps each element in the set of matched elements inside the inner-
@@ -387,8 +438,18 @@ func (s *Selection) WrapAllSelection(sel *Selection) *Selection {
// document.
//
// It returns the original set of elements.
func (s *Selection) WrapAllHtml(html string) *Selection {
return s.wrapAllNodes(parseHtml(html)...)
func (s *Selection) WrapAllHtml(htmlStr string) *Selection {
var context *html.Node
var nodes []*html.Node
if len(s.Nodes) > 0 {
context = s.Nodes[0]
if context.Parent != nil {
nodes = parseHtmlWithContext(htmlStr, context)
} else {
nodes = parseHtml(htmlStr)
}
}
return s.wrapAllNodes(nodes...)
}
func (s *Selection) wrapAllNodes(ns ...*html.Node) *Selection {
@@ -457,8 +518,17 @@ func (s *Selection) WrapInnerSelection(sel *Selection) *Selection {
// cloned before being inserted into the document.
//
// It returns the original set of elements.
func (s *Selection) WrapInnerHtml(html string) *Selection {
return s.wrapInnerNodes(parseHtml(html)...)
func (s *Selection) WrapInnerHtml(htmlStr string) *Selection {
nodesMap := make(map[html.NodeType][]*html.Node)
for _, context := range s.Nodes {
nodes, found := nodesMap[context.Type]
if !found {
nodes = parseHtmlWithContext(htmlStr, context)
nodesMap[context.Type] = nodes
}
newSingleSelection(context, s.document).wrapInnerNodes(cloneNodes(nodes)...)
}
return s
}
// WrapInnerNode wraps an HTML structure, matched by the given selector, around
@@ -498,6 +568,16 @@ func parseHtml(h string) []*html.Node {
return nodes
}
func parseHtmlWithContext(h string, context *html.Node) []*html.Node {
// Errors are only returned when the io.Reader returns any error besides
// EOF, but strings.Reader never will
nodes, err := html.ParseFragment(strings.NewReader(h), context)
if err != nil {
panic("goquery: failed to parse HTML: " + err.Error())
}
return nodes
}
func setHtmlNodes(s *Selection, ns ...*html.Node) *Selection {
for _, n := range s.Nodes {
for c := n.FirstChild; c != nil; c = n.FirstChild {
@@ -577,3 +657,24 @@ func (s *Selection) manipulateNodes(ns []*html.Node, reverse bool,
return s
}
func (s *Selection) eachNodeHtml(htmlStr string, parent bool, fn func(n *html.Node, nodes []*html.Node)) *Selection {
nodesMap := make(map[html.NodeType][]*html.Node)
var context *html.Node
for _, n := range s.Nodes {
if parent {
context = n.Parent
} else {
context = n
}
if context != nil {
nodes, found := nodesMap[context.Type]
if !found {
nodes = parseHtmlWithContext(htmlStr, context)
nodesMap[context.Type] = nodes
}
fn(n, cloneNodes(nodes))
}
}
return s
}
+51
View File
@@ -56,6 +56,13 @@ func TestAfterHtml(t *testing.T) {
printSel(t, doc.Selection)
}
func TestAfterHtmlContext(t *testing.T) {
doc := DocBClone()
doc.Find("table tr td").AfterHtml("<td>Test</td>")
assertLength(t, doc.Find("table tr td").Nodes, 14)
printSel(t, doc.Selection)
}
func TestAppend(t *testing.T) {
doc := Doc2Clone()
doc.Find("#main").Append("#nf6")
@@ -113,6 +120,15 @@ func TestAppendHtml(t *testing.T) {
printSel(t, doc.Selection)
}
func TestAppendHtmlContext(t *testing.T) {
doc := DocBClone()
doc.Find("table tr").AppendHtml("<td class='new-node'>new node</td>")
assertLength(t, doc.Find("table td").Nodes, 11)
assertLength(t, doc.Find("table tr td.new-node:last-child").Nodes, 4)
printSel(t, doc.Selection)
}
func TestBefore(t *testing.T) {
doc := Doc2Clone()
doc.Find("#main").Before("#nf6")
@@ -151,6 +167,14 @@ func TestBeforeHtml(t *testing.T) {
printSel(t, doc.Selection)
}
func TestBeforeHtmlContext(t *testing.T) {
doc := DocBClone()
doc.Find("table tr td:first-child").BeforeHtml("<td class='new-node'>new node</td>")
assertLength(t, doc.Find("table td.new-node:first-child").Nodes, 3)
printSel(t, doc.Selection)
}
func TestEmpty(t *testing.T) {
doc := Doc2Clone()
s := doc.Find("#main").Empty()
@@ -218,6 +242,15 @@ func TestPrependHtml(t *testing.T) {
printSel(t, doc.Selection)
}
func TestPrependHtmlContext(t *testing.T) {
doc := DocBClone()
doc.Find("table tr").PrependHtml("<td class='new-node'>new node</td>")
assertLength(t, doc.Find("table td").Nodes, 11)
assertLength(t, doc.Find("table tr td.new-node:first-child").Nodes, 4)
printSel(t, doc.Selection)
}
func TestRemove(t *testing.T) {
doc := Doc2Clone()
doc.Find("#nf1").Remove()
@@ -278,6 +311,15 @@ func TestReplaceWithHtml(t *testing.T) {
printSel(t, doc.Selection)
}
func TestReplaceWithHtmlContext(t *testing.T) {
doc := DocBClone()
doc.Find("table th").ReplaceWithHtml("<td>Test</td><td>Replace</td>")
assertLength(t, doc.Find("table th").Nodes, 0)
assertLength(t, doc.Find("table tr:first-child td").Nodes, 6)
printSel(t, doc.Selection)
}
func TestSetHtml(t *testing.T) {
doc := Doc2Clone()
q := doc.Find("#main, #foot")
@@ -313,6 +355,15 @@ func TestSetHtmlEmpty(t *testing.T) {
printSel(t, doc.Selection)
}
func TestSetHtmlContext(t *testing.T) {
doc := DocBClone()
doc.Find("table tr").SetHtml("<td class='new-node'>Test</td>")
assertLength(t, doc.Find("table th").Nodes, 0)
assertLength(t, doc.Find("table td.new-node").Nodes, 4)
printSel(t, doc.Selection)
}
func TestSetText(t *testing.T) {
doc := Doc2Clone()
q := doc.Find("#main, #foot")
+4
View File
@@ -53,6 +53,10 @@ func DocB() *Document {
return docB
}
func DocBClone() *Document {
return CloneDocument(DocB())
}
func DocW() *Document {
if docW == nil {
docW = loadDoc("gowiki.html")