File Coverage

blib/lib/HTML/FormatText/WithLinks.pm
Criterion Covered Total %
statement 153 156 98.0
branch 54 60 90.0
condition 33 38 86.8
subroutine 24 24 100.0
pod 4 16 25.0
total 268 294 91.1


line stmt bran cond sub pod time code
1             package HTML::FormatText::WithLinks;
2              
3 17     17   259294 use strict;
  17         36  
  17         664  
4 17     17   7797 use URI::WithBase;
  17         112161  
  17         449  
5 17     17   12357 use HTML::TreeBuilder;
  17         443476  
  17         166  
6 17     17   653 use base qw(HTML::FormatText);
  17         24  
  17         10261  
7 17     17   88930 use vars qw($VERSION);
  17         30  
  17         21144  
8              
9             $VERSION = '0.15';
10              
11             sub new {
12              
13 33     33 1 13270 my $proto = shift;
14 33   66     191 my $class = ref( $proto ) || $proto;
15 33         277 my $self = $class->SUPER::new( @_ );
16 33 100       745 $self->configure() unless @_;
17              
18 33         149 bless ( $self, $class );
19 33         76 return $self;
20              
21             }
22              
23             sub configure {
24              
25 33     33 0 665 my ($self, $hash) = @_;
26              
27             # a base uri so we can resolve relative uris
28 33         175 $self->{base} = $hash->{base};
29 33         68 delete $hash->{base};
30 33 100       146 $self->{base} =~ s#(.*?)/[^/]*$#$1/# if $self->{base};
31              
32 33         71 $self->{doc_overrides_base} = $hash->{doc_overrides_base};
33 33         77 delete $hash->{doc_overrides_base};
34              
35 33         68 $self->{before_link} = '[%n]';
36 33         59 $self->{after_link} = '';
37 33         61 $self->{footnote} = '%n. %l';
38 33     74   159 $self->{link_num_generator} = sub { return shift() + 1 };
  74         120  
39              
40 33         64 $self->{unique_links} = 0;
41              
42 33         57 $self->{anchor_links} = 1;
43              
44 33         59 $self->{skip_linked_urls} = 0;
45              
46 33         62 $self->{_link_track} = {};
47              
48 33         52 $self->{bold_marker} = '_';
49 33         59 $self->{italic_marker} = '/';
50              
51 33         80 foreach ( qw( before_link after_link footnote link_num_generator
52             with_emphasis bold_marker italic_marker
53             unique_links anchor_links skip_linked_urls ) ) {
54 330 100       540 $self->{ $_ } = $hash->{ $_ } if exists $hash->{ $_ };
55 330         345 delete $hash->{ $_ };
56             }
57              
58 33         191 $self->SUPER::configure($hash);
59              
60             }
61              
62             # we need to do this as if you pass an HTML fragment without any
63             # containing block level markup (e.g. a p tag) then no indentation
64             # takes place so if we've not got a cur_pos we indent.
65             sub textflow {
66 120     120 0 5062 my $self = shift;
67 120 50       419 $self->goto_lm unless defined $self->{cur_pos};
68 120         755 $self->SUPER::textflow(@_);
69             }
70              
71             sub head_start {
72 29     29 0 7937 my ($self) = @_;
73 29         217 $self->SUPER::head_start();
74              
75             # we don't care about what the documents says it's base is
76 29 100 100     421 if ( $self->{base} and not $self->{doc_overrides_base} ) {
77 4         12 return 0;
78             }
79              
80             # descend into for possible there, even if superclass not
81             # interested (as of HTML::FormatText 2.04 it's not)
82 25         98 return 1;
83             }
84              
85             # is supposed to be inside , but no need to demand that.
86             # "lynx -source" sticks a at the very start of the document, before
87             # even , so accepting anywhere lets that work.
88             sub base_start {
89 2     2 0 42 my ($self, $node) = @_;
90 2 50       6 if (my $href = $node->attr('href')) {
91 2         21 $self->{base} = $href;
92             }
93              
94             # allow for no superclass base_start() in HTML::FormatText 2.04
95 2 50       18 if (! HTML::FormatText->can('base_start')) {
96 2         5 return 0;
97             }
98              
99             # chain up if it exists in the future
100 0         0 return $self->SUPER::base_start();
101             }
102              
103             sub a_start {
104              
105 34     34 0 6995 my $self = shift;
106 34         52 my $node = shift;
107             # local urls are no use so we have to make them absolute
108 34   100     88 my $href = $node->attr('href') || '';
109 34 100 100     677 if ($href && $self->{anchor_links} == 0 && $href =~ m/^#/o) {
    100 66        
      100        
      100        
110 1         3 $href = '';
111             }
112             elsif ($href and $self->{skip_linked_urls} and $href eq $node->as_text) {
113 3         55 $href = '';
114             }
115 34 100       151 if ( $href ) {
116 26 100       125 if ($href !~ m#^https?:|^mailto:#o) {
117 5         31 $href = URI::WithBase->new($href, $self->{base})->abs();
118             }
119 26 100       19077 if ($self->{unique_links})
120             {
121 8 100       15 if (defined $self->{_link_track}->{$href})
122             {
123 3         8 $self->out( $self->text('before_link', $self->{_link_track}->{$href}, $href ) );
124             } else {
125 5         6 push @{$self->{_links}}, $href;
  5         9  
126 5         6 $self->{_link_track}->{$href} = $#{$self->{_links}};
  5         18  
127 5         6 $self->out( $self->text('before_link', $#{$self->{_links}}, $href ) );
  5         15  
128             }
129             } else {
130 18         29 push @{$self->{_links}}, $href;
  18         48  
131 18         59 $self->out( $self->text('before_link') );
132             }
133             }
134 34         562 $self->SUPER::a_start();
135              
136             }
137              
138             sub a_end {
139              
140 34     34 0 1753 my $self = shift;
141 34         50 my $node = shift;
142 34         36 my $text;
143 34 100 100     145 unless ($self->{skip_linked_urls} and $node->attr('href') eq $node->as_text) {
144 31 100       115 if ($self->{unique_links})
145             {
146 8         15 my $href = $node->attr('href');
147 8         66 $text = $self->text('after_link', $self->{_link_track}->{$href}, $href);
148             } else {
149 23         61 $text = $self->text('after_link');
150             }
151             # If we're just dealing with a fragment of HTML, with a link at the
152             # end, we get a space before the first footnote link if we do
153             # $self->out( '' )
154 31 100       90 if ($text ne '') {
155 6         14 $self->out( $text );
156             }
157             }
158 34         277 $self->SUPER::a_end();
159              
160             }
161              
162             sub b_start {
163 6     6 0 847 my $self = shift;
164 6 100       20 $self->out( $self->{'bold_marker'} ) if $self->{ with_emphasis };
165 6         73 $self->SUPER::b_start();
166             }
167              
168             sub b_end {
169 6     6 0 236 my $self = shift;
170 6 100       18 $self->out( $self->{'bold_marker'} ) if $self->{ with_emphasis };
171 6         50 $self->SUPER::b_end();
172             }
173              
174             sub i_start {
175 6     6 0 315 my $self = shift;
176 6 100       20 $self->out( $self->{'italic_marker'} ) if $self->{ with_emphasis };
177 6         78 $self->SUPER::i_start();
178             }
179              
180             sub i_end {
181 6     6 0 231 my $self = shift;
182 6 100       16 $self->out( $self->{'italic_marker'} ) if $self->{ with_emphasis };
183 6         55 $self->SUPER::i_end();
184             }
185              
186             # print out our links
187             sub html_end {
188              
189 29     29 0 3357 my $self = shift;
190 29 100 66     125 if ( $self->{_links} and @{$self->{_links}} and $self->{footnote} ) {
  29   100     215  
191 16         78 $self->nl; $self->nl; # be tidy
  16         133  
192 16         112 $self->goto_lm;
193 16         71 for (0 .. $#{$self->{_links}}) {
  16         52  
194 20         62 $self->goto_lm;
195 20         100 $self->out(
196             $self->text( 'footnote', $_, $self->{_links}->[$_] )
197             );
198 20         585 $self->nl;
199             }
200             }
201 29         226 $self->SUPER::end();
202              
203             }
204              
205             sub _link_num {
206              
207 77     77   85 my ($self, $num) = @_;
208 77 100       158 $num = $#{$self->{_links}} unless defined $num;
  41         61  
209 77         74 return &{$self->{link_num_generator}}($num);
  77         193  
210              
211             }
212              
213             sub text {
214              
215 77     77 0 111 my ($self, $type, $num, $href) = @_;
216              
217 77 100 66     215 if ($self->{_links} and @{$self->{_links}}) {
  77         234  
218 72 100 66     230 $href = $self->{_links}->[$#{$self->{_links}}]
  36         69  
219             unless (defined $num and defined $href);
220             }
221 77         142 $num = $self->_link_num($num);
222 77         121 my $text = $self->{$type};
223 77         166 $text =~ s/%n/$num/g;
224 77         117 $text =~ s/%l/$href/g;
225 77         243 return $text;
226             }
227              
228             sub parse {
229              
230 30     30 1 12403 my $self = shift;
231 30         50 my $text = shift;
232              
233 30 100       131 return undef unless defined $text;
234 29 100       89 return '' if $text eq '';
235              
236 28         190 my $tree = HTML::TreeBuilder->new->parse( $text );
237              
238 28         34947 return $self->_parse( $tree );
239             }
240              
241             sub parse_file {
242              
243 3     3 1 1147 my $self = shift;
244 3         5 my $file = shift;
245              
246 3 100 100     61 unless (-e $file and -f $file) {
247 2         13 $self->error("$file not found or not a regular file");
248 2         6 return undef;
249             }
250              
251 1         11 my $tree = HTML::TreeBuilder->new->parse_file( $file );
252            
253 1         1653 return $self->_parse( $tree );
254             }
255              
256             sub _parse {
257              
258 29     29   58 my $self = shift;
259 29         41 my $tree = shift;
260              
261 29         66 $self->{_link_track} = {};
262 29         92 $self->{_links} = [];
263              
264 29 50       101 unless ( $tree ) {
265 0 0       0 $self->error( "HTML::TreeBuilder problem" . ( $! ? ": $!" : '' ) );
266 0         0 return undef;
267             }
268 29         103 $tree->eof();
269              
270 29         4347 my $return_text = $self->format( $tree );
271              
272 29         987 $tree->delete;
273              
274 29         2626 return $return_text;
275             }
276            
277              
278             sub error {
279 4     4 1 421 my $self = shift;
280 4 100       10 if (@_) {
281 2         3 $self->{error} = shift;
282             }
283 4         10 return $self->{error};
284             }
285              
286             1;
287              
288             __END__