...

Source file src/github.com/PuerkitoBio/goquery/manipulation.go

Documentation: github.com/PuerkitoBio/goquery

     1  package goquery
     2  
     3  import (
     4  	"strings"
     5  
     6  	"golang.org/x/net/html"
     7  )
     8  
     9  // After applies the selector from the root document and inserts the matched elements
    10  // after the elements in the set of matched elements.
    11  //
    12  // If one of the matched elements in the selection is not currently in the
    13  // document, it's impossible to insert nodes after it, so it will be ignored.
    14  //
    15  // This follows the same rules as Selection.Append.
    16  func (s *Selection) After(selector string) *Selection {
    17  	return s.AfterMatcher(compileMatcher(selector))
    18  }
    19  
    20  // AfterMatcher applies the matcher from the root document and inserts the matched elements
    21  // after the elements in the set of matched elements.
    22  //
    23  // If one of the matched elements in the selection is not currently in the
    24  // document, it's impossible to insert nodes after it, so it will be ignored.
    25  //
    26  // This follows the same rules as Selection.Append.
    27  func (s *Selection) AfterMatcher(m Matcher) *Selection {
    28  	return s.AfterNodes(m.MatchAll(s.document.rootNode)...)
    29  }
    30  
    31  // AfterSelection inserts the elements in the selection after each element in the set of matched
    32  // elements.
    33  //
    34  // This follows the same rules as Selection.Append.
    35  func (s *Selection) AfterSelection(sel *Selection) *Selection {
    36  	return s.AfterNodes(sel.Nodes...)
    37  }
    38  
    39  // AfterHtml parses the html and inserts it after the set of matched elements.
    40  //
    41  // This follows the same rules as Selection.Append.
    42  func (s *Selection) AfterHtml(htmlStr string) *Selection {
    43  	return s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) {
    44  		nextSibling := node.NextSibling
    45  		for _, n := range nodes {
    46  			if node.Parent != nil {
    47  				node.Parent.InsertBefore(n, nextSibling)
    48  			}
    49  		}
    50  	})
    51  }
    52  
    53  // AfterNodes inserts the nodes after each element in the set of matched elements.
    54  //
    55  // This follows the same rules as Selection.Append.
    56  func (s *Selection) AfterNodes(ns ...*html.Node) *Selection {
    57  	return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) {
    58  		if sn.Parent != nil {
    59  			sn.Parent.InsertBefore(n, sn.NextSibling)
    60  		}
    61  	})
    62  }
    63  
    64  // Append appends the elements specified by the selector to the end of each element
    65  // in the set of matched elements, following those rules:
    66  //
    67  // 1) The selector is applied to the root document.
    68  //
    69  // 2) Elements that are part of the document will be moved to the new location.
    70  //
    71  // 3) If there are multiple locations to append to, cloned nodes will be
    72  // appended to all target locations except the last one, which will be moved
    73  // as noted in (2).
    74  func (s *Selection) Append(selector string) *Selection {
    75  	return s.AppendMatcher(compileMatcher(selector))
    76  }
    77  
    78  // AppendMatcher appends the elements specified by the matcher to the end of each element
    79  // in the set of matched elements.
    80  //
    81  // This follows the same rules as Selection.Append.
    82  func (s *Selection) AppendMatcher(m Matcher) *Selection {
    83  	return s.AppendNodes(m.MatchAll(s.document.rootNode)...)
    84  }
    85  
    86  // AppendSelection appends the elements in the selection to the end of each element
    87  // in the set of matched elements.
    88  //
    89  // This follows the same rules as Selection.Append.
    90  func (s *Selection) AppendSelection(sel *Selection) *Selection {
    91  	return s.AppendNodes(sel.Nodes...)
    92  }
    93  
    94  // AppendHtml parses the html and appends it to the set of matched elements.
    95  func (s *Selection) AppendHtml(htmlStr string) *Selection {
    96  	return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) {
    97  		for _, n := range nodes {
    98  			node.AppendChild(n)
    99  		}
   100  	})
   101  }
   102  
   103  // AppendNodes appends the specified nodes to each node in the set of matched elements.
   104  //
   105  // This follows the same rules as Selection.Append.
   106  func (s *Selection) AppendNodes(ns ...*html.Node) *Selection {
   107  	return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) {
   108  		sn.AppendChild(n)
   109  	})
   110  }
   111  
   112  // Before inserts the matched elements before each element in the set of matched elements.
   113  //
   114  // This follows the same rules as Selection.Append.
   115  func (s *Selection) Before(selector string) *Selection {
   116  	return s.BeforeMatcher(compileMatcher(selector))
   117  }
   118  
   119  // BeforeMatcher inserts the matched elements before each element in the set of matched elements.
   120  //
   121  // This follows the same rules as Selection.Append.
   122  func (s *Selection) BeforeMatcher(m Matcher) *Selection {
   123  	return s.BeforeNodes(m.MatchAll(s.document.rootNode)...)
   124  }
   125  
   126  // BeforeSelection inserts the elements in the selection before each element in the set of matched
   127  // elements.
   128  //
   129  // This follows the same rules as Selection.Append.
   130  func (s *Selection) BeforeSelection(sel *Selection) *Selection {
   131  	return s.BeforeNodes(sel.Nodes...)
   132  }
   133  
   134  // BeforeHtml parses the html and inserts it before the set of matched elements.
   135  //
   136  // This follows the same rules as Selection.Append.
   137  func (s *Selection) BeforeHtml(htmlStr string) *Selection {
   138  	return s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) {
   139  		for _, n := range nodes {
   140  			if node.Parent != nil {
   141  				node.Parent.InsertBefore(n, node)
   142  			}
   143  		}
   144  	})
   145  }
   146  
   147  // BeforeNodes inserts the nodes before each element in the set of matched elements.
   148  //
   149  // This follows the same rules as Selection.Append.
   150  func (s *Selection) BeforeNodes(ns ...*html.Node) *Selection {
   151  	return s.manipulateNodes(ns, false, func(sn *html.Node, n *html.Node) {
   152  		if sn.Parent != nil {
   153  			sn.Parent.InsertBefore(n, sn)
   154  		}
   155  	})
   156  }
   157  
   158  // Clone creates a deep copy of the set of matched nodes. The new nodes will not be
   159  // attached to the document.
   160  func (s *Selection) Clone() *Selection {
   161  	ns := newEmptySelection(s.document)
   162  	ns.Nodes = cloneNodes(s.Nodes)
   163  	return ns
   164  }
   165  
   166  // Empty removes all children nodes from the set of matched elements.
   167  // It returns the children nodes in a new Selection.
   168  func (s *Selection) Empty() *Selection {
   169  	var nodes []*html.Node
   170  
   171  	for _, n := range s.Nodes {
   172  		for c := n.FirstChild; c != nil; c = n.FirstChild {
   173  			n.RemoveChild(c)
   174  			nodes = append(nodes, c)
   175  		}
   176  	}
   177  
   178  	return pushStack(s, nodes)
   179  }
   180  
   181  // Prepend prepends the elements specified by the selector to each element in
   182  // the set of matched elements, following the same rules as Append.
   183  func (s *Selection) Prepend(selector string) *Selection {
   184  	return s.PrependMatcher(compileMatcher(selector))
   185  }
   186  
   187  // PrependMatcher prepends the elements specified by the matcher to each
   188  // element in the set of matched elements.
   189  //
   190  // This follows the same rules as Selection.Append.
   191  func (s *Selection) PrependMatcher(m Matcher) *Selection {
   192  	return s.PrependNodes(m.MatchAll(s.document.rootNode)...)
   193  }
   194  
   195  // PrependSelection prepends the elements in the selection to each element in
   196  // the set of matched elements.
   197  //
   198  // This follows the same rules as Selection.Append.
   199  func (s *Selection) PrependSelection(sel *Selection) *Selection {
   200  	return s.PrependNodes(sel.Nodes...)
   201  }
   202  
   203  // PrependHtml parses the html and prepends it to the set of matched elements.
   204  func (s *Selection) PrependHtml(htmlStr string) *Selection {
   205  	return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) {
   206  		firstChild := node.FirstChild
   207  		for _, n := range nodes {
   208  			node.InsertBefore(n, firstChild)
   209  		}
   210  	})
   211  }
   212  
   213  // PrependNodes prepends the specified nodes to each node in the set of
   214  // matched elements.
   215  //
   216  // This follows the same rules as Selection.Append.
   217  func (s *Selection) PrependNodes(ns ...*html.Node) *Selection {
   218  	return s.manipulateNodes(ns, true, func(sn *html.Node, n *html.Node) {
   219  		// sn.FirstChild may be nil, in which case this functions like
   220  		// sn.AppendChild()
   221  		sn.InsertBefore(n, sn.FirstChild)
   222  	})
   223  }
   224  
   225  // Remove removes the set of matched elements from the document.
   226  // It returns the same selection, now consisting of nodes not in the document.
   227  func (s *Selection) Remove() *Selection {
   228  	for _, n := range s.Nodes {
   229  		if n.Parent != nil {
   230  			n.Parent.RemoveChild(n)
   231  		}
   232  	}
   233  
   234  	return s
   235  }
   236  
   237  // RemoveFiltered removes from the current set of matched elements those that
   238  // match the selector filter. It returns the Selection of removed nodes.
   239  //
   240  // For example if the selection s contains "<h1>", "<h2>" and "<h3>"
   241  // and s.RemoveFiltered("h2") is called, only the "<h2>" node is removed
   242  // (and returned), while "<h1>" and "<h3>" are kept in the document.
   243  func (s *Selection) RemoveFiltered(selector string) *Selection {
   244  	return s.RemoveMatcher(compileMatcher(selector))
   245  }
   246  
   247  // RemoveMatcher removes from the current set of matched elements those that
   248  // match the Matcher filter. It returns the Selection of removed nodes.
   249  // See RemoveFiltered for additional information.
   250  func (s *Selection) RemoveMatcher(m Matcher) *Selection {
   251  	return s.FilterMatcher(m).Remove()
   252  }
   253  
   254  // ReplaceWith replaces each element in the set of matched elements with the
   255  // nodes matched by the given selector.
   256  // It returns the removed elements.
   257  //
   258  // This follows the same rules as Selection.Append.
   259  func (s *Selection) ReplaceWith(selector string) *Selection {
   260  	return s.ReplaceWithMatcher(compileMatcher(selector))
   261  }
   262  
   263  // ReplaceWithMatcher replaces each element in the set of matched elements with
   264  // the nodes matched by the given Matcher.
   265  // It returns the removed elements.
   266  //
   267  // This follows the same rules as Selection.Append.
   268  func (s *Selection) ReplaceWithMatcher(m Matcher) *Selection {
   269  	return s.ReplaceWithNodes(m.MatchAll(s.document.rootNode)...)
   270  }
   271  
   272  // ReplaceWithSelection replaces each element in the set of matched elements with
   273  // the nodes from the given Selection.
   274  // It returns the removed elements.
   275  //
   276  // This follows the same rules as Selection.Append.
   277  func (s *Selection) ReplaceWithSelection(sel *Selection) *Selection {
   278  	return s.ReplaceWithNodes(sel.Nodes...)
   279  }
   280  
   281  // ReplaceWithHtml replaces each element in the set of matched elements with
   282  // the parsed HTML.
   283  // It returns the removed elements.
   284  //
   285  // This follows the same rules as Selection.Append.
   286  func (s *Selection) ReplaceWithHtml(htmlStr string) *Selection {
   287  	s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) {
   288  		nextSibling := node.NextSibling
   289  		for _, n := range nodes {
   290  			if node.Parent != nil {
   291  				node.Parent.InsertBefore(n, nextSibling)
   292  			}
   293  		}
   294  	})
   295  	return s.Remove()
   296  }
   297  
   298  // ReplaceWithNodes replaces each element in the set of matched elements with
   299  // the given nodes.
   300  // It returns the removed elements.
   301  //
   302  // This follows the same rules as Selection.Append.
   303  func (s *Selection) ReplaceWithNodes(ns ...*html.Node) *Selection {
   304  	s.AfterNodes(ns...)
   305  	return s.Remove()
   306  }
   307  
   308  // SetHtml sets the html content of each element in the selection to
   309  // specified html string.
   310  func (s *Selection) SetHtml(htmlStr string) *Selection {
   311  	for _, context := range s.Nodes {
   312  		for c := context.FirstChild; c != nil; c = context.FirstChild {
   313  			context.RemoveChild(c)
   314  		}
   315  	}
   316  	return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) {
   317  		for _, n := range nodes {
   318  			node.AppendChild(n)
   319  		}
   320  	})
   321  }
   322  
   323  // SetText sets the content of each element in the selection to specified content.
   324  // The provided text string is escaped.
   325  func (s *Selection) SetText(text string) *Selection {
   326  	return s.SetHtml(html.EscapeString(text))
   327  }
   328  
   329  // Unwrap removes the parents of the set of matched elements, leaving the matched
   330  // elements (and their siblings, if any) in their place.
   331  // It returns the original selection.
   332  func (s *Selection) Unwrap() *Selection {
   333  	s.Parent().Each(func(i int, ss *Selection) {
   334  		// For some reason, jquery allows unwrap to remove the <head> element, so
   335  		// allowing it here too. Same for <html>. Why it allows those elements to
   336  		// be unwrapped while not allowing body is a mystery to me.
   337  		if ss.Nodes[0].Data != "body" {
   338  			ss.ReplaceWithSelection(ss.Contents())
   339  		}
   340  	})
   341  
   342  	return s
   343  }
   344  
   345  // Wrap wraps each element in the set of matched elements inside the first
   346  // element matched by the given selector. The matched child is cloned before
   347  // being inserted into the document.
   348  //
   349  // It returns the original set of elements.
   350  func (s *Selection) Wrap(selector string) *Selection {
   351  	return s.WrapMatcher(compileMatcher(selector))
   352  }
   353  
   354  // WrapMatcher wraps each element in the set of matched elements inside the
   355  // first element matched by the given matcher. The matched child is cloned
   356  // before being inserted into the document.
   357  //
   358  // It returns the original set of elements.
   359  func (s *Selection) WrapMatcher(m Matcher) *Selection {
   360  	return s.wrapNodes(m.MatchAll(s.document.rootNode)...)
   361  }
   362  
   363  // WrapSelection wraps each element in the set of matched elements inside the
   364  // first element in the given Selection. The element is cloned before being
   365  // inserted into the document.
   366  //
   367  // It returns the original set of elements.
   368  func (s *Selection) WrapSelection(sel *Selection) *Selection {
   369  	return s.wrapNodes(sel.Nodes...)
   370  }
   371  
   372  // WrapHtml wraps each element in the set of matched elements inside the inner-
   373  // most child of the given HTML.
   374  //
   375  // It returns the original set of elements.
   376  func (s *Selection) WrapHtml(htmlStr string) *Selection {
   377  	nodesMap := make(map[string][]*html.Node)
   378  	for _, context := range s.Nodes {
   379  		var parent *html.Node
   380  		if context.Parent != nil {
   381  			parent = context.Parent
   382  		} else {
   383  			parent = &html.Node{Type: html.ElementNode}
   384  		}
   385  		nodes, found := nodesMap[nodeName(parent)]
   386  		if !found {
   387  			nodes = parseHtmlWithContext(htmlStr, parent)
   388  			nodesMap[nodeName(parent)] = nodes
   389  		}
   390  		newSingleSelection(context, s.document).wrapAllNodes(cloneNodes(nodes)...)
   391  	}
   392  	return s
   393  }
   394  
   395  // WrapNode wraps each element in the set of matched elements inside the inner-
   396  // most child of the given node. The given node is copied before being inserted
   397  // into the document.
   398  //
   399  // It returns the original set of elements.
   400  func (s *Selection) WrapNode(n *html.Node) *Selection {
   401  	return s.wrapNodes(n)
   402  }
   403  
   404  func (s *Selection) wrapNodes(ns ...*html.Node) *Selection {
   405  	s.Each(func(i int, ss *Selection) {
   406  		ss.wrapAllNodes(ns...)
   407  	})
   408  
   409  	return s
   410  }
   411  
   412  // WrapAll wraps a single HTML structure, matched by the given selector, around
   413  // all elements in the set of matched elements. The matched child is cloned
   414  // before being inserted into the document.
   415  //
   416  // It returns the original set of elements.
   417  func (s *Selection) WrapAll(selector string) *Selection {
   418  	return s.WrapAllMatcher(compileMatcher(selector))
   419  }
   420  
   421  // WrapAllMatcher wraps a single HTML structure, matched by the given Matcher,
   422  // around all elements in the set of matched elements. The matched child is
   423  // cloned before being inserted into the document.
   424  //
   425  // It returns the original set of elements.
   426  func (s *Selection) WrapAllMatcher(m Matcher) *Selection {
   427  	return s.wrapAllNodes(m.MatchAll(s.document.rootNode)...)
   428  }
   429  
   430  // WrapAllSelection wraps a single HTML structure, the first node of the given
   431  // Selection, around all elements in the set of matched elements. The matched
   432  // child is cloned before being inserted into the document.
   433  //
   434  // It returns the original set of elements.
   435  func (s *Selection) WrapAllSelection(sel *Selection) *Selection {
   436  	return s.wrapAllNodes(sel.Nodes...)
   437  }
   438  
   439  // WrapAllHtml wraps the given HTML structure around all elements in the set of
   440  // matched elements. The matched child is cloned before being inserted into the
   441  // document.
   442  //
   443  // It returns the original set of elements.
   444  func (s *Selection) WrapAllHtml(htmlStr string) *Selection {
   445  	var context *html.Node
   446  	var nodes []*html.Node
   447  	if len(s.Nodes) > 0 {
   448  		context = s.Nodes[0]
   449  		if context.Parent != nil {
   450  			nodes = parseHtmlWithContext(htmlStr, context)
   451  		} else {
   452  			nodes = parseHtml(htmlStr)
   453  		}
   454  	}
   455  	return s.wrapAllNodes(nodes...)
   456  }
   457  
   458  func (s *Selection) wrapAllNodes(ns ...*html.Node) *Selection {
   459  	if len(ns) > 0 {
   460  		return s.WrapAllNode(ns[0])
   461  	}
   462  	return s
   463  }
   464  
   465  // WrapAllNode wraps the given node around the first element in the Selection,
   466  // making all other nodes in the Selection children of the given node. The node
   467  // is cloned before being inserted into the document.
   468  //
   469  // It returns the original set of elements.
   470  func (s *Selection) WrapAllNode(n *html.Node) *Selection {
   471  	if s.Size() == 0 {
   472  		return s
   473  	}
   474  
   475  	wrap := cloneNode(n)
   476  
   477  	first := s.Nodes[0]
   478  	if first.Parent != nil {
   479  		first.Parent.InsertBefore(wrap, first)
   480  		first.Parent.RemoveChild(first)
   481  	}
   482  
   483  	for c := getFirstChildEl(wrap); c != nil; c = getFirstChildEl(wrap) {
   484  		wrap = c
   485  	}
   486  
   487  	newSingleSelection(wrap, s.document).AppendSelection(s)
   488  
   489  	return s
   490  }
   491  
   492  // WrapInner wraps an HTML structure, matched by the given selector, around the
   493  // content of element in the set of matched elements. The matched child is
   494  // cloned before being inserted into the document.
   495  //
   496  // It returns the original set of elements.
   497  func (s *Selection) WrapInner(selector string) *Selection {
   498  	return s.WrapInnerMatcher(compileMatcher(selector))
   499  }
   500  
   501  // WrapInnerMatcher wraps an HTML structure, matched by the given selector,
   502  // around the content of element in the set of matched elements. The matched
   503  // child is cloned before being inserted into the document.
   504  //
   505  // It returns the original set of elements.
   506  func (s *Selection) WrapInnerMatcher(m Matcher) *Selection {
   507  	return s.wrapInnerNodes(m.MatchAll(s.document.rootNode)...)
   508  }
   509  
   510  // WrapInnerSelection wraps an HTML structure, matched by the given selector,
   511  // around the content of element in the set of matched elements. The matched
   512  // child is cloned before being inserted into the document.
   513  //
   514  // It returns the original set of elements.
   515  func (s *Selection) WrapInnerSelection(sel *Selection) *Selection {
   516  	return s.wrapInnerNodes(sel.Nodes...)
   517  }
   518  
   519  // WrapInnerHtml wraps an HTML structure, matched by the given selector, around
   520  // the content of element in the set of matched elements. The matched child is
   521  // cloned before being inserted into the document.
   522  //
   523  // It returns the original set of elements.
   524  func (s *Selection) WrapInnerHtml(htmlStr string) *Selection {
   525  	nodesMap := make(map[string][]*html.Node)
   526  	for _, context := range s.Nodes {
   527  		nodes, found := nodesMap[nodeName(context)]
   528  		if !found {
   529  			nodes = parseHtmlWithContext(htmlStr, context)
   530  			nodesMap[nodeName(context)] = nodes
   531  		}
   532  		newSingleSelection(context, s.document).wrapInnerNodes(cloneNodes(nodes)...)
   533  	}
   534  	return s
   535  }
   536  
   537  // WrapInnerNode wraps an HTML structure, matched by the given selector, around
   538  // the content of element in the set of matched elements. The matched child is
   539  // cloned before being inserted into the document.
   540  //
   541  // It returns the original set of elements.
   542  func (s *Selection) WrapInnerNode(n *html.Node) *Selection {
   543  	return s.wrapInnerNodes(n)
   544  }
   545  
   546  func (s *Selection) wrapInnerNodes(ns ...*html.Node) *Selection {
   547  	if len(ns) == 0 {
   548  		return s
   549  	}
   550  
   551  	s.Each(func(i int, s *Selection) {
   552  		contents := s.Contents()
   553  
   554  		if contents.Size() > 0 {
   555  			contents.wrapAllNodes(ns...)
   556  		} else {
   557  			s.AppendNodes(cloneNode(ns[0]))
   558  		}
   559  	})
   560  
   561  	return s
   562  }
   563  
   564  func parseHtml(h string) []*html.Node {
   565  	// Errors are only returned when the io.Reader returns any error besides
   566  	// EOF, but strings.Reader never will
   567  	nodes, err := html.ParseFragment(strings.NewReader(h), &html.Node{Type: html.ElementNode})
   568  	if err != nil {
   569  		panic("goquery: failed to parse HTML: " + err.Error())
   570  	}
   571  	return nodes
   572  }
   573  
   574  func parseHtmlWithContext(h string, context *html.Node) []*html.Node {
   575  	// Errors are only returned when the io.Reader returns any error besides
   576  	// EOF, but strings.Reader never will
   577  	nodes, err := html.ParseFragment(strings.NewReader(h), context)
   578  	if err != nil {
   579  		panic("goquery: failed to parse HTML: " + err.Error())
   580  	}
   581  	return nodes
   582  }
   583  
   584  // Get the first child that is an ElementNode
   585  func getFirstChildEl(n *html.Node) *html.Node {
   586  	c := n.FirstChild
   587  	for c != nil && c.Type != html.ElementNode {
   588  		c = c.NextSibling
   589  	}
   590  	return c
   591  }
   592  
   593  // Deep copy a slice of nodes.
   594  func cloneNodes(ns []*html.Node) []*html.Node {
   595  	cns := make([]*html.Node, 0, len(ns))
   596  
   597  	for _, n := range ns {
   598  		cns = append(cns, cloneNode(n))
   599  	}
   600  
   601  	return cns
   602  }
   603  
   604  // Deep copy a node. The new node has clones of all the original node's
   605  // children but none of its parents or siblings.
   606  func cloneNode(n *html.Node) *html.Node {
   607  	nn := &html.Node{
   608  		Type:     n.Type,
   609  		DataAtom: n.DataAtom,
   610  		Data:     n.Data,
   611  		Attr:     make([]html.Attribute, len(n.Attr)),
   612  	}
   613  
   614  	copy(nn.Attr, n.Attr)
   615  	for c := n.FirstChild; c != nil; c = c.NextSibling {
   616  		nn.AppendChild(cloneNode(c))
   617  	}
   618  
   619  	return nn
   620  }
   621  
   622  func (s *Selection) manipulateNodes(ns []*html.Node, reverse bool,
   623  	f func(sn *html.Node, n *html.Node)) *Selection {
   624  
   625  	lasti := s.Size() - 1
   626  
   627  	// net.Html doesn't provide document fragments for insertion, so to get
   628  	// things in the correct order with After() and Prepend(), the callback
   629  	// needs to be called on the reverse of the nodes.
   630  	if reverse {
   631  		for i, j := 0, len(ns)-1; i < j; i, j = i+1, j-1 {
   632  			ns[i], ns[j] = ns[j], ns[i]
   633  		}
   634  	}
   635  
   636  	for i, sn := range s.Nodes {
   637  		for _, n := range ns {
   638  			if i != lasti {
   639  				f(sn, cloneNode(n))
   640  			} else {
   641  				if n.Parent != nil {
   642  					n.Parent.RemoveChild(n)
   643  				}
   644  				f(sn, n)
   645  			}
   646  		}
   647  	}
   648  
   649  	return s
   650  }
   651  
   652  // eachNodeHtml parses the given html string and inserts the resulting nodes in the dom with the mergeFn.
   653  // The parsed nodes are inserted for each element of the selection.
   654  // isParent can be used to indicate that the elements of the selection should be treated as the parent for the parsed html.
   655  // A cache is used to avoid parsing the html multiple times should the elements of the selection result in the same context.
   656  func (s *Selection) eachNodeHtml(htmlStr string, isParent bool, mergeFn func(n *html.Node, nodes []*html.Node)) *Selection {
   657  	// cache to avoid parsing the html for the same context multiple times
   658  	nodeCache := make(map[string][]*html.Node)
   659  	var context *html.Node
   660  	for _, n := range s.Nodes {
   661  		if isParent {
   662  			context = n.Parent
   663  		} else {
   664  			if n.Type != html.ElementNode {
   665  				continue
   666  			}
   667  			context = n
   668  		}
   669  		if context != nil {
   670  			nodes, found := nodeCache[nodeName(context)]
   671  			if !found {
   672  				nodes = parseHtmlWithContext(htmlStr, context)
   673  				nodeCache[nodeName(context)] = nodes
   674  			}
   675  			mergeFn(n, cloneNodes(nodes))
   676  		}
   677  	}
   678  	return s
   679  }
   680  

View as plain text