Skip to content

Instantly share code, notes, and snippets.

/clone.diff Secret

Created October 23, 2015 15:56
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/b533d94c0924f80dc209 to your computer and use it in GitHub Desktop.
Save anonymous/b533d94c0924f80dc209 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 2bda8b1..c9403bc 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]) }
@@ -126,7 +134,7 @@ sub remove { shift->replace('') }
sub replace {
my ($self, $new) = @_;
return $self->parse($new) if (my $tree = $self->tree)->[0] eq 'root';
- return $self->_replace($self->_parent, $tree, $self->_parse($new));
+ return $self->_replace($self->_parent, $tree, $self->_parse($new))->parent;
}
sub root {
@@ -138,7 +146,7 @@ sub root {
sub strip {
my $self = shift;
return $self if (my $tree = $self->tree)->[0] ne 'tag';
- return $self->_replace($tree->[3], $tree, ['root', _nodes($tree)]);
+ return $self->_replace($tree->[3], $tree, ['root', _nodes($tree)])->parent;
}
sub tag {
@@ -185,7 +193,7 @@ sub _add {
my $parent = $self->_parent;
splice @$parent, _offset($parent, $tree) + $offset, 0,
- _link($self->_parse($new), $parent);
+ _link($parent, _nodes($self->_parse($new)));
return $self;
}
@@ -235,7 +243,7 @@ sub _content {
$start = $start ? ($#$tree + 1) : _start($tree);
$offset = $offset ? $#$tree : 0;
- splice @$tree, $start, $offset, _link($self->_parse($new), $tree);
+ splice @$tree, $start, $offset, _link($tree, _nodes($self->_parse($new)));
return $self;
}
@@ -250,17 +258,16 @@ sub _delegate {
}
sub _link {
- my ($children, $parent) = @_;
+ my ($parent, @children) = @_;
# Link parent to children
- my @new = @$children[1 .. $#$children];
- for my $node (@new) {
+ for my $node (@children) {
my $offset = $node->[0] eq 'tag' ? 3 : 2;
$node->[$offset] = $parent;
weaken $node->[$offset];
}
- return @new;
+ return @children;
}
sub _maybe { $_[1] ? $_[0]->_build($_[1], $_[0]->xml) : undef }
@@ -280,12 +287,22 @@ 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) = @_;
- splice @$parent, _offset($parent, $tree), 1, _link($new, $parent);
- return $self->parent;
+ splice @$parent, _offset($parent, $tree), 1, _link($parent, _nodes($new));
+ return $self;
}
sub _select {
@@ -363,14 +380,14 @@ sub _wrap {
# Wrap content
if ($content) {
- push @$current, _link(['root', _nodes($tree)], $current);
- splice @$tree, _start($tree), $#$tree, _link($new, $tree);
+ push @$current, _link($current, _nodes($tree));
+ splice @$tree, _start($tree), $#$tree, _link($tree, _nodes($new));
return $self;
}
# Wrap element
$self->_replace($self->_parent, $tree, $new);
- push @$current, _link(['root', $tree], $current);
+ push @$current, _link($current, $tree);
return $self;
}
@@ -499,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.
@@ -513,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
@@ -583,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>
@@ -624,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 its parent and turn it into a new subtree with its own
+C<root> node.
+
=head2 find
my $collection = $dom->find('div ~ p');
@@ -766,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.
@@ -780,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
@@ -836,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>
@@ -970,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
@@ -989,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