File Coverage

blib/lib/HTML/ElementGlob.pm
Criterion Covered Total %
statement 9 76 11.8
branch 0 26 0.0
condition 0 3 0.0
subroutine 3 14 21.4
pod 0 9 0.0
total 12 128 9.3


line stmt bran cond sub pod time code
1             package HTML::ElementGlob;
2              
3 1     1   728 use strict;
  1         3  
  1         39  
4 1     1   6 use vars qw($VERSION $AUTOLOAD);
  1         2  
  1         55  
5              
6 1     1   6 use HTML::ElementSuper;
  1         10  
  1         8  
7              
8             $VERSION = '1.18';
9              
10             ####################################################
11             # glob_* methods do the HTML::Element type methods #
12             # on the glob structure itself, rather than muxing #
13             # the methods to its children. Most of these are #
14             # taken care of in AUTOLOAD, but we override some. #
15             ####################################################
16              
17             sub glob_delete_content {
18             # Do not propogate delete_content to children, as
19             # this should be the job of the real parent.
20 0     0 0   my $self = shift;
21 0 0         @{$self->glob_content} = () unless $self->glob_is_empty;
  0            
22 0           $self;
23             }
24              
25             sub glob_delete {
26             # Do not propogate delete to children, either.
27 0     0 0   my $self = shift;
28 0           $self->glob_delete_content;
29 0           %{$self} = ();
  0            
30             }
31              
32             sub context_is_glob {
33             # The newer HTML::Element class invokes detach() quite a bit
34             # during content operations -- *without* prepending glob_,
35             # obviously. We have to have some way of indicating to children
36             # globs that they should NOT broadcast methods to children --
37             # otherwise, all the regular elements in the child glob will get
38             # detach() invoked as well. So...if a glob knows it is about to
39             # perform an operation on another glob that should not be
40             # broadcast -- set this flag, then unset it afterwards.
41 0     0 0   my $self = shift;
42 0 0         @_ ? $self->{_context_is_glob} = shift : $self->{_context_is_glob};
43             }
44              
45             ######################################################
46             # MUXed methods (pass invocation to children) #
47             # Some methods do not really make sense in a globbed #
48             # context, so we try to 'do the right thing' here. #
49             ######################################################
50              
51             # HTML::Element based methods
52 0     0 0   sub push_content { shift->_content_manipulate('push_content', @_) }
53 0     0 0   sub unshift_content { shift->_content_manipulate('unshift_content', @_) }
54 0     0 0   sub splice_content { shift->_content_manipulate('splice_content', @_) }
55             # replace_with_content does not apply, as elements are not passed
56             # in the argument list, they are summoned from each individual
57             # element's content.
58              
59             # HTML::ElementSuper based methods
60 0     0 0   sub wrap_content { shift->_content_manipulate('wrap_content', @_) }
61 0     0 0   sub replace_content { shift->_content_manipulate('replace_content', @_) }
62              
63             sub _content_manipulate {
64             # Generic method for cloning and broadcasting the
65             # element trees provided to content methods
66 0     0     my $self = shift;
67 0           my $name = shift;
68 0           my @children = $self->{_element}->content_list;
69             # Find the first child that will have the method
70             # invoked.
71 0           my $first = undef;
72 0           foreach (0 .. $#children) {
73 0 0         if (ref $children[$_]) {
74 0           $first = $_;
75 0           last;
76             }
77             }
78 0 0         return undef unless defined $first;
79             # Deal with the tail elements first
80 0 0         if ($first < $#children) {
81 0           foreach ($first+1 .. $#children) {
82 0 0         next unless ref $children[$_];
83 0           $children[$_]->$name($self->{_element}->clone(@_));
84             }
85             }
86             # First child can have the real copy
87 0           $children[$first]->$name(@_);
88             }
89              
90             # Constructor
91              
92             sub new {
93 0     0 0   my $that = shift;
94 0   0       my $class = ref($that) || $that;
95 0           my $self = {};
96 0           bless $self,$class;
97 0           $self->{_element} = new HTML::ElementSuper @_;
98 0           $self->{_babysitter} = new HTML::ElementSuper @_;
99 0           $self;
100             }
101              
102             sub AUTOLOAD {
103             # Methods starting with glob deal with glob management,
104             # otherwise they get passed blindly to all children unless
105             # they have been overridden above.
106 0     0     my $self = shift;
107 0           my $name = $AUTOLOAD;
108 0           $name =~ s/.*:://;
109 0 0         return if $name =~ /^DESTROY/;
110              
111             # First, deal with glob_* induced methods
112 0 0         if ($name =~ s/^glob_//) {
    0          
113             # First, indicate to other globs that subsequent method
114             # calls are glob_ induced.
115 0           foreach (grep { ref $_ eq ref $self } @_) {
  0            
116 0           $_->context_is_glob(1);
117             }
118             # Store the pedigree of all elements, including globs,
119             # since no matter what a glob does it should not disturb
120             # the original lineage of an element. With the new
121             # HTML::Element, detach() gets called which also
122             # adjusts the content of the parent if available,
123             # so we give them to the babysitter for now (there
124             # is no publicly available method for just dropping
125             # a parent, and I'm loathe to mess with internal state
126             # variables and break containment on HTML::Element)
127 0           my @result;
128             my %parents;
129 0           for (grep { ref $_->parent } grep { ref $_ } @_) {
  0            
  0            
130 0 0         next if $parents{$_};
131 0           $parents{$_} = $_->parent;
132 0           $_->parent($self->{_babysitter});
133             }
134             # Invoke the method on our internal element
135 0           @result = $self->{_element}->$name(@_);
136             # Restore the lineages.
137              
138 0           for (grep { ref $_ } @_) {
  0            
139 0 0         $_->parent(delete $parents{$_}) if $parents{$_};
140             }
141             # Cancel glob_ induced context.
142 0           foreach (grep { ref $_ eq ref $self } @_) {
  0            
143 0           $_->context_is_glob(0);
144             }
145 0 0         return wantarray ? @result : $result[$#result];
146             }
147             elsif ($self->context_is_glob) {
148             # Here, we have intercepted a native method call that should
149             # actually be executing in glob_ context -- so we do so in
150             # order to ensure any overriden glob_* methods get properly
151             # invoked.
152 0           $name = "glob_$name";
153 0           return $self->$name(@_);
154             }
155              
156             # Otherwise broadcast to component elements.
157 0 0         if (!$self->{_element}->is_empty) {
158 0           my @results;
159 0           foreach (grep { ref $_ } $self->{_element}->content_list) {
  0            
160 0           push(@results, $_->$name(@_));
161             }
162 0           return @results;
163             }
164             }
165              
166             1;
167             __END__