Skip to content

Instantly share code, notes, and snippets.

/detach.diff Secret

Created October 23, 2015 17:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/3caf5d2cf4a6c3a7c70e to your computer and use it in GitHub Desktop.
Save anonymous/3caf5d2cf4a6c3a7c70e to your computer and use it in GitHub Desktop.
diff --git a/Changes b/Changes
index d508b80..bdfa978 100644
--- a/Changes
+++ b/Changes
@@ -1,5 +1,8 @@
6.26 2015-10-23
+ - Added detach method to Mojo::DOM.
+ - Improved performance of DOM manipulation methods in Mojo::DOM significantly
+ when working with existing Mojo::DOM objects.
6.25 2015-10-21
- Deprecated Mojo::Message::Request::proxy with boolean and string arguments
diff --git a/lib/Mojo/DOM.pm b/lib/Mojo/DOM.pm
index 400e04d..4387e58 100644
--- a/lib/Mojo/DOM.pm
+++ b/lib/Mojo/DOM.pm
@@ -15,6 +15,7 @@ use Mojo::DOM::CSS;
use Mojo::DOM::HTML;
use Mojo::Util 'squish';
use Scalar::Util qw(blessed weaken);
+use Storable 'dclone';
sub all_text { shift->_all_text(1, @_) }
@@ -68,6 +69,13 @@ sub content {
sub descendant_nodes { $_[0]->_collect(_all(_nodes($_[0]->tree))) }
+sub detach {
+ my $self = shift;
+ return $self if (my $tree = $self->tree)->[0] eq 'root';
+ _link(my $new = ['root', $tree], _nodes($tree));
+ return $self->_replace($self->_parent, $tree, ['root'])->tree($new);
+}
+
sub find { $_[0]->_collect(@{$_[0]->_css->select($_[1])}) }
sub following { _select($_[0]->_collect(@{$_[0]->_siblings(1)->[1]}), $_[1]) }
@@ -279,7 +287,17 @@ sub _offset {
sub _parent { $_[0]->tree->[$_[0]->type eq 'tag' ? 3 : 2] }
-sub _parse { Mojo::DOM::HTML->new(xml => shift->xml)->parse(shift)->tree }
+sub _parse {
+ my ($self, $html) = @_;
+
+ return Mojo::DOM::HTML->new(xml => $self->xml)->parse($html)->tree
+ unless blessed $html && $html->isa('Mojo::DOM');
+
+ my $clone = dclone $html->tree;
+ return $clone if $clone->[0] eq 'root';
+ _link(my $parent = ['root', $clone], _nodes($clone));
+ return $parent;
+}
sub _replace {
my ($self, $parent, $tree, $new) = @_;
@@ -498,6 +516,7 @@ All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
=head2 append
+ $dom = $dom->append(Mojo::DOM->new);
$dom = $dom->append('<p>I ♥ Mojolicious!</p>');
Append HTML/XML fragment to this node.
@@ -512,6 +531,7 @@ Append HTML/XML fragment to this node.
=head2 append_content
+ $dom = $dom->append_content(Mojo::DOM->new);
$dom = $dom->append_content('<p>I ♥ Mojolicious!</p>');
Append HTML/XML fragment (for C<root> and C<tag> nodes) or raw content to this
@@ -582,6 +602,7 @@ All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
=head2 content
my $str = $dom->content;
+ $dom = $dom->content(Mojo::DOM->new);
$dom = $dom->content('<p>I ♥ Mojolicious!</p>');
Return this node's content or replace it with HTML/XML fragment (for C<root>
@@ -623,6 +644,13 @@ element as L<Mojo::DOM> objects.
->at('p')->descendant_nodes->grep(sub { $_->type eq 'text' })
->map(content => 'test')->first->root;
+=head2 detach
+
+ $dom = $dom->detach;
+
+Detach this node from L</"parent"> and turn it into a new subtree with its own
+C<root> node.
+
=head2 find
my $collection = $dom->find('div ~ p');
@@ -765,6 +793,7 @@ node as L<Mojo::DOM> objects.
=head2 prepend
+ $dom = $dom->prepend(Mojo::DOM->new);
$dom = $dom->prepend('<p>I ♥ Mojolicious!</p>');
Prepend HTML/XML fragment to this node.
@@ -779,6 +808,7 @@ Prepend HTML/XML fragment to this node.
=head2 prepend_content
+ $dom = $dom->prepend_content(Mojo::DOM->new);
$dom = $dom->prepend_content('<p>I ♥ Mojolicious!</p>');
Prepend HTML/XML fragment (for C<root> and C<tag> nodes) or raw content to this
@@ -835,6 +865,7 @@ Remove this node and return L</"root"> (for C<root> nodes) or L</"parent">.
=head2 replace
+ my $parent = $dom->replace(Mojo::DOM->new);
my $parent = $dom->replace('<div>I ♥ Mojolicious!</div>');
Replace this node with HTML/XML fragment and return L</"root"> (for C<root>
@@ -969,6 +1000,7 @@ if none could be found.
=head2 wrap
+ $dom = $dom->wrap(Mojo::DOM->new);
$dom = $dom->wrap('<div></div>');
Wrap HTML/XML fragment around this node, placing it as the last child of the
@@ -988,6 +1020,7 @@ first innermost element.
=head2 wrap_content
+ $dom = $dom->wrap_content(Mojo::DOM->new);
$dom = $dom->wrap_content('<div></div>');
Wrap HTML/XML fragment around this node's content, placing it as the last
diff --git a/t/mojo/dom.t b/t/mojo/dom.t
index b6c01a9..c9a15ea 100644
--- a/t/mojo/dom.t
+++ b/t/mojo/dom.t
@@ -2120,6 +2120,22 @@ is $dom->at('a')->wrap_content('C<c><d>D</d><e>E</e></c>F')->parent->type,
is "$dom", '<e:a>C<c><d>D1<b c="d">Test</b></d><e>E</e></c>F</e:a>',
'right result';
+# Detach nodes
+$dom = Mojo::DOM->new('<p><b>Test</b>123</p>')->detach;
+is "$dom", '<p><b>Test</b>123</p>', 'right result';
+my $dom2 = $dom->at('b')->[0];
+is $dom2->type, 'text', 'right type';
+is $dom2->detach->type, 'root', 'right type';
+is "$dom2", 'Test', 'right result';
+is "$dom", '<p><b></b>123</p>', 'right result';
+$dom->at('p')->append($dom2);
+is "$dom", '<p><b></b>123</p>Test', 'right result';
+$dom->at('p')->prepend($dom2);
+is "$dom", 'Test<p><b></b>123</p>Test', 'right result';
+$dom->at('b')->content(Mojo::DOM->new('B'))
+ ->append_content(Mojo::DOM->new('C'))->prepend_content(Mojo::DOM->new('A'));
+is "$dom", 'Test<p><b>ABC</b>123</p>Test', 'right result';
+
# Broken "div" in "td"
$dom = Mojo::DOM->new(<<EOF);
<table>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment