File Coverage

lib/HTML/Filter/Callbacks/Tag.pm
Criterion Covered Total %
statement 124 126 98.4
branch 47 60 78.3
condition 21 34 61.7
subroutine 19 19 100.0
pod 15 15 100.0
total 226 254 88.9


line stmt bran cond sub pod time code
1             package HTML::Filter::Callbacks::Tag;
2              
3 4     4   21 use strict;
  4         7  
  4         156  
4 4     4   34 use warnings;
  4         8  
  4         131  
5 4     4   21 use HTML::Entities;
  4         6  
  4         6749  
6              
7             sub new {
8 16     16 1 35 my ($class, %args) = @_;
9              
10 16         87 bless {}, $class;
11             }
12              
13             sub set {
14 78     78 1 126 my ($self, $tokens, $org, $skipped_text) = @_;
15              
16 78         140 my ($name, @attrs) = @$tokens;
17 78 100 100     264 if (@attrs >= 2 and $attrs[-1] eq '/' and $attrs[-2] eq '/') {
      66        
18 4         10 splice @attrs, -2, 2;
19             }
20              
21 78 100       991 %$self = (
    100          
22             name => $name,
23             attrs => \@attrs,
24             org => $org,
25             skipped => $skipped_text,
26             prepend => '',
27             append => '',
28             is_dirty => 0,
29             is_removed => 0,
30             is_end => (substr($org, 0, 2) eq '
31             is_empty => (substr($org, -2, 2) eq '/>' ? 1 : 0),
32             );
33             }
34              
35 80     80 1 389 sub name { shift->{name} }
36              
37             sub as_string {
38 91     91 1 188 my $self = shift;
39              
40 91 50       198 return unless $self->{name};
41              
42 91         164 my $out = '';
43 91 100 66     440 if (defined $self->{skipped} and length $self->{skipped}) {
44 58         104 $out .= $self->{skipped};
45             }
46 91 100 66     392 if (defined $self->{prepend} and length $self->{prepend}) {
47 3         8 $out .= $self->{prepend};
48             }
49 91 100       191 unless ($self->{is_dirty}) {
50 58         91 $out .= $self->{org};
51             }
52             else {
53 33 100       82 unless ($self->{is_removed}) {
54 19         33 $out .= '<';
55 19 50       51 $out .= '/' if $self->{is_end};
56 19         30 $out .= $self->{name};
57 19         22 my @attrs = @{ $self->{attrs} };
  19         56  
58 19         71 while (my ($key, $value) = splice @attrs, 0, 2) {
59 27 50       131 $out .= " $key=$value" unless $key eq '/';
60             }
61 19 100 100     80 $out .= ' /' if $self->{is_empty} && substr($self->{name}, -1, 1) ne '/';
62 19         33 $out .= '>';
63             }
64             }
65 91 100 66     393 if (defined $self->{append} and length $self->{append}) {
66 2         6 $out .= $self->{append};
67             }
68 91         422 return $out;
69             }
70              
71             sub remove_tag {
72 1     1 1 6 my $self = shift;
73              
74 1         3 $self->{is_removed} = 1;
75 1         3 $self->{is_dirty} = 1;
76             }
77              
78             sub remove_text_and_tag {
79 11     11 1 40 my $self = shift;
80              
81 11         18 $self->{is_removed} = 1;
82 11         21 $self->{skipped} = '';
83 11         50 $self->{is_dirty} = 1;
84             }
85              
86             sub remove_text {
87 1     1 1 5 my $self = shift;
88              
89 1         3 $self->{skipped} = '';
90             }
91              
92             sub remove_attr {
93 7     7 1 45 my ($self, $cond) = @_;
94              
95 7 50       26 return unless $self->{attrs};
96 7 50 33     44 return unless defined $cond and length $cond;
97              
98 7 100       76 $cond = qr/^$cond$/i unless ref $cond;
99              
100 7         10 my $offset = scalar @{ $self->{attrs} };
  7         16  
101 7         22 while ($offset > 0) {
102 11 100       67 if ($self->{attrs}->[$offset - 2] =~ /$cond/) {
103 5         7 splice @{ $self->{attrs} }, $offset - 2, 2;
  5         15  
104             }
105 11         27 $offset -= 2;
106             }
107 7         30 $self->{is_dirty} = 1;
108             }
109              
110             sub replace_attr {
111 5     5 1 33 my ($self, $cond, $code_or_value) = @_;
112              
113 5 50       18 return unless $self->{attrs};
114 5 50 33     30 return unless defined $cond and length $cond;
115              
116 5 100       97 $cond = qr/^$cond$/i unless ref $cond;
117              
118 5         7 my $offset = scalar @{ $self->{attrs} };
  5         14  
119 5         15 while ($offset > 0) {
120 8 100       52 if ($self->{attrs}->[$offset - 2] =~ /$cond/) {
121 5         20 my ($value, $quote) = $self->_remove_quote($self->{attrs}->[$offset - 1]);
122              
123 5 100       18 if (ref $code_or_value eq 'CODE') {
124 1         2 local $_ = $value;
125 1         3 $value = $code_or_value->($_);
126             }
127             else {
128 4         7 $value = $code_or_value;
129             }
130 5 50       20 $value = '' unless defined $value;
131 5         21 $value = encode_entities($value, q/<>&'"/);
132 5 50       616 if ($quote) {
133 5         12 $value = "$quote$value$quote";
134             }
135 5         15 $self->{attrs}->[$offset - 1] = $value;
136             }
137 8         20 $offset -= 2;
138             }
139 5         24 $self->{is_dirty} = 1;
140             }
141              
142             sub replace_tag {
143 2     2 1 8 my ($self, $new_name) = @_;
144              
145 2         8 $self->{name} = $new_name;
146 2         8 $self->{is_dirty} = 1;
147             }
148              
149             sub _remove_quote {
150 12     12   22 my ($self, $value) = @_;
151 12         23 my $open = substr($value, 0, 1);
152 12         15 my $close = substr($value, -1, 1);
153 12         12 my $quote;
154 12 50 33     84 if ($open eq $close and ($open eq q/'/ or $open eq q/"/)) {
      66        
155 10         16 $quote = $open;
156 10         19 $value = substr($value, 1, length($value) - 2);
157             }
158 12         61 $value = decode_entities($value);
159 12 100       54 return wantarray ? ($value, $quote) : $value;
160             }
161              
162             sub add_attr {
163 2     2 1 11 my ($self, $name, $value) = @_;
164 2         4 $value = $self->_remove_quote($value);
165 2         8 $value = encode_entities($value, q/<>&"'/);
166 2   50     234 my $offset = scalar @{ $self->{attrs} ||= [] };
  2         8  
167 2         3 my $replaced;
168 2         32 while ($offset > 0) {
169 5 100       15 if ($self->{attrs}->[$offset - 2] eq $name) {
170 1         4 $self->{attrs}->[$offset - 1] = qq/"$value"/;
171 1         2 $replaced = 1;
172 1         1 last;
173             }
174 4         9 $offset -= 2;
175             }
176 2 100 50     6 push @{ $self->{attrs} ||= [] }, $name, qq/"$value"/ unless $replaced;
  1         6  
177 2         7 $self->{is_dirty} = 1;
178             }
179              
180             sub attr {
181 6     6 1 40 my ($self, $name) = @_;
182              
183 6 50       21 return unless $self->{attrs};
184              
185 6         11 $name = lc $name;
186 6         7 my $offset = scalar @{ $self->{attrs} };
  6         15  
187 6         19 while ($offset > 0) {
188 5 50       22 if (lc $self->{attrs}->[$offset - 2] eq $name) {
189 5         17 my $value = $self->_remove_quote($self->{attrs}->[$offset - 1]);
190 5         36 return decode_entities($value);
191             }
192 0         0 $offset -= 2;
193             }
194 1         7 return;
195             }
196              
197             sub prepend {
198 2     2 1 15 my ($self, $html) = @_;
199              
200 2         6 $self->{prepend} = $html;
201             }
202              
203             sub append {
204 1     1 1 9 my ($self, $html) = @_;
205              
206 1         4 $self->{append} = $html;
207             }
208              
209             sub text {
210 3     3 1 19 my $self = shift;
211 3 50       8 if (@_) {
212 0         0 $self->{skipped} = shift;
213             }
214 3         23 $self->{skipped};
215             }
216              
217             1;
218              
219             __END__