File Coverage

lib/Pod/Xhtml.pm
Criterion Covered Total %
statement 416 452 92.0
branch 170 216 78.7
condition 50 71 70.4
subroutine 38 43 88.3
pod 7 23 30.4
total 681 805 84.6


line stmt bran cond sub pod time code
1             # $Id: Xhtml.pm,v 1.61 2010/07/29 16:17:53 jamiel Exp $
2             package Pod::Xhtml;
3              
4 3     3   15401 use strict;
  3         6  
  3         101  
5 3     3   16 use Pod::Parser;
  3         6  
  3         269  
6 3     3   1215 use Pod::ParseUtils;
  3         3828  
  3         81  
7 3     3   19 use Carp;
  3         10  
  3         212  
8 3     3   15 use vars qw/@ISA %COMMANDS %SEQ $VERSION $FirstAnchorId $ContentSuffix/;
  3         5  
  3         307  
9 3     3   27 use constant P2X_REGION => qr/(?:pod2)?xhtml/;
  3         3  
  3         20062  
10              
11             $FirstAnchorId = "TOP";
12             $ContentSuffix = "-CONTENT";
13              
14             @ISA = qw(Pod::Parser);
15             ($VERSION) = ('$Revision: 1.61 $' =~ m/([\d\.]+)/);
16              
17             # recognized commands
18             %COMMANDS = map { $_ => 1 } qw(pod head1 head2 head3 head4 item over back for begin end);
19              
20             # recognized special sequences
21             %SEQ = (
22             B => \&seqB,
23             C => \&seqC,
24             E => \&seqE,
25             F => \&seqF,
26             I => \&seqI,
27             L => \&seqL,
28             S => \&seqS,
29             X => \&seqX,
30             Z => \&seqZ,
31             );
32              
33             ########## New PUBLIC methods for this class
34 1     1 1 6 sub asString { my $self = shift; return $self->{buffer}; }
  1         7  
35 0     0 1 0 sub asStringRef { my $self = shift; return \$self->{buffer}; }
  0         0  
36 0     0 1 0 sub addHeadText { my $self = shift; $self->{HeadText} .= shift; }
  0         0  
37 0     0 1 0 sub addBodyOpenText { my $self = shift; $self->{BodyOpenText} .= shift; }
  0         0  
38 0     0 1 0 sub addBodyCloseText { my $self = shift; $self->{BodyCloseText} .= shift; }
  0         0  
