File Coverage

blib/lib/HTML/FormatText/WithLinks.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package HTML::FormatText::WithLinks;
2              
3 18     18   659652 use strict;
  18         52  
  18         790  
4 18     18   18141 use URI::WithBase;
  18         250162  
  18         701  
5 18     18   33148 use HTML::TreeBuilder;
  0            
  0            
6             use base qw(HTML::FormatText);
7             use vars qw($VERSION);
8              
9             $VERSION = '0.14';
10              
11             sub new {
12              
13             my $proto = shift;
14             my $class = ref( $proto ) || $proto;
15             my $self = $class->SUPER::new( @_ );
16             $self->configure() unless @_;
17              
18             bless ( $self, $class );
19             return $self;
20              
21             }
22              
23             sub configure {
24              
25             my ($self, $hash) = @_;
26              
27             # a base uri so we can resolve relative uris
28             $self->{base} = $hash->{base};
29             delete $hash->{base};
30             $self->{base} =~ s#(.*?)/[^/]*$#$1/# if $self->{base};
31              
32             $self->{doc_overrides_base} = $hash->{doc_overrides_base};
33             delete $hash->{doc_overrides_base};
34              
35             $self->{before_link} = '[%n]';
36             $self->{after_link} = '';
37             $self->{footnote} = '%n. %l';
38             $self->{link_num_generator} = sub { return shift() + 1 };
39              
40             $self->{unique_links} = 0;
41              
42             $self->{anchor_links} = 1;
43              
44             $self->{skip_linked_urls} = 0;
45              
46             $self->{_link_track} = {};
47              
48             $self->{bold_marker} = '_';
49             $self->{italic_marker} = '/';
50              
51             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             $self->{ $_ } = $hash->{ $_ } if exists $hash->{ $_ };
55             delete $hash->{ $_ };
56             }
57              
58             $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             my $self = shift;
67             $self->goto_lm unless defined $self->{cur_pos};
68             $self->SUPER::textflow(@_);
69             }
70              
71             sub head_start {
72             my ($self) = @_;
73             $self->SUPER::head_start();
74              
75             # we don't care about what the documens says it's base is
76             if ( $self->{base} and not $self->{doc_overrides_base} ) {
77             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             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             my ($self, $node) = @_;
90             if (my $href = $node->attr('href')) {
91             $self->{base} = $href;
92             }
93              
94             # allow for no superclass base_start() in HTML::FormatText 2.04
95             if (! HTML::FormatText->can('base_start')) {
96             return 0;
97             }
98              
99             # chain up if it exists in the future
100             return $self->SUPER::base_start();
101             }
102              
103             sub a_start {
104              
105             my $self = shift;
106             my $node = shift;
107             # local urls are no use so we have to make them absolute
108             my $href = $node->attr('href') || '';
109             if ($href && $self->{anchor_links} == 0 && $href =~ m/^#/o) {
110             $href = '';
111             }
112             elsif ($href and $self->{skip_linked_urls} and $href eq $node->as_text) {
113             $href = '';
114             }
115             if ( $href ) {
116             if ($href !~ m#^https?:|^mailto:#o) {
117             $href = URI::WithBase->new($href, $self->{base})->abs();
118             }
119             if ($self->{unique_links})
120             {
121             if (defined $self->{_link_track}->{$href})
122             {
123             $self->out( $self->text('before_link', $self->{_link_track}->{$href}, $href ) );
124             } else {
125             push @{$self->{_links}}, $href;
126             $self->{_link_track}->{$href} = $#{$self->{_links}};
127             $self->out( $self->text('before_link', $#{$self->{_links}}, $href ) );
128             }
129             } else {
130             push @{$self->{_links}}, $href;
131             $self->out( $self->text('before_link') );
132             }
133             }
134             $self->SUPER::a_start();
135              
136             }
137              
138             sub a_end {
139              
140             my $self = shift;
141             my $node = shift;
142             my $text;
143             unless ($self->{skip_linked_urls} and $node->attr('href') eq $node->as_text) {
144             if ($self->{unique_links})
145             {
146             my $href = $node->attr('href');
147             $text = $self->text('after_link', $self->{_link_track}->{$href}, $href);
148             } else {
149             $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             if ($text ne '') {
155             $self->out( $text );
156             }
157             }
158             $self->SUPER::a_end();
159              
160             }
161              
162             sub b_start {
163             my $self = shift;
164             $self->out( $self->{'bold_marker'} ) if $self->{ with_emphasis };
165             $self->SUPER::b_start();
166             }
167              
168             sub b_end {
169             my $self = shift;
170             $self->out( $self->{'bold_marker'} ) if $self->{ with_emphasis };
171             $self->SUPER::b_end();
172             }
173              
174             sub i_start {
175             my $self = shift;
176             $self->out( $self->{'italic_marker'} ) if $self->{ with_emphasis };
177             $self->SUPER::i_start();
178             }
179              
180             sub i_end {
181             my $self = shift;
182             $self->out( $self->{'italic_marker'} ) if $self->{ with_emphasis };
183             $self->SUPER::i_end();
184             }
185              
186             # print out our links
187             sub html_end {
188              
189             my $self = shift;
190             if ( $self->{_links} and @{$self->{_links}} and $self->{footnote} ) {
191             $self->nl; $self->nl; # be tidy
192             $self->goto_lm;
193             for (0 .. $#{$self->{_links}}) {
194             $self->goto_lm;
195             $self->out(
196             $self->text( 'footnote', $_, $self->{_links}->[$_] )
197             );
198             $self->nl;
199             }
200             }
201             $self->SUPER::end();
202              
203             }
204              
205             sub _link_num {
206              
207             my ($self, $num) = @_;
208             $num = $#{$self->{_links}} unless defined $num;
209             return &{$self->{link_num_generator}}($num);
210              
211             }
212              
213             sub text {
214              
215             my ($self, $type, $num, $href) = @_;
216              
217             if ($self->{_links} and @{$self->{_links}}) {
218             $href = $self->{_links}->[$#{$self->{_links}}]
219             unless (defined $num and defined $href);
220             }
221             $num = $self->_link_num($num);
222             my $text = $self->{$type};
223             $text =~ s/%n/$num/g;
224             $text =~ s/%l/$href/g;
225             return $text;
226             }
227              
228             sub parse {
229              
230             my $self = shift;
231             my $text = shift;
232              
233             return undef unless defined $text;
234             return '' if $text eq '';
235              
236             my $tree = HTML::TreeBuilder->new->parse( $text );
237              
238             return $self->_parse( $tree );
239             }
240              
241             sub parse_file {
242              
243             my $self = shift;
244             my $file = shift;
245              
246             unless (-e $file and -f $file) {
247             $self->error("$file not found or not a regular file");
248             return undef;
249             }
250              
251             my $tree = HTML::TreeBuilder->new->parse_file( $file );
252            
253             return $self->_parse( $tree );
254             }
255              
256             sub _parse {
257              
258             my $self = shift;
259             my $tree = shift;
260              
261             $self->{_link_track} = {};
262             $self->{_links} = [];
263              
264             unless ( $tree ) {
265             $self->error( "HTML::TreeBuilder problem" . ( $! ? ": $!" : '' ) );
266             return undef;
267             }
268             $tree->eof();
269              
270             my $return_text = $self->format( $tree );
271              
272             $tree->delete;
273              
274             return $return_text;
275             }
276            
277              
278             sub error {
279             my $self = shift;
280             if (@_) {
281             $self->{error} = shift;
282             }
283             return $self->{error};
284             }
285              
286             1;
287              
288             __END__