github.com/godaddy-x/freego@v1.0.156/goquery/manipulation.go (about) 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(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. 49 func (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). 67 func (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. 75 func (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. 83 func (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. 88 func (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. 95 func (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. 104 func (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. 111 func (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. 119 func (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. 126 func (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. 133 func (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. 143 func (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. 151 func (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. 166 func (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. 174 func (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. 182 func (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. 187 func (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. 195 func (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. 205 func (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. 217 func (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. 223 func (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. 232 func (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. 241 func (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. 250 func (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. 259 func (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. 268 func (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. 275 func (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. 281 func (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. 288 func (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. 306 func (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. 315 func (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. 324 func (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. 332 func (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. 341 func (s *Selection) WrapNode(n *html.Node) *Selection { 342 return s.wrapNodes(n) 343 } 344 345 func (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. 358 func (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. 367 func (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. 376 func (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. 385 func (s *Selection) WrapAllHtml(html string) *Selection { 386 return s.wrapAllNodes(parseHtml(html)...) 387 } 388 389 func (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. 401 func (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. 428 func (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. 437 func (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. 446 func (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. 455 func (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. 464 func (s *Selection) WrapInnerNode(n *html.Node) *Selection { 465 return s.wrapInnerNodes(n) 466 } 467 468 func (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 486 func 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 496 func 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 509 func 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. 518 func 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. 530 func 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 546 func (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 }