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