| blib/lib/Pod/Classdoc/Project.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 24 | 260 | 9.2 |
| branch | 0 | 128 | 0.0 |
| condition | 0 | 87 | 0.0 |
| subroutine | 8 | 15 | 53.3 |
| pod | 0 | 1 | 0.0 |
| total | 32 | 491 | 6.5 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | =pod | ||||||
| 2 | |||||||
| 3 | =begin classdoc | ||||||
| 4 | |||||||
| 5 | Generates and merges |
||||||
| 6 | and |
||||||
| 7 | tree widget (via |
||||||
| 8 | manuals, classdocs, and folded/highlighted source code. | ||||||
| 9 | |||||||
| 10 | @author Dean Arnold | ||||||
| 11 | @exports renderProject() the only public method | ||||||
| 12 | @see |
||||||
| 13 | @see |
||||||
| 14 | @see |
||||||
| 15 | @see |
||||||
| 16 | |||||||
| 17 | =end classdoc | ||||||
| 18 | |||||||
| 19 | =cut | ||||||
| 20 | |||||||
| 21 | package Pod::Classdoc::Project; | ||||||
| 22 | |||||||
| 23 | 2 | 2 | 4858 | use Pod::ProjectDocs; | |||
| 2 | 591428 | ||||||
| 2 | 20 | ||||||
| 24 | 2 | 2 | 9078 | use Pod::Classdoc; | |||
| 2 | 7 | ||||||
| 2 | 74 | ||||||
| 25 | 2 | 2 | 24 | use JSON; | |||
| 2 | 4 | ||||||
| 2 | 25 | ||||||
| 26 | 2 | 2 | 389 | use Exporter; | |||
| 2 | 5 | ||||||
| 2 | 103 | ||||||
| 27 | |||||||
| 28 | 2 | 2 | 14 | use base('Exporter'); | |||
| 2 | 4 | ||||||
| 2 | 249 | ||||||
| 29 | |||||||
| 30 | @EXPORT = ('renderProject'); | ||||||
| 31 | |||||||
| 32 | 2 | 2 | 12 | use strict; | |||
| 2 | 474 | ||||||
| 2 | 709 | ||||||
| 33 | 2 | 2 | 290 | use warnings; | |||
| 2 | 5 | ||||||
| 2 | 11793 | ||||||
| 34 | |||||||
| 35 | our $VERSION = '1.01'; | ||||||
| 36 | |||||||
| 37 | our %defaults = ( | ||||||
| 38 | 'Heredocs', 1, | ||||||
| 39 | 'POD', 1, | ||||||
| 40 | 'Comments', 1, | ||||||
| 41 | 'Expandable', 1, | ||||||
| 42 | 'Imports', 1, | ||||||
| 43 | 'MinFoldLines', 4, | ||||||
| 44 | ); | ||||||
| 45 | |||||||
| 46 | =pod | ||||||
| 47 | |||||||
| 48 | =begin classdoc | ||||||
| 49 | |||||||
| 50 | Generates merged project documentation from |
||||||
| 51 | |
||||||
| 52 | with a table of contents widget generated from |
||||||
| 53 | |||||||
| 54 | @optional Additions HTML document to be appended to the TOC widget | ||||||
| 55 | @optional Comments boolean; if true (the default), fold comments in PPI::HTML::CodeFolder output | ||||||
| 56 | @optional Charset specifies character set for Pod::ProjectDocs | ||||||
| 57 | @optional CloseImage name of closed node icon in TOC; default 'closedbook.gif' | ||||||
| 58 | @optional CSSPath path to CSS files; default is <Output>/css |
||||||
| 59 | @optional Description specifies description header for Pod::ProjectDocs | ||||||
| 60 | @optional Download specifies a download file to be appended to the TOC widget | ||||||
| 61 | @optional Expandable boolean; if true (the default), folds in PPI::HTML::CodeFolder output are expandable | ||||||
| 62 | @optional Force boolean; if true, forces generation of all Pod::ProjectDocs documents | ||||||
| 63 | @optional Heredoc boolean; if true (the default), fold heredocs in PPI::HTML::CodeFolder output | ||||||
| 64 | @optional Imports boolean; if true (the default), fold imports in PPI::HTML::CodeFolder output | ||||||
| 65 | @optional IconPath path to icon images for TOC; default is <Output>/img |
||||||
| 66 | @optional JSPath path to Javascript files; default is <Output>/js |
||||||
| 67 | @optional Language specifies language info for Pod::ProjectDocs | ||||||
| 68 | @optional Libs library directories to be processed; defaults to './lib' and './bin' | ||||||
| 69 | @optional MinFoldLines minimum number of lines for codefolding; default 4 | ||||||
| 70 | @optional NoIcons boolean; if true (default false), TOC will not use icons | ||||||
| 71 | @optional NoSource boolean; if true (default false), omit PPI::HTML::CodeFolder source processing | ||||||
| 72 | @optional OpenImage name of open node icon in TOC; default 'openbook.gif' | ||||||
| 73 | @optional Order arrayref of package/script names; TOC nodes will be ordered in same order | ||||||
| 74 | as this list. Any unlisted packages/scripts will be alphabetically ordered | ||||||
| 75 | after these nodes are included. | ||||||
| 76 | @optional Output root path of output files; default './classdocs' | ||||||
| 77 | @optional POD boolean; if true (the default), fold POD in PPI::HTML::CodeFolder output | ||||||
| 78 | @optional RootImage name of root node icon in TOC; default 'globe.gif' | ||||||
| 79 | @optional Title title string for HTML document, and root node of TOC | ||||||
| 80 | @optional Verbose boolean; if true, emits lots of diagnostic info | ||||||
| 81 | |||||||
| 82 | @static | ||||||
| 83 | |||||||
| 84 | =end classdoc | ||||||
| 85 | |||||||
| 86 | =cut | ||||||
| 87 | |||||||
| 88 | sub renderProject { | ||||||
| 89 | 0 | 0 | 0 | my %args = @_; | |||
| 90 | |||||||
| 91 | 0 | 0 | my $out = $args{Output} || './classdocs'; | ||||
| 92 | 0 | 0 | my $csspath = $args{CSSPath} || "$out/css"; | ||||
| 93 | 0 | 0 | my $jspath = $args{JSPath} || "$out/js"; | ||||
| 94 | 0 | 0 | my $imgpath = $args{Iconpath} || "$out/img"; | ||||
| 95 | 0 | 0 | my $openimg = $args{OpenImage} || 'openbook.gif'; | ||||
| 96 | 0 | 0 | my $closeimg = $args{CloseImage} || 'closedbook.gif'; | ||||
| 97 | 0 | 0 | my $rootimg = $args{RootImage} || 'globe.gif'; | ||||
| 98 | 0 | 0 | $args{Title} ||= 'My Project'; | ||||
| 99 | |||||||
| 100 | 0 | while (my ($k, $v) = each %defaults) { | |||||
| 101 | 0 | 0 | $args{$k} = $v unless exists $args{$k}; | ||||
| 102 | } | ||||||
| 103 | |||||||
| 104 | 0 | 0 | unless ($args{NoSource}) { | ||||
| 105 | 0 | eval { | |||||
| 106 | 0 | require PPI::HTML::CodeFolder; | |||||
| 107 | }; | ||||||
| 108 | 0 | 0 | $args{NoSource} = 1, | ||||
| 109 | warn "Cannot generate codefolded sources:\n$@\n" | ||||||
| 110 | if $@; | ||||||
| 111 | } | ||||||
| 112 | |||||||
| 113 | 0 | my $notree; | |||||
| 114 | 0 | eval { | |||||
| 115 | 0 | require HTML::ListToTree; | |||||
| 116 | }; | ||||||
| 117 | 0 | 0 | $notree = 1, | ||||
| 118 | warn "Cannot generate tree table of contents:\n$@\n" | ||||||
| 119 | if $@; | ||||||
| 120 | # | ||||||
| 121 | # first generate project docs; note that this | ||||||
| 122 | # copies source files into the outroot/src path | ||||||
| 123 | # | ||||||
| 124 | 0 | 0 | print "\nGenerating ProjectDocs..." | ||||
| 125 | if $args{Verbose}; | ||||||
| 126 | |||||||
| 127 | 0 | $args{Libs} = [ './lib', './bin' ] | |||||
| 128 | 0 | 0 | 0 | unless $args{Libs} && ref $args{Libs} && ($#{$args{Libs}} >= 0); | |||
| 0 | |||||||
| 129 | |||||||
| 130 | 0 | 0 | Pod::ProjectDocs->new( | ||||
| 131 | outroot => $out, | ||||||
| 132 | libroot => $args{Libs}, | ||||||
| 133 | title => $args{Title}, | ||||||
| 134 | desc => $args{Description}, | ||||||
| 135 | charset => $args{CharSet}, | ||||||
| 136 | index => 1, | ||||||
| 137 | verbose => $args{Verbose}, | ||||||
| 138 | forcegen => $args{Force}, | ||||||
| 139 | lang => $args{Language}, | ||||||
| 140 | )->gen() or die $@; | ||||||
| 141 | # | ||||||
| 142 | # then generate classdocs | ||||||
| 143 | # | ||||||
| 144 | 0 | 0 | print "done\nCollecting source files..." | ||||
| 145 | if $args{Verbose}; | ||||||
| 146 | |||||||
| 147 | 0 | my $path = "$out/src"; | |||||
| 148 | 0 | my @dirs = (); | |||||
| 149 | 0 | 0 | die $@ | ||||
| 150 | unless _recurseDirs($path, \@dirs); | ||||||
| 151 | |||||||
| 152 | 0 | 0 | print "done\nScanning ", join(', ', @dirs), "\n" | ||||
| 153 | if $args{Verbose}; | ||||||
| 154 | |||||||
| 155 | 0 | my @files = (); | |||||
| 156 | 0 | foreach my $p (@dirs) { | |||||
| 157 | 0 | 0 | 0 | warn "$p directory not found" and | |||
| 158 | next | ||||||
| 159 | unless opendir(PATH, $p); | ||||||
| 160 | # | ||||||
| 161 | # recurse the directory to find all .pm files; | ||||||
| 162 | # | ||||||
| 163 | 0 | my @tfiles = readdir PATH; | |||||
| 164 | 0 | closedir PATH; | |||||
| 165 | |||||||
| 166 | 0 | push @files, map "$p/$_", grep /\.pm$/, @tfiles; | |||||
| 167 | } | ||||||
| 168 | |||||||
| 169 | 0 | 0 | my $classdocs = Pod::Classdoc::ForProjectTOC->new($out, $args{Title}, $args{Verbose}) or die $@; | ||||
| 170 | |||||||
| 171 | 0 | my %sources = (); | |||||
| 172 | 0 | my $HTML; | |||||
| 173 | 0 | 0 | unless ($args{NoSource}) { | ||||
| 174 | 0 | my %tagcolors = ( | |||||
| 175 | cast => '#339999', | ||||||
| 176 | comment => '#008080', | ||||||
| 177 | core => '#FF0000', | ||||||
| 178 | double => '#999999', | ||||||
| 179 | heredoc => '#FF0000', | ||||||
| 180 | heredoc_content => '#FF0000', | ||||||
| 181 | heredoc_terminator => '#FF0000', | ||||||
| 182 | interpolate => '#999999', | ||||||
| 183 | keyword => '#0000FF', | ||||||
| 184 | line_number => '#666666', | ||||||
| 185 | literal => '#999999', | ||||||
| 186 | magic => '#0099FF', | ||||||
| 187 | match => '#9900FF', | ||||||
| 188 | number => '#990000', | ||||||
| 189 | operator => '#DD7700', | ||||||
| 190 | pod => '#008080', | ||||||
| 191 | pragma => '#990000', | ||||||
| 192 | regex => '#9900FF', | ||||||
| 193 | single => '#999999', | ||||||
| 194 | substitute => '#9900FF', | ||||||
| 195 | transliterate => '#9900FF', | ||||||
| 196 | word => '#999999', | ||||||
| 197 | ); | ||||||
| 198 | |||||||
| 199 | 0 | 0 | $HTML = PPI::HTML::CodeFolder->new( | ||||
| 200 | line_numbers => 1, | ||||||
| 201 | page => 1, | ||||||
| 202 | colors => \%tagcolors, | ||||||
| 203 | verbose => $args{Verbose}, | ||||||
| 204 | fold => { | ||||||
| 205 | Abbreviate => 1, | ||||||
| 206 | Heredocs => $args{Heredocs}, | ||||||
| 207 | POD => $args{POD}, | ||||||
| 208 | Comments => $args{Comments}, | ||||||
| 209 | Expandable => $args{Expandable}, | ||||||
| 210 | Imports => $args{Imports}, | ||||||
| 211 | MinFoldLines => $args{MinFoldLines}, | ||||||
| 212 | Javascript => "$jspath/ppicf.js", | ||||||
| 213 | Stylesheet => "$csspath/ppicf.css", | ||||||
| 214 | }, | ||||||
| 215 | ) | ||||||
| 216 | or die "\nFailed to create a PPI::HTML::CodeFolder"; | ||||||
| 217 | } | ||||||
| 218 | |||||||
| 219 | 0 | foreach my $file (@files) { | |||||
| 220 | # | ||||||
| 221 | # add a file to the classdocs | ||||||
| 222 | # | ||||||
| 223 | 0 | 0 | print "$file: generating classdocs...\r" | ||||
| 224 | if $args{Verbose}; | ||||||
| 225 | 0 | my $Document = $classdocs->open($file); | |||||
| 226 | |||||||
| 227 | 0 | 0 | unless ($args{NoSource}) { | ||||
| 228 | # | ||||||
| 229 | # codefold/highlight the file | ||||||
| 230 | # | ||||||
| 231 | 0 | 0 | print "$file: generating codefolded source...\r" | ||||
| 232 | if $args{Verbose}; | ||||||
| 233 | |||||||
| 234 | 0 | my $outfile = substr($file, length($path) + 1); | |||||
| 235 | 0 | 0 | my $t = $HTML->html( $Document, "$out/$outfile.html" ) | ||||
| 236 | or die "\nFailed to generate HTML"; | ||||||
| 237 | # | ||||||
| 238 | # create output in output file | ||||||
| 239 | # | ||||||
| 240 | 0 | 0 | open(OUTF, ">$out/$outfile.html") or die "Can't create $out/$outfile.html: $!"; | ||||
| 241 | 0 | print OUTF $t; | |||||
| 242 | 0 | close OUTF; | |||||
| 243 | # | ||||||
| 244 | # don't need the original sources now | ||||||
| 245 | # | ||||||
| 246 | 0 | unlink $file; | |||||
| 247 | } | ||||||
| 248 | } | ||||||
| 249 | |||||||
| 250 | 0 | foreach ($out, $csspath, $jspath, $imgpath) { | |||||
| 251 | 0 | 0 | mkdir $_ | ||||
| 252 | unless -d $_; | ||||||
| 253 | } | ||||||
| 254 | |||||||
| 255 | 0 | 0 | print "\nRendering classdocs...\n" | ||||
| 256 | if $args{Verbose}; | ||||||
| 257 | |||||||
| 258 | 0 | $classdocs->writeClassdocs(1); | |||||
| 259 | # | ||||||
| 260 | # generate the TOC | ||||||
| 261 | # | ||||||
| 262 | 0 | $/ = undef; | |||||
| 263 | 0 | 0 | print "Generating table of contents...\n" | ||||
| 264 | if $args{Verbose}; | ||||||
| 265 | # | ||||||
| 266 | # extract index from root document | ||||||
| 267 | # | ||||||
| 268 | 0 | 0 | open INF, "$out/index.html" or die $!; | ||||
| 269 | 0 | my $html = |
|||||
| 270 | 0 | close INF; | |||||
| 271 | # | ||||||
| 272 | # get rid of search box and adjust path separators as needed | ||||||
| 273 | # | ||||||
| 274 | 0 | $html=~s! \s* !!s; Search.*? |
|||||
| 275 | 0 | $html=~s!\.\\!./!gs; | |||||
| 276 | 0 | $html=~s!\\\\!/!gs; | |||||
| 277 | # | ||||||
| 278 | # replace current index page after edits | ||||||
| 279 | # | ||||||
| 280 | 0 | 0 | open OUTF, ">$out/project.html" | ||||
| 281 | or die "Cannot create $out/project.html: $!"; | ||||||
| 282 | 0 | print OUTF $html; | |||||
| 283 | 0 | close OUTF; | |||||
| 284 | |||||||
| 285 | 0 | my ($list) = ($html=~/var\s+managers\s*=\s*([^\n]+)\n/); | |||||
| 286 | |||||||
| 287 | 0 | 0 | $list = substr($list, 0, -1) if (substr($list, -1) eq ';'); | ||||
| 288 | |||||||
| 289 | 0 | $list = jsonToObj($list); | |||||
| 290 | |||||||
| 291 | 0 | my $mans = $list->[0]; | |||||
| 292 | 0 | 0 | 0 | die "Unrecognizable project index\n" | |||
| 293 | unless ($mans->{desc} eq 'Package Manuals') || | ||||||
| 294 | ($mans->{desc} eq 'Perl Manuals'); | ||||||
| 295 | # | ||||||
| 296 | # locate any manfiles and map to package names | ||||||
| 297 | # | ||||||
| 298 | 0 | my %manuals = (); | |||||
| 299 | 0 | $_->{name}=~s/-/::/g, | |||||
| 300 | $_->{path}=~tr/\\/\//, | ||||||
| 301 | $manuals{$_->{name}} = { | ||||||
| 302 | Manual => $_->{path}, | ||||||
| 303 | TOC => _extractTOC(join('/', $out, $_->{path}), $csspath) | ||||||
| 304 | } | ||||||
| 305 | 0 | foreach (@{$mans->{records}}); | |||||
| 306 | |||||||
| 307 | 0 | 0 | my $toc = $classdocs->getProjectTOC( | ||||
| 308 | Manuals => \%manuals, | ||||||
| 309 | SourceMap => $HTML ? $HTML->getCrossReference() : undef, | ||||||
| 310 | GroupExternals => 1, | ||||||
| 311 | Additions => $args{Additions}, | ||||||
| 312 | Order => $args{Order} | ||||||
| 313 | ); | ||||||
| 314 | 0 | ($toc) = ($toc=~/(.*?)/s); | |||||
| 315 | |||||||
| 316 | # open OUTF, ">testoc.html"; | ||||||
| 317 | # print OUTF $toc; | ||||||
| 318 | # close OUTF; | ||||||
| 319 | # | ||||||
| 320 | # replace index page with frameset | ||||||
| 321 | # | ||||||
| 322 | 0 | 0 | open(INDEX, ">$out/index.html") or die $!; | ||||
| 323 | 0 | print INDEX | |||||
| 324 | " | ||||||
| 325 | |||||||
| 326 | |
||||||
| 327 | |||||||
| 328 | |||||||
| 329 | |||||||
| 330 | |||||||
| 331 | |||||||
| 332 | |||||||
| 333 | "; | ||||||
| 334 | 0 | close INDEX; | |||||
| 335 | # | ||||||
| 336 | # render the TOC and write it out; | ||||||
| 337 | # add any download link, and current generate timestamp | ||||||
| 338 | # | ||||||
| 339 | 0 | my $download = $args{Download}; | |||||
| 340 | 0 | 0 | if ($download) { | ||||
| 341 | 0 | my @parts = split /[\\\/]/, $download; | |||||
| 342 | 0 | $download = "$parts[-1] "; |
|||||
| 343 | } | ||||||
| 344 | else { | ||||||
| 345 | 0 | $download = ''; | |||||
| 346 | } | ||||||
| 347 | |||||||
| 348 | 0 | $download .= "Generated by Pod::Classdoc::Project v.$VERSION at " . _trimtime() . ''; |
|||||
| 349 | 0 | 0 | unless ($notree) { | ||||
| 350 | 0 | 0 | my $tree = HTML::ListToTree->new( | ||||
| 351 | Text => $args{Title}, | ||||||
| 352 | Link => 'project.html', | ||||||
| 353 | Source => $toc | ||||||
| 354 | ) | ||||||
| 355 | or die $@; | ||||||
| 356 | 0 | my $widget = $tree->render( | |||||
| 357 | CloseIcon => $closeimg, | ||||||
| 358 | OpenIcon => $openimg, | ||||||
| 359 | RootIcon => $rootimg, | ||||||
| 360 | IconPath => _pathAdjust($out, $imgpath), | ||||||
| 361 | CSSPath => _pathAdjust($out, $csspath) . '/dtree.css', | ||||||
| 362 | JSPath => _pathAdjust($out, $jspath) . '/dtree.js', | ||||||
| 363 | UseIcons => (!$args{NoIcons}), | ||||||
| 364 | Additions => $download, | ||||||
| 365 | BasePath => $out | ||||||
| 366 | ); | ||||||
| 367 | |||||||
| 368 | 0 | 0 | open(TREE, ">$out/toc.html") or die $!; | ||||
| 369 | 0 | print TREE $widget; | |||||
| 370 | 0 | close TREE; | |||||
| 371 | # | ||||||
| 372 | # make sure to write out the extras | ||||||
| 373 | # | ||||||
| 374 | 0 | 0 | 0 | die $@ | |||
| 0 | |||||||
| 0 | |||||||
| 0 | |||||||
| 375 | unless $tree->writeJavascript("$jspath/dtree.js") && | ||||||
| 376 | $tree->writeCSS("$csspath/dtree.css") && | ||||||
| 377 | $tree->writeIcons($imgpath) && | ||||||
| 378 | ((!$HTML) || | ||||||
| 379 | ($HTML->writeJavascript("$jspath/ppicf.js") && | ||||||
| 380 | $HTML->writeCSS("$csspath/ppicf.css"))); | ||||||
| 381 | } | ||||||
| 382 | 0 | return 1; | |||||
| 383 | } | ||||||
| 384 | |||||||
| 385 | sub _trimtime { | ||||||
| 386 | 0 | 0 | my @parts = split /\s+/, (scalar localtime()); | ||||
| 387 | 0 | shift @parts; | |||||
| 388 | 0 | ($parts[0], $parts[1], $parts[2]) = ($parts[2], $parts[0], $parts[1] . ','); | |||||
| 389 | 0 | return join(' ', @parts); | |||||
| 390 | } | ||||||
| 391 | |||||||
| 392 | sub _recurseDirs { | ||||||
| 393 | 0 | 0 | my ($path, $dirs) = @_; | ||||
| 394 | |||||||
| 395 | 0 | 0 | $@ = "$path directory not found", | ||||
| 396 | return undef | ||||||
| 397 | unless opendir(PATH, $path); | ||||||
| 398 | # | ||||||
| 399 | # recurse the directory to find all subdirs | ||||||
| 400 | # | ||||||
| 401 | 0 | my @files = readdir PATH; | |||||
| 402 | 0 | closedir PATH; | |||||
| 403 | 0 | push @$dirs, $path; | |||||
| 404 | 0 | foreach (@files) { | |||||
| 405 | return undef | ||||||
| 406 | 0 | 0 | 0 | if ($_ ne '.') && ($_ ne '..') && (-d "$path/$_") && (!_recurseDirs("$path/$_", $dirs)); | |||
| 0 | |||||||
| 0 | |||||||
| 407 | } | ||||||
| 408 | 0 | return 1; | |||||
| 409 | } | ||||||
| 410 | |||||||
| 411 | # | ||||||
| 412 | # extract index from a manual file, and otherwise | ||||||
| 413 | # beautify the file | ||||||
| 414 | # | ||||||
| 415 | sub _extractTOC { | ||||||
| 416 | 0 | 0 | my ($file, $css) = @_; | ||||
| 417 | |||||||
| 418 | 0 | my $oldsep = $/; | |||||
| 419 | 0 | $/ = undef; | |||||
| 420 | 0 | 0 | open INF, $file or die $!; | ||||
| 421 | 0 | my $html = |
|||||
| 422 | 0 | close INF; | |||||
| 423 | 0 | $/ = $oldsep; | |||||
| 424 | |||||||
| 425 | 0 | $html=~s/ |
|||||
| 426 | |||||||
| 427 | return undef | ||||||
| 428 | 0 | 0 | unless ($html=~s/\s+(.+)//s); | ||||
| 429 | 0 | my $index = $1; | |||||
| 430 | # | ||||||
| 431 | # clean up stuff we've changed or don't want | ||||||
| 432 | # | ||||||
| 433 | 0 | 0 | $html=~s!(href=["'])([^"']+)!{ my $t = $2; $t=~tr/\\/\//; $1 . $t; }!egs | ||||
| 0 | |||||||
| 0 | |||||||
| 0 | |||||||
| 0 | |||||||
| 434 | if ($^O eq 'MSWin32'); | ||||||
| 435 | 0 | $html=~s///gs; | |||||
| 436 | 0 | $html=~s/Source<\/a>//s; | |||||
| 437 | |||||||
| 438 | 0 | $html=~s! .+? !!s; |
|||||
| 439 | |||||||
| 440 | 0 | $index=~s!Index\s*
|
|||||
| 441 | 0 | $index=~s! !!s; |
|||||
| 442 | |||||||
| 443 | # " to keep textpad happy | ||||||
| 444 | 0 | 0 | open FRAME, ">$file" or die $!; | ||||
| 445 | 0 | print FRAME $html; | |||||
| 446 | 0 | close FRAME; | |||||
| 447 | 0 | return $index; | |||||
| 448 | } | ||||||
| 449 | |||||||
| 450 | sub _pathAdjust { | ||||||
| 451 | 0 | 0 | my ($path, $jspath) = @_; | ||||
| 452 | # return $jspath | ||||||
| 453 | # unless (substr($jspath, 0, 2) eq './') && (substr($path, 0, 2) eq './'); | ||||||
| 454 | # | ||||||
| 455 | # relative path, adjust as needed from current base | ||||||
| 456 | # | ||||||
| 457 | 0 | my @parts = split /\//, $path; | |||||
| 458 | 0 | my @jsparts = split /\//, $jspath; | |||||
| 459 | # my $jsfile = pop @jsparts; # get rid of filename | ||||||
| 460 | # pop @parts; # remove filename | ||||||
| 461 | 0 | shift @parts; | |||||
| 462 | 0 | shift @jsparts; # and the relative lead | |||||
| 463 | 0 | my $prefix = ''; | |||||
| 464 | 0 | 0 | shift @parts, | ||||
| 0 | |||||||
| 465 | shift @jsparts | ||||||
| 466 | while @parts && @jsparts && ($parts[0] eq $jsparts[0]); | ||||||
| 467 | # push @jsparts, $jsfile; | ||||||
| 468 | 0 | return ('../' x scalar @parts) . join('/', @jsparts) | |||||
| 469 | } | ||||||
| 470 | |||||||
| 471 | |||||||
| 472 | 1; | ||||||
| 473 | |||||||
| 474 | =pod | ||||||
| 475 | |||||||
| 476 | =begin classdoc | ||||||
| 477 | |||||||
| 478 | Subclass of |
||||||
| 479 | write a project TOC. | ||||||
| 480 | |||||||
| 481 | =end classdoc | ||||||
| 482 | |||||||
| 483 | =cut | ||||||
| 484 | |||||||
| 485 | package Pod::Classdoc::ForProjectTOC; | ||||||
| 486 | |||||||
| 487 | 2 | 2 | 26 | use base ('Pod::Classdoc'); | |||
| 2 | 4 | ||||||
| 2 | 4528 | ||||||
| 488 | |||||||
| 489 | =pod | ||||||
| 490 | |||||||
| 491 | =begin classdoc | ||||||
| 492 | |||||||
| 493 | Write out a project table of contents document for the current collection of | ||||||
| 494 | classdocs as a nested HTML list. The output filename is 'toc.html'. | ||||||
| 495 | The caller may optionally specify the order of classes in the menu. | ||||||
| 496 | |||||||
| 497 | @optional Additions string of additional HTML list elements to append to TOC | ||||||
| 498 | @optional Manuals hashref mapping package names to manual files | ||||||
| 499 | @optional SourceMap hashref mapping packages and methods to their source filename | ||||||
| 500 | @optional Order arrayref of packages in the order in which they should appear in TOC; if a partial list, | ||||||
| 501 | any remaining packages will be appended to the TOC in alphabetical order | ||||||
| 502 | @optional GroupExternals if true, group external methods separately | ||||||
| 503 | |||||||
| 504 | @return this object on success, undef on failure, with error message in $@ | ||||||
| 505 | |||||||
| 506 | =end classdoc | ||||||
| 507 | |||||||
| 508 | =cut | ||||||
| 509 | |||||||
| 510 | sub writeProjectTOC { | ||||||
| 511 | 0 | 0 | my $self = shift; | ||||
| 512 | 0 | my $path = $self->{_path}; | |||||
| 513 | 0 | 0 | $@ = "Can't open $path/toc.html: $!", | ||||
| 514 | return undef | ||||||
| 515 | unless CORE::open(OUTF, ">$path/toc.html"); | ||||||
| 516 | |||||||
| 517 | 0 | print OUTF $self->getProjectTOC(@_); | |||||
| 518 | 0 | close OUTF; | |||||
| 519 | 0 | return $self; | |||||
| 520 | } | ||||||
| 521 | |||||||
| 522 | =pod | ||||||
| 523 | |||||||
| 524 | =begin classdoc | ||||||
| 525 | |||||||
| 526 | Generate a project table of contents document for the current collection of | ||||||
| 527 | classdocs as a nested HTML list. Caller may optionally specify | ||||||
| 528 | the order of classes in the menu. | ||||||
| 529 | |||||||
| 530 | @optional Additions string of additional HTML list elements to append to TOC | ||||||
| 531 | @optional Manuals hashref mapping package names to manual files | ||||||
| 532 | @optional SourceMap hashref mapping packages and methods to their source filename | ||||||
| 533 | @optional Order arrayref of package names in the order in which they should appear in TOC; if a partial list, | ||||||
| 534 | any remaining packages will be appended to the TOC in alphabetical order | ||||||
| 535 | @optional GroupExternals if true, group external methods separately | ||||||
| 536 | |||||||
| 537 | @return the TOC document | ||||||
| 538 | |||||||
| 539 | =end classdoc | ||||||
| 540 | |||||||
| 541 | =cut | ||||||
| 542 | |||||||
| 543 | sub getProjectTOC { | ||||||
| 544 | 0 | 0 | my $self = shift; | ||||
| 545 | 0 | my %args = @_; | |||||
| 546 | 0 | 0 | my @order = $args{Order} ? @{$args{Order}} : (); | ||||
| 0 | |||||||
| 547 | 0 | 0 | my $sources = $args{SourceMap} || {}; | ||||
| 548 | 0 | 0 | my $manuals = $args{Manuals} || {}; | ||||
| 549 | 0 | my $path = $self->{_path}; | |||||
| 550 | 0 | my $title = $self->{_title}; | |||||
| 551 | 0 | my $base; | |||||
| 552 | 0 | my $doc = | |||||
| 553 | " | ||||||
| 554 | |||||||
| 555 | |||||||
| 556 | |||||||
| 557 | |
||||||
| 558 | "; | ||||||
| 559 | 0 | my %ordered = (); | |||||
| 560 | 0 | $ordered{$_} = 1 foreach (@order); | |||||
| 561 | # | ||||||
| 562 | # merge any undoc'd packages | ||||||
| 563 | # | ||||||
| 564 | 0 | while (my ($pkg, $pkginfo) = each %$sources) { | |||||
| 565 | 0 | 0 | $self->{_classes}{$pkg} = { } | ||||
| 566 | unless exists $self->{_classes}{$pkg}; | ||||||
| 567 | |||||||
| 568 | 0 | my $info = $self->{_classes}{$pkg}; | |||||
| 569 | 0 | 0 | $info->{URL} = exists $info->{File} ? join('#', $self->makeClassPath($pkg), $pkg) : $pkginfo->{URL}; | ||||
| 570 | 0 | 0 | $info->{Methods} ||= {}; | ||||
| 571 | 0 | 0 | $info->{constructors} ||= {}; | ||||
| 572 | 0 | my $methods = $info->{Methods}; | |||||
| 573 | 0 | my $constr = $info->{constructors}; | |||||
| 574 | 0 | while (my ($sub, $suburl) = each %{$pkginfo->{Methods}}) { | |||||
| 0 | |||||||
| 575 | 0 | 0 | $constr->{$sub}{URL} = join('#_f_', $self->makeClassPath($pkg), $sub), | ||||
| 576 | $constr->{$sub}{Source} = $suburl, | ||||||
| 577 | next | ||||||
| 578 | if exists $constr->{$sub}; | ||||||
| 579 | |||||||
| 580 | 0 | 0 | 0 | print STDERR "*** $pkg\::$sub has no classdocs.\n" | |||
| 581 | unless (substr($sub, 0, 1) eq '_') || exists $methods->{$sub}; | ||||||
| 582 | |||||||
| 583 | 0 | 0 | $methods->{$sub}{URL} = $suburl, | ||||
| 584 | next | ||||||
| 585 | unless exists $methods->{$sub}; | ||||||
| 586 | |||||||
| 587 | 0 | $methods->{$sub}{URL} = join('#_f_', $self->makeClassPath($pkg), $sub); | |||||
| 588 | 0 | $methods->{$sub}{Source} = $suburl; | |||||
| 589 | } | ||||||
| 590 | } | ||||||
| 591 | # | ||||||
| 592 | # merge in any manuals | ||||||
| 593 | # | ||||||
| 594 | 0 | my ($pkg, $manual, $key, $info); | |||||
| 595 | 0 | 0 | 0 | $self->{_classes}{$pkg} ||= { }, | |||
| 596 | $info = $self->{_classes}{$pkg}, | ||||||
| 597 | $key = exists $info->{URL} ? 'Manual' : 'URL', | ||||||
| 598 | $info->{$key} = $manual->{Manual} | ||||||
| 599 | while (($pkg, $manual) = each %$manuals); | ||||||
| 600 | |||||||
| 601 | 0 | foreach (sort keys %{$self->{_classes}}) { | |||||
| 0 | |||||||
| 602 | 0 | 0 | push @order, $_ unless exists $ordered{$_}; | ||||
| 603 | } | ||||||
| 604 | |||||||
| 605 | 0 | foreach $pkg (@order) { | |||||
| 606 | # | ||||||
| 607 | # due to input @order, we might get classes that don't exist | ||||||
| 608 | # | ||||||
| 609 | 0 | 0 | next unless exists $self->{_classes}{$pkg}; | ||||
| 610 | |||||||
| 611 | 0 | my $info = $self->{_classes}{$pkg}; | |||||
| 612 | 0 | $base = $pkg; | |||||
| 613 | 0 | $base =~s/::/\//g; | |||||
| 614 | 0 | $doc .= "
|
|||||
| 615 | # | ||||||
| 616 | # only point to classdocs if we have some | ||||||
| 617 | # | ||||||
| 618 | 0 | 0 | $doc .= " |
||||
| 619 | |
||||||
| 620 | if $info->{File}; | ||||||
| 621 | # | ||||||
| 622 | # ditto for manuals | ||||||
| 623 | # if no source or docs, dump manual TOC and skip the rest | ||||||
| 624 | # | ||||||
| 625 | 0 | 0 | $doc .= $info->{Manual} ? | ||||
| 0 | |||||||
| 626 | " |
||||||
| 627 | join( '', $manuals->{$pkg}{TOC}, "\n\n") | ||||||
| 628 | if exists $manuals->{$pkg}; | ||||||
| 629 | |||||||
| 630 | 0 | my %t; | |||||
| 631 | 0 | my ($k, $v); | |||||
| 632 | 0 | 0 | 0 | if (exists $info->{exports} && @{$info->{exports}}) { | |||
| 0 | |||||||
| 633 | 0 | $doc .= " |
|||||
| 634 | |
||||||
| 635 | "; | ||||||
| 636 | 0 | %t = @{$info->{exports}}; | |||||
| 0 | |||||||
| 637 | $doc .= " |
||||||
| 638 | 0 | foreach (sort keys %t); | |||||
| 639 | 0 | $doc .= "\n\n"; | |||||
| 640 | } | ||||||
| 641 | 0 | 0 | 0 | if (exists $info->{imports} && @{$info->{imports}}) { | |||
| 0 | |||||||
| 642 | 0 | $doc .= " |
|||||
| 643 | |
||||||
| 644 | "; | ||||||
| 645 | 0 | %t = @{$info->{imports}}; | |||||
| 0 | |||||||
| 646 | $doc .= " |
||||||
| 647 | 0 | foreach (sort keys %t); | |||||
| 648 | 0 | $doc .= "\n\n"; | |||||
| 649 | } | ||||||
| 650 | 0 | 0 | 0 | if (exists $info->{member} && @{$info->{member}}) { | |||
| 0 | |||||||
| 651 | 0 | $doc .= " |
|||||
| 652 | |
||||||
| 653 | "; | ||||||
| 654 | 0 | %t = @{$info->{member}}; | |||||
| 0 | |||||||
| 655 | $doc .= " |
||||||
| 656 | 0 | foreach (sort keys %t); | |||||
| 657 | 0 | $doc .= "\n\n"; | |||||
| 658 | } | ||||||
| 659 | 0 | 0 | 0 | if (exists $info->{constructors} && %{$info->{constructors}}) { | |||
| 0 | |||||||
| 660 | 0 | $doc .= " |
|||||
| 661 | |
||||||
| 662 | "; | ||||||
| 663 | 0 | my $constr = $info->{constructors}; | |||||
| 664 | 0 | foreach (sort keys %$constr) { | |||||
| 665 | 0 | $doc .= " |
|||||
| 666 | 0 | 0 | $doc .= "(ext.)\n", | ||||
| 667 | next | ||||||
| 668 | if $constr->{$_}{External}; | ||||||
| 669 | |||||||
| 670 | 0 | 0 | $doc .= "\n", | ||||
| 671 | next | ||||||
| 672 | unless $constr->{$_}{Source}; | ||||||
| 673 | |||||||
| 674 | 0 | $doc .= " |
|||||
| 675 | |
||||||
| 676 | \n"; | ||||||
| 677 | } | ||||||
| 678 | 0 | $doc .= "\n\n"; | |||||
| 679 | } | ||||||
| 680 | 0 | 0 | 0 | if (exists $info->{Methods} && %{$info->{Methods}}) { | |||
| 0 | |||||||
| 681 | 0 | my %externals = (); | |||||
| 682 | 0 | 0 | if ($args{GroupExternals}) { | ||||
| 683 | 0 | while (my ($sub, $subinfo) = each %{$info->{Methods}}) { | |||||
| 0 | |||||||
| 684 | 0 | 0 | $externals{$sub} = $subinfo | ||||
| 685 | if $subinfo->{External}; | ||||||
| 686 | } | ||||||
| 687 | } | ||||||
| 688 | 0 | $doc .= " |
|||||
| 689 | |
||||||
| 690 | "; | ||||||
| 691 | 0 | my $methods = $info->{Methods}; | |||||
| 692 | 0 | foreach (sort keys %$methods) { | |||||
| 693 | 0 | 0 | $doc .= exists $methods->{$_}{Source} ? | ||||
| 0 | |||||||
| 694 | "
|
||||||
| 695 | " |
||||||
| 696 | unless exists $externals{$_}; | ||||||
| 697 | } | ||||||
| 698 | 0 | 0 | if (%externals) { | ||||
| 699 | 0 | $doc .= " |
|||||
| 700 | |
||||||
| 701 | "; | ||||||
| 702 | $doc .= " |
||||||
| 703 | 0 | foreach (sort keys %externals); | |||||
| 704 | 0 | $doc .= "\n\n"; | |||||
| 705 | } | ||||||
| 706 | 0 | $doc .= "\n\n"; | |||||
| 707 | } | ||||||
| 708 | 0 | $doc .= "\n\n"; | |||||
| 709 | } | ||||||
| 710 | 0 | 0 | $args{Additions} ||= ''; | ||||
| 711 | 0 | $doc .= "\n$args{Additions} | |||||
| 712 | |||||||
| 713 | |||||||
| 714 | |||||||
| 715 | |||||||
| 716 | |||||||
| 717 | "; | ||||||
| 718 | |||||||
| 719 | 0 | return $doc; | |||||
| 720 | } | ||||||
| 721 | |||||||
| 722 | 1; | ||||||
| 723 |