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