39              
40             ########## Override methods in Pod::Parser
41             ########## PUBLIC INTERFACE
42             sub parse_from_file {
43 5     5 1 2206 my $self = shift;
44 5         17 $self->resetMe;
45 5         864 $self->SUPER::parse_from_file(@_);
46             }
47              
48             sub parse_from_filehandle {
49 7     7 1 551 my $self = shift;
50 7         21 $self->resetMe;
51 7         1154 $self->SUPER::parse_from_filehandle(@_);
52             }
53              
54             ########## INTERNALS
55             sub initialize {
56 7     7 0 80910 my $self = shift;
57              
58 7 50       73 $self->{TopLinks} = qq(

Top

) unless defined $self->{TopLinks};
59 7 100       34 $self->{MakeIndex} = 1 unless defined $self->{MakeIndex};
60 7 50       31 $self->{MakeMeta} = 1 unless defined $self->{MakeMeta};
61 7 50       26 $self->{FragmentOnly} = 0 unless defined $self->{FragmentOnly};
62 7         36 $self->{HeadText} = $self->{BodyOpenText} = $self->{BodyCloseText} = '';
63 7   33     24 $self->{LinkParser} ||= new Pod::Hyperlink;
64 7   50     47 $self->{TopHeading} ||= 1;
65 7         19 $self->{TopHeading} = int $self->{TopHeading}; # heading level must be an integer
66 7 50       23 croak "TopHeading must be greater than zero" if $self->{TopHeading} < 1; # (prevent negative heading levels)
67 7         75 $self->SUPER::initialize();
68             }
69              
70             sub command {
71 122     122 0 199 my ($parser, $command, $paragraph, $line_num, $pod_para) = @_;
72 122         4378 my $ptree = $parser->parse_text( $paragraph, $line_num );
73 122         472 $pod_para->parse_tree( $ptree );
74 122         215 $parser->parse_tree->append( $pod_para );
75             }
76              
77             sub verbatim {
78 8     8 0 14 my ($parser, $paragraph, $line_num, $pod_para) = @_;
79 8         14 $parser->parse_tree->append( $pod_para );
80             }
81              
82             sub textblock {
83 91     91 0 134 my ($parser, $paragraph, $line_num, $pod_para) = @_;
84 91         6931 my $ptree = $parser->parse_text( $paragraph, $line_num );
85 91         318 $pod_para->parse_tree( $ptree );
86 91         164 $parser->parse_tree->append( $pod_para );
87             }
88              
89             sub end_pod {
90 7     7 0 18 my $self = shift;
91 7         18 my $ptree = $self->parse_tree;
92              
93             # clean up tree ready for parse
94 7         24 foreach my $para (@$ptree) {
95 221 100       408 if ($para->{'-prefix'} eq '=') {
  99 100       183  
96 122         254 $para->{'TYPE'} = 'COMMAND';
97             } elsif (! @{$para->{'-ptree'}}) {
98 8         17 $para->{'-ptree'}->[0] = $para->{'-text'};
99 8         14 $para->{'TYPE'} = 'VERBATIM';
100             } else {
101 91         184 $para->{'TYPE'} = 'TEXT';
102             }
103 221         299 foreach (@{$para->{'-ptree'}}) {
  221         367  
104 308 100       511 unless (ref $_) { s/\n\s+$//; }
  248         840  
105             }
106             }
107              
108             # now loop over each para and expand any html escapes or sequences
109 7         39 $self->_paraExpand( $_ ) foreach (@$ptree);
110              
111 7         57 $self->{buffer} =~ s/(\n?)<\/pre>\s*
/$1/sg; # concatenate 'pre' blocks 
112 7         53 1 while $self->{buffer} =~ s/
(\s+)<\/pre>/$1/sg; 
113 7 50       36 $self->{buffer} = $self->_makeIndex . $self->{buffer} if $self->{MakeIndex};
114 7         122 $self->{buffer} =~ s/<<>>/$1/ge;
  29         121  
115             $self->{buffer} = join "\n", qq[
], $self->{buffer},
116 7   50     21 ( @{ $self->{sections} } > 1 && "" ), "";
117              
118             # Expand internal L<> links to the correct sections
119 7         62 $self->{buffer} =~ s/#<<<(.*?)>>>/'#' . $self->_findSection($1)/eg;
  11         30  
120 7 50       54 die "gotcha" if $self->{buffer} =~ /#<<
121              
122 7         22 my $headblock = sprintf "%s\n%s\n%s\n\t%s\n",
123             qq(),
124             qq(),
125             qq(\n),
126             _htmlEscape( $self->{doctitle} );
127 7 50       41 $headblock .= $self->_makeMeta if $self->{MakeMeta};
128              
129 7 50       35 unless ($self->{FragmentOnly}) {
130 7         192 $self->{buffer} = $headblock . $self->{HeadText} . "\n\n" . $self->{BodyOpenText} . $self->{buffer};
131 7         27 $self->{buffer} .= $self->{BodyCloseText} . "\n\n";
132             }
133              
134             # in stringmode we only accumulate the XHTML else we print it to the
135             # filehandle
136 7 100       92 unless ($self->{StringMode}) {
137 6         41 my $out_fh = $self->output_handle;
138 6         706 print $out_fh $self->{buffer};
139             }
140             }
141              
142             ########## Everything else is PRIVATE
143             sub resetMe {
144 12     12 0 17 my $self = shift;
145 12         132 $self->{'-ptree'} = new Pod::ParseTree;
146 12         63 $self->{'sections'} = [];
147 12         48 $self->{'listKind'} = [];
148 12         27 $self->{'listHasItems'} = [];
149 12         21 $self->{'dataSections'} = [];
150 12         21 $self->{'section_names'} = {};
151 12         41 $self->{'section_ids'} = {};
152 12         22 $self->{'tagStack'} = [];
153              
154 12         27 foreach (qw(inList titleflag )) { $self->{$_} = 0; }
  24         54  
155 12         113 foreach (qw(buffer doctitle)) { $self->{$_} = ''; }
  24         49  
156            
157             # add the "$FirstAnchor" section into the sections
158 12         38 $self->_addSection ( '', $FirstAnchorId );
159             }
160              
161 228     228 0 10935 sub parse_tree { return $_[0]->{'-ptree'}; }
162              
163             sub _paraExpand {
164 221     221   241 my $self = shift;
165 221         211 my $para = shift;
166              
167             # skip data region unless its ident matches P2X_REGION (eg xhtml)
168 221         178 my $in_dsection = !!(@{$self->{dataSections}});
  221         449  
169 221   100     405 my $p2x_region = $in_dsection && $self->{dataSections}->[-1] =~ P2X_REGION;
170 221   100     357 my $skip_region = $in_dsection && !$p2x_region;
171              
172             # collapse interior sequences and strings
173             # escape html unless it's a html data region
174 221         236 foreach ( @{$para->{'-ptree'}} ) {
  221         418  
175 308 100       843 $_ = (ref $_) ? $self->_handleSequence($_, $p2x_region) :
    100          
176             $p2x_region ? $_ : _htmlEscape($_);
177             }
178             # the parse tree has now been collapsed into a list of strings
179 221         279 my $string = join('', @{$para->{'-ptree'}});
  221         451  
180              
181 221 100       642 if ($para->{TYPE} eq 'TEXT') {
    100          
    50          
182 91 100       155 return if $skip_region;
183 90         191 $self->_addTextblock($string, $p2x_region);
184             } elsif ($para->{TYPE} eq 'VERBATIM') {
185 8 50       19 return if $skip_region;
186 8         15 my $paragraph = "
$string\n\n
";
187 8         16 $self->_addTextblock( $paragraph, 1 ); # no wrap
188 8 50       35 if ($self->{titleflag} != 0) {
189 0         0 $self->_setTitle( $paragraph );
190 0         0 warn "NAME followed by verbatim paragraph";
191             }
192             } elsif ($para->{TYPE} eq 'COMMAND') {
193 122         348 $self->_addCommand($para->{'-name'}, $string, $para->{'-text'}, $para->{'-line'} )
194             } else {
195 0         0 warn "Unrecognized paragraph type $para->{TYPE} found at $self->{_INFILE} line $para->{'-line'}\n";
196             }
197             }
198              
199             sub _addCommand {
200 122     122   134 my $self = shift;
201 122         207 my ($command, $paragraph, $raw_para, $line) = @_;
202 122         104 my $anchor;
203              
204 122 50       268 unless (exists $COMMANDS{$command}) {
205 0         0 warn "Unrecognized command '$command' skipped at $self->{_INFILE} line $line\n";
206 0         0 return;
207             }
208              
209 122         159 for ($command) {
210 122         107 my $data_para = @{$self->{dataSections}}; # inside a data paragraph?
  122         191  
211 122 100 66     383 /^head1/ && !$data_para && do {
212 29         63 my $top_heading = 'h'. $self->{TopHeading};
213 29 50       78 $top_heading = 'h1' if !$self->{FragmentOnly}; # ignore TopHeading when not in fragment mode
214              
215             # if ANY sections are open then close the previously opened div
216 29 100       33 $self->{buffer} .= "\n\n" unless ( @{ $self->{sections} } == 1 );
  29         88  
217            
218 29         67 $anchor = $self->_addSection( 'head1', $paragraph );
219 29         85 my $anchorContent = $self->_addSection( '', $paragraph . $ContentSuffix);
220            
221 29 50       173 $self->{buffer} .= qq(<$top_heading id="$anchor">$paragraph)
222             .($self->{TopLinks} ? $self->{TopLinks} : '')."\n"
223             ."
\n";
224              
225 29 100       64 if ($anchor eq 'NAME') { $self->{titleflag} = 1; }
  5         7  
226 29         124 last;
227             };
228 93 100 66     273 /^head([234])/ && !$data_para && do {
229 24         42 my $head_level = $1;
230 24 50       56 if($self->{FragmentOnly}){
231 0         0 $head_level += ($self->{TopHeading} - 1);
232 0 0       0 $head_level = 6 if $head_level > 6;
233             }
234             # if ANY sections are open then close the previously opened div
235 24 50       23 $self->{buffer} .= "\n\n" unless ( @{ $self->{sections} } == 1 );
  24         70  
236              
237 24         68 $anchor = $self->_addSection( "head${head_level}", $paragraph );
238 24         72 my $anchorContent = $self->_addSection( '', $paragraph . $ContentSuffix);
239              
240 24         148 $self->{buffer} .= "$paragraph\n" . "
\n";
241 24         95 last;
242             };
243 69 100 66     260 /^item/ && !$data_para && do {
244 36 50       98 unless ($self->{inList}) {
245 0         0 warn "Not in list at $self->{_INFILE} line $line\n";
246 0         0 last;
247             }
248              
249 36         55 $self->{listHasItems}[-1]++;
250 36         46 $self->{listCurrentParas}[-1] = 0;
251              
252             # is this the first item in the list?
253 36 100 66     32 if (@{$self->{listKind}} && $self->{listKind}[-1] == 0) {
  36         155  
254 14         21 my $parent_list = $self->{listKind}[-2]; # this is a sub-list
255 14 100 100     76 if ($parent_list && $parent_list == 1) {
    100 66        
256             #
    sub lists must be in an
  • [BEGIN]
257 1         4 $self->{buffer} .= $self->_tagLevel () . "
  • \n";
  • 258 1         4 push @{$self->{tagStack}}, "li";
      1         3  
    259             } elsif ($parent_list && $parent_list == 2) {
    260             #
    sub lists must be in a

    [BEGIN]

    261 5         15 $self->{buffer} .= $self->_tagLevel () . "

    \n";

    262 5         9 push @{$self->{tagStack}}, "p";
      5         11  
    263             }
    264              
    265 14 100       28 if ($paragraph eq '*') {
    266 6         11 $self->{listKind}[-1] = 1;
    267 6         21 $self->{buffer} .= $self->_tagLevel () . "
      \n";
    268 6         12 push @{$self->{tagStack}}, "ul";
      6         16  
    269             } else {
    270 8         14 $self->{listKind}[-1] = 2;
    271 8         20 $self->{buffer} .= $self->_tagLevel () . "
    \n";
    272 8         10 push @{$self->{tagStack}}, "dl";
      8         20  
    273             }
    274             } else {
    275             # close last list item's tag#
    276 22 100       53 if ($self->{listKind}[-1] == 1) {
    277 10         12 my $o = pop @{$self->{tagStack}};
      10         19  
    278 10 50       25 warn "expected 'li' to be on the tag stack but got '$o'\n"
    279             if $o ne 'li';
    280 10         21 $self->{buffer} .= $self->_tagLevel () . "\n";
    281             }
    282             }
    283 36 100 66     43 if (@{$self->{listKind}} && $self->{listKind}[-1] == 2) {
      36         165  
    284 20 100 66     18 if (@{$self->{tagStack}} && $self->{tagStack}[-1] eq "dd") {
      20         91  
    285 12         13 my $o = pop @{$self->{tagStack}};
      12         20  
    286 12 50       22 warn "expected 'dd' to be on the tag stack but got '$o'\n"
    287             if $o ne 'dd';
    288 12         46 $self->{buffer} .= $self->_tagLevel () . "\n";
    289             }
    290 20         51 $self->{buffer} .= $self->_tagLevel () . qq(
    291 20         27 push @{$self->{tagStack}}, "dt";
      20         33  
    292 20 100       40 if ($self->{MakeIndex} >= 2) {
    293 12         25 $anchor = $self->_addSection( 'item', $paragraph );
    294 12         27 $self->{buffer} .= qq( id="$anchor");
    295             }
    296 20         26 $self->{buffer} .= ">";
    297 20         31 $self->{buffer} .= qq($paragraph\n);
    298 20         15 my $o = pop @{$self->{tagStack}};
      20         33  
    299 20 50       43 warn "expected 'dt' to be on the tag stack but got '$o'\n"
    300             if $o ne 'dt';
    301             }
    302 36         148 last;
    303             };
    304 33 100 66     120 /^over/ && !$data_para && do {
    305 14         25 $self->{inList}++;
    306 14         16 push @{$self->{listKind}}, 0;
      14         26  
    307 14         46 push @{$self->{listHasItems}}, 0;
      14         22  
    308 14 100       34 push @{$self->{sections}}, 'over' if $self->{MakeIndex} >= 2;
      8         15  
    309 14         14 push @{$self->{listCurrentParas}}, 0;
      14         36  
    310             };
    311 33 100 66     124 /^back/ && !$data_para && do {
    312 14         13 my $listItems = pop @{$self->{listHasItems}};
      14         30  
    313 14 50 66     41 if (--$self->{inList} < 0) {
        50          
        100          
    314 0         0 warn "=back commands don't balance =overs at $self->{_INFILE} line $line\n";
    315 0         0 last;
    316 14         71 } elsif ($listItems == 0) {
    317 0         0 warn "empty list at $self->{_INFILE} line $line\n";
    318 0         0 last;
    319             } elsif (@{$self->{listKind}} && $self->{listKind}[-1] == 1) {
    320 6         7 my $o = pop @{$self->{tagStack}};
      6         12  
    321 6 50       14 warn "expected 'li' to be on the tag stack but got '$o'\n"
    322             if $o ne 'li';
    323 6         6 $o = pop @{$self->{tagStack}};
      6         11  
    324 6 50       16 warn "expected 'ul' to be on the tag stack but got '$o'\n"
    325             if $o ne 'ul';
    326 6         12 $self->{buffer} .= "\n\n\n";
    327             } else {
    328 8   66     9 while (@{$self->{tagStack}} && $self->{tagStack}[-1] eq "dd") {
      16         77  
    329 8         10 pop @{$self->{tagStack}};
      8         13  
    330 8         18 $self->{buffer} .=$self->_tagLevel () . "\n";
    331             }
    332 8         10 my $o = pop @{$self->{tagStack}};
      8         15  
    333 8 50       17 warn "expected 'dl' to be on the tag stack but got '$o'\n"
    334             if $o ne 'dl';
    335 8         16 $self->{buffer} .= $self->_tagLevel () . "\n";
    336             }
    337              
    338 14         27 my $parent_list = $self->{listKind}[-2]; # this is a sub-list
    339 14 100 100     43 if ($parent_list && $parent_list == 1) {
    340 1         1 my $o = pop @{$self->{tagStack}};
      1         3  
    341 1 50       4 warn "expected 'li' to be on the tag stack but got '$o'\n"
    342             if $o ne 'li';
    343             #
      sub lists must be in an
    • [END]
    344 1         3 $self->{buffer} .= $self->_tagLevel () . "\n";
    345             }
    346 14 100 100     42 if ($parent_list && $parent_list == 2) {
    347 5         5 my $o = pop @{$self->{tagStack}};
      5         11  
    348 5 50       12 warn "expected 'p' to be on the tag stack but got '$o'\n"
    349             if $o ne 'p';
    350             #
    sub lists must be in a

    [END]

    351 5         16 $self->{buffer} .= $self->_tagLevel () . "

    \n";
    352             }
    353              
    354 14 100       33 if ( $self->{MakeIndex} >= 2 ) {
    355 8 100       44 if ( ! ref $self->{sections}->[ -1 ] ) {
    356 4 100       21 if ( $self->{sections}->[ -1 ] =~ /^over$/i ) {
    357 3         3 pop @{ $self->{sections} };
      3         6  
    358             }
    359             } else {
    360 4 50       19 if ( $self->{sections}->[ -1 ] [ 0 ] =~ /^item$/i ) {
    361 4         5 push @{ $self->{sections} }, 'back';
      4         10  
    362             }
    363             }
    364             }
    365              
    366 14         15 pop @{$self->{listKind}};
      14         18  
    367 14         17 pop @{$self->{listCurrentParas}};
      14         17  
    368 14         53 last;
    369             };
    370 19 100 66     49 /^for/ && !$data_para && do {
    371 1         4 my($ident, $html) = $raw_para =~ /^\s*(\S+)\s+(.*)/;
    372 1 50       6 $html = undef unless $ident =~ P2X_REGION;
    373 1 50       5 $self->{buffer} .= $html if $html;
    374             };
    375 19 100 66     48 /^begin/ && !$data_para && do {
    376 2         7 my ($ident) = $paragraph =~ /(\S+)/;
    377 2         3 push @{$self->{dataSections}}, $ident;
      2         4  
    378 2         8 last;
    379             };
    380 17 100       92 /^end/ && do {
    381 2         6 my ($ident) = $paragraph =~ /(\S+)/;
    382 2 50       3 unless (@{$self->{dataSections}}) {
      2         6  
    383 0         0 warn "no corresponding '=begin $ident' marker at $self->{_INFILE} line $line\n";
    384 0         0 last;
    385             }
    386 2         4 my $current_section = $self->{dataSections}[-1];
    387 2 50       6 unless ($current_section eq $ident) {
    388 0         0 warn "'=end $ident' doesn't match '=begin $current_section' at $self->{_INFILE} line $line\n";
    389 0         0 last;
    390             }
    391 2         3 pop @{$self->{dataSections}};
      2         3  
    392 2         10 last;
    393             };
    394             }
    395             }
    396              
    397             sub _addTextblock {
    398 98     98   101 my $self = shift;
    399 98         144 my($paragraph, $no_wrap) = @_;
    400              
    401 98 100       185 if ($self->{titleflag} != 0) { $self->_setTitle( $paragraph ); }
      5         14  
    402              
    403             # DON'T wrap a paragraph in a

    if it's a

    ! 
    404 98 100       371 $no_wrap = 1 if $paragraph =~ m/^\s*
    /im; 
    405              
    406 98 100 66     129 if (! @{$self->{listKind}} || $self->{listKind}[-1] == 0) {
      98 100 66     361  
      42         170  
    407 56 100       85 if (!$no_wrap) {
    408 51         121 $self->{buffer} .= $self->_tagLevel () . "

    $paragraph

    \n";
    409             } else {
    410 5         25 $self->{buffer} .= "$paragraph\n";
    411             }
    412             } elsif (@{$self->{listKind}} && $self->{listKind}[-1] == 1) {
    413 20 100       49 if ($self->{listCurrentParas}[-1]++ == 0) {
    414             # should this list item be closed?
    415 16         18 push @{$self->{tagStack}}, "li";
      16         28  
    416 16         43 $self->{buffer} .= $self->_tagLevel () . "
  • $paragraph";
  • 417             } else {
    418 4         15 $self->{buffer} .= "\n

    $paragraph";
    419             }
    420             } else {
    421 22 100       52 if ($self->{listCurrentParas}[-1]++ == 0) {
    422 20         38 $self->{buffer} .= $self->_tagLevel () . "
    \n";
    423 20         28 push @{$self->{tagStack}}, "dd";
      20         38  
    424             }
    425              
    426 22 100       32 if (!$no_wrap) {
    427 20         42 $self->{buffer} .= $self->_tagLevel () . "

    $paragraph

    \n";
    428             } else {
    429 2         6 $self->{buffer} .= "$paragraph\n";
    430             }
    431             }
    432             }
    433              
    434             sub _tagLevel {
    435 191     191   7715 my $self = shift;
    436 191         198 return ( "\t" x scalar @{$self->{tagStack}} );
      191         891  
    437             }
    438              
    439             # expand interior sequences recursively, bottom up
    440             sub _handleSequence {
    441 65     65   83 my $self = shift;
    442 65         86 my($seq, $no_escape) = @_;
    443 65         76 my $buffer = '';
    444              
    445 65         63 foreach (@{$seq->{'-ptree'}}) {
      65         134  
    446 72 100       112 if (ref $_) {
    447 5         41 $buffer .= $self->_handleSequence($_);
    448             } else {
    449 67 50       147 $buffer .= $no_escape ? $_ : _htmlEscape($_);
    450             }
    451             }
    452              
    453 65 50       222 unless (exists $SEQ{$seq->{'-name'}}) {
    454 0         0 warn "Unrecognized special sequence '$seq->{'-name'}' skipped at $self->{_INFILE} line $seq->{'-line'}\n";
    455 0         0 return $buffer;
    456             }
    457 65         171 return $SEQ{$seq->{'-name'}}->($self, $buffer);
    458             }
    459              
    460             sub _makeIndexId {
    461 118     118   138 my $arg = shift;
    462              
    463 118         369 $arg =~ s/\W+/_/g;
    464 118         478 $arg =~ s/^_+|_+$//g;
    465 118         152 $arg =~ s/__+/_/g;
    466 118         173 $arg = substr($arg, 0, 36);
    467 118         259 return $arg;
    468             }
    469              
    470             sub _addSection {
    471 130     130   163 my $self = shift;
    472 130         176 my ($type, $htmlarg) = @_;
    473 130 50       232 return unless defined $htmlarg;
    474              
    475 130         208 my $index_id;
    476 130 100       273 if ($self->{section_names}{$htmlarg}) {
    477 21         28 $index_id = $self->{section_names}{$htmlarg};
    478             } else {
    479 109         192 $index_id = _makeIndexId($htmlarg);
    480             }
    481            
    482 130 100       438 if ($self->{section_ids}{$index_id}++) {
    483 26         55 $index_id .= "-" . $self->{section_ids}{$index_id};
    484             }
    485            
    486             # if {section_names}{$htmlarg} is already set then this is a duplicate 'id',
    487             # so keep the reference to the first one
    488 130 100       406 $self->{section_names}{$htmlarg} = $index_id
    489             unless exists $self->{section_names}{$htmlarg};
    490              
    491 130         123 push( @{$self->{sections}}, [$type, $index_id, $htmlarg]);
      130         396  
    492 130         259 return $index_id;
    493             }
    494              
    495             sub _findSection {
    496 11     11   19 my $self = shift;
    497 11         16 my ($htmlarg) = @_;
    498              
    499 11         12 my $index_id;
    500 11 100       31 if ($index_id = $self->{section_names}{$htmlarg}) {
    501 2         15 return $index_id;
    502             } else {
    503 9         21 return _makeIndexId($htmlarg);
    504             }
    505             }
    506              
    507             sub _get_elem_level {
    508 113     113   119 my $elem = shift;
    509 113 50       155 if (ref($elem)) {
    510 113         137 my $type = $elem->[0];
    511 113 100       248 if ($type =~ /^head(\d+)$/) {
    512 53         136 return $1;
    513             } else {
    514 60         101 return 0;
    515             }
    516             } else {
    517 0         0 return 0;
    518             }
    519             }
    520              
    521             sub _makeTabbing {
    522 0   0 0   0 my $level = shift || 0;
    523              
    524 0         0 return "\n" . ( "\t" x $level );
    525             }
    526              
    527             sub _makeIndex {
    528 7     7   11 my $self = shift;
    529              
    530 7         21 my $string = "\n

    Index

    \n";
    531              
    532 7         11 my $previous_level = 0;
    533 7         10 my $previous_section = '';
    534            
    535 7         15 SECTION: foreach my $section ( @{ $self->{sections} } )
      7         18  
    536             {
    537 134         134 my $this_level = 0;
    538              
    539 134 100       193 if ( ! ref $section )
    540             {
    541 9         11 for ( $section )
    542             {
    543 9 100       25 if ( $section =~ m/^over$/i )
        50          
    544             {
    545 5         6 $previous_level++;
    546 5   100     29 $string .= ( $previous_section ne 'over' && "\n" ) .
    547             "
  • \n
      \n";
  • 548             }
    549             elsif ( $section =~ m/^back$/i )
    550             {
    551 4         5 $previous_level--;
    552 4         6 $string .= "\n\n";
    553             }
    554             }
    555              
    556 9         16 $previous_section = $section;
    557             }
    558             else
    559             {
    560 125         208 my ( $type, $href, $name ) = @$section;
    561            
    562 125 100       219 if ( $section->[ 0 ] =~ m/^item$/i )
    563             {
    564 12         14 $this_level = $previous_level;
    565             }
    566             else
    567             {
    568 113         180 $this_level = _get_elem_level ( $section );
    569             }
    570              
    571 125 100       287 next SECTION if $this_level == 0;
    572            
    573 65 100       113 if ( $this_level > $previous_level )
        100          
    574             {
    575             # open new list(s)
    576 22         49 $string .= "\n
      " .
    577             ( "\n
  • \n
      " ) x ( $this_level - $previous_level - 1 );
  • 578             }
    579             elsif ( $this_level < $previous_level )
    580             {
    581             # close list(s)
    582 7         15 $string .= "\n" .
    583             ( "\n\n" ) x ( $previous_level - $this_level );
    584             }
    585             else
    586             {
    587 36 100       91 $string .= "\n" unless $previous_section =~ /^over$/i;
    588             }
    589              
    590 65         119 $string .= '
  • ' . $name . '';
  • 591              
    592 65         65 $previous_level = $this_level;
    593              
    594 65 50       142 $previous_section = ( ref $section ? $section->[ 0 ] : $section );
    595             }
    596             }
    597              
    598 7         19 $string .= ( "\n\n" x $previous_level );
    599 7         10 $string .= "
    \n\n\n";
    600            
    601 7         44 return $string;
    602             }
    603              
    604             sub _makeMeta {
    605 7     7   10 my $self = shift;
    606             return
    607 7         22 qq(\t\n)
    608             . qq(\t\n)
    609             . qq(\t\n)
    610             . qq(\t\n)
    611             . qq(\t\n);
    612             }
    613              
    614             sub _setTitle {
    615 5     5   11 my $self = shift;
    616 5         6 my $paragraph = shift;
    617              
    618 5 100       28 if ($paragraph =~ m/^(.+?) - /) {
        50          
        50          
    619 3         8 $self->{doctitle} = $1;
    620             } elsif ($paragraph =~ m/^(.+?): /) {
    621 0         0 $self->{doctitle} = $1;
    622             } elsif ($paragraph =~ m/^(.+?)\.pm/) {
    623 0         0 $self->{doctitle} = $1;
    624             } else {
    625 2         6 $self->{doctitle} = substr($paragraph, 0, 80);
    626             }
    627 5         13 $self->{titleflag} = 0;
    628             }
    629              
    630             sub _htmlEscape {
    631 367     367   518 my $txt = shift;
    632 367         533 $txt =~ s/&/&/g;
    633 367         418 $txt =~ s/
    634 367         385 $txt =~ s/>/>/g;
    635 367         386 $txt =~ s/\"/"/g;
    636 367         1890 return $txt;
    637             }
    638              
    639             ########## Sequence handlers
    640 5     5 0 68 sub seqI { return '' . $_[1] . ''; }
    641 3     3 0 44 sub seqB { return '' . $_[1] . ''; }
    642 3     3 0 44 sub seqC { return '' . $_[1] . ''; }
    643 2     2 0 29 sub seqF { return '' . $_[1] . ''; }
    644 3     3 0 64 sub seqZ { return ''; }
    645              
    646             sub seqL {
    647 50     50 0 156 my ($self, $link) = @_;
    648 50         239 $self->{LinkParser}->parse( $link );
    649              
    650 50         8100 my $page = $self->{LinkParser}->page;
    651 50         391 my $kind = $self->{LinkParser}->type;
    652 50         176 my $string = '';
    653              
    654 50 100       208 if ($kind eq 'hyperlink') { #easy, a hyperlink
        100          
        100          
        100          
    655 7         27 my $targ = $self->{LinkParser}->node;
    656 7         54 my $text = $self->{LinkParser}->text;
    657 7         155 $string = qq($text);
    658             } elsif ($kind =~ m/^bounceurl:(.+)$/) {
    659             # Our link-parser has decided that the link should be handled by a particular URL
    660 13         108 my $url = $1;
    661 13         23 $url = _htmlEscape( $url ); # since the URL may contain ampersands
    662 13         66 $string = $self->{LinkParser}->markup;
    663 13 100       87 if ($string =~ m/P<.+>/) { # when there's no alternative text we get P, and maybe Q
    664 7         32 $string =~ s|Q<(.+?)>|$1|;
    665 7         54 $string =~ s|P<(.+?)>|$1|;
    666             } else {
    667 6         46 $string =~ s|Q<(.+?)>|$1|;
    668             }
    669             } elsif ($page eq '') { # a link to this page
    670             # Post-process these links so we can things up to the correct sections
    671 17         48 my $targ = $self->{LinkParser}->node;
    672 17         105 $string = $self->{LinkParser}->markup;
    673 17         805 $string =~ s|Q<(.+?)>|$1|;
    674             } elsif ($link !~ /\|/) { # a link off-page with _no_ alt text
    675 10         42 $string = $self->{LinkParser}->markup;
    676 10         636 $string =~ s|Q<(.+?)>|$1|;
    677 10         69 $string =~ s|P<(.+?)>|$1|;
    678             } else { # a link off-page with alt text
    679 3         12 my $text = _htmlEscape( $self->{LinkParser}->text );
    680 3         11 my $targ = _htmlEscape( $self->{LinkParser}->node );
    681 3         9 $string = "$text (";
    682 3 100       12 $string .= "$targ in " if $targ;
    683 3         29 $string .= "$page)";
    684             }
    685 50         680 return $string;
    686             }
    687              
    688             sub seqS {
    689 3     3 0 7 my $text = $_[1];
    690 3         21 $text =~ s/\s/ /g;
    691 3         48 return $text;
    692             }
    693              
    694             sub seqX {
    695 2     2 0 4 my $self = shift;
    696 2         2 my $arg = shift;
    697 2         30 return qq[];
    698             }
    699              
    700             sub seqE {
    701 9     9 0 12 my $self = shift;
    702 9         12 my $arg = shift;
    703 9         8 my $rv;
    704              
    705 9 50       49 if ($arg eq 'sol') {
        50          
        50          
        50          
    706 0         0 $rv = '/';
    707             } elsif ($arg eq 'verbar') {
    708 0         0 $rv = '|';
    709             } elsif ($arg =~ /^\d$/) {
    710 0         0 $rv = "&#$arg;";
    711             } elsif ($arg =~ /^0?x(\d+)$/) {
    712 0         0 $rv = $1;
    713             } else {
    714 9         15 $rv = "&$arg;";
    715             }
    716 9         94 return $rv;
    717             }
    718             1;
    719             __END__