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