sub detach {
my $self = $_[0];
return undef unless(my $parent = $self->{'_parent'});
$self->{'_parent'} = undef;
my $cohort = $parent->{'_content'} || return $parent;
@$cohort = grep { not( ref($_) and $_ eq $self) } @$cohort;
# filter $self out, if parent has any evident content
return $parent;
}
=head2 $h->detach_content()
This unlinks all of $h's children from $h, and returns them.
Note that these are not explicitly destroyed; for that, you
can just use $h->delete_content.
=cut
sub detach_content {
my $c = $_[0]->{'_content'} || return(); # in case of no content
for (@$c) {
$_->{'_parent'} = undef if ref $_;
}
return splice @$c;
}
=head2 $h->replace_with( $element_or_text, ... )
This replaces C<$h> in its parent's content list with the nodes
specified. The element C<$h> (which by then may have no parent)
is returned. This causes a fatal error if C<$h> has no parent.
The list of nodes to insert may contain C<$h>, but at most once.
Aside from that possible exception, the nodes to insert should not
already be children of C<$h>'s parent.
Also, note that this method does not destroy C<$h> -- use
C<< $h->replace_with(...)->delete >> if you need that.
=cut
sub replace_with {
my ($self, @replacers) = @_;
Carp::croak "the target node has no parent"
unless my($parent) = $self->{'_parent'};
my $parent_content = $parent->{'_content'};
Carp::croak "the target node's parent has no content!?"
unless $parent_content and @$parent_content;
my $replacers_contains_self;
for(@replacers) {
if (!ref $_) {
# noop
}
elsif($_ eq $self) {
# noop, but check that it's there just once.
Carp::croak
"Replacement list contains several copies of target!"
if $replacers_contains_self++;
}
elsif($_ eq $parent) {
Carp::croak "Can't replace an item with its parent!";
}
elsif(ref($_) eq 'ARRAY') {
$_ = $self->new_from_lol($_);
}
else {
$_->detach;
$_->{'_parent'} = $parent;
# each of these are necessary
}
} # for @replacers
@$parent_content = map { ( ref($_) and $_ eq $self) ? @replacers : $_ } @$parent_content;
$self->{'_parent'} = undef unless $replacers_contains_self;
# if replacers does contain self, then the parent attribute is fine as-is
return $self;
}
=head2 $h->preinsert($element_or_text...)
Inserts the given nodes right BEFORE C<$h> in C<$h>'s parent's
content list. This causes a fatal error if C<$h> has no parent.
None of the given nodes should be C<$h> or other children of C<$h>.
Returns C<$h>.
=cut
sub preinsert {
my $self = shift;
return $self unless @_;
return $self->replace_with(@_, $self);
}
=head2 $h->postinsert($element_or_text...)
=10= |