diff options
Diffstat (limited to 'docs/tool/Modules/NaturalDocs/Builder/HTML.pm')
| -rw-r--r-- | docs/tool/Modules/NaturalDocs/Builder/HTML.pm | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/docs/tool/Modules/NaturalDocs/Builder/HTML.pm b/docs/tool/Modules/NaturalDocs/Builder/HTML.pm new file mode 100644 index 00000000..95f31b5a --- /dev/null +++ b/docs/tool/Modules/NaturalDocs/Builder/HTML.pm @@ -0,0 +1,398 @@ +############################################################################### +# +# Package: NaturalDocs::Builder::HTML +# +############################################################################### +# +# A package that generates output in HTML. +# +# All functions are called with Package->Function() notation. +# +############################################################################### + +# This file is part of Natural Docs, which is Copyright (C) 2003-2008 Greg Valure +# Natural Docs is licensed under the GPL + + +use strict; +use integer; + +package NaturalDocs::Builder::HTML; + +use base 'NaturalDocs::Builder::HTMLBase'; + + +############################################################################### +# Group: Implemented Interface Functions + + +# +# Function: INIT +# +# Registers the package with <NaturalDocs::Builder>. +# +sub INIT + { + NaturalDocs::Builder->Add(__PACKAGE__); + }; + + +# +# Function: CommandLineOption +# +# Returns the option to follow -o to use this package. In this case, "html". +# +sub CommandLineOption + { + return 'HTML'; + }; + + +# +# Function: BuildFile +# +# Builds the output file from the parsed source file. +# +# Parameters: +# +# sourcefile - The <FileName> of the source file. +# parsedFile - An arrayref of the source file as <NaturalDocs::Parser::ParsedTopic> objects. +# +sub BuildFile #(sourceFile, parsedFile) + { + my ($self, $sourceFile, $parsedFile) = @_; + + my $outputFile = $self->OutputFileOf($sourceFile); + + + # 99.99% of the time the output directory will already exist, so this will actually be more efficient. It only won't exist + # if a new file was added in a new subdirectory and this is the first time that file was ever parsed. + if (!open(OUTPUTFILEHANDLE, '>' . $outputFile)) + { + NaturalDocs::File->CreatePath( NaturalDocs::File->NoFileName($outputFile) ); + + open(OUTPUTFILEHANDLE, '>' . $outputFile) + or die "Couldn't create output file " . $outputFile . "\n"; + }; + + print OUTPUTFILEHANDLE + + + '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" ' + . '"http://www.w3.org/TR/REC-html40/strict.dtd">' . "\n\n" + + . '<html><head>' + + . (NaturalDocs::Settings->CharSet() ? + '<meta http-equiv="Content-Type" content="text/html; charset=' . NaturalDocs::Settings->CharSet() . '">' : '') + + . '<title>' + . $self->BuildTitle($sourceFile) + . '</title>' + + . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($outputFile, $self->MainCSSFile(), 1) . '">' + + . '<script language=JavaScript src="' . $self->MakeRelativeURL($outputFile, $self->MainJavaScriptFile(), 1) . '">' + . '</script>' + . '<script language=JavaScript src="' . $self->MakeRelativeURL($outputFile, $self->SearchDataJavaScriptFile(), 1) . '">' + . '</script>' + + . '</head><body class="ContentPage" onLoad="NDOnLoad()">' + . $self->OpeningBrowserStyles() + + . $self->StandardComments() + + . "\n\n\n" + . $self->BuildContent($sourceFile, $parsedFile) + . "\n\n\n" + . $self->BuildFooter() + . "\n\n\n" + . $self->BuildMenu($sourceFile, undef) + . "\n\n\n" + . $self->BuildToolTips() + . "\n\n\n" + . '<div id=MSearchResultsWindow>' + . '<iframe src="" frameborder=0 name=MSearchResults id=MSearchResults></iframe>' + . '<a href="javascript:searchPanel.CloseResultsWindow()" id=MSearchResultsWindowClose>Close</a>' + . '</div>' + . "\n\n\n" + + . $self->ClosingBrowserStyles() + . '</body></html>'; + + + close(OUTPUTFILEHANDLE); + }; + + +# +# Function: BuildIndex +# +# Builds an index for the passed type. +# +# Parameters: +# +# type - The <TopicType> to limit the index to, or undef if none. +# +sub BuildIndex #(type) + { + my ($self, $type) = @_; + + my $indexTitle = $self->IndexTitleOf($type); + + my $startIndexPage = + + '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" ' + . '"http://www.w3.org/TR/REC-html40/strict.dtd">' . "\n\n" + + . '<html><head>' + + . (NaturalDocs::Settings->CharSet() ? + '<meta http-equiv="Content-Type" content="text/html; charset=' . NaturalDocs::Settings->CharSet() . '">' : '') + + . '<title>' + . $indexTitle; + + if (defined NaturalDocs::Menu->Title()) + { $startIndexPage .= ' - ' . $self->StringToHTML(NaturalDocs::Menu->Title()); }; + + $startIndexPage .= + '</title>' + + . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($self->IndexDirectory(), + $self->MainCSSFile()) . '">' + + . '<script language=JavaScript src="' . $self->MakeRelativeURL($self->IndexDirectory(), + $self->MainJavaScriptFile()) . '"></script>' + . '<script language=JavaScript src="' . $self->MakeRelativeURL($self->IndexDirectory(), + $self->SearchDataJavaScriptFile()) . '">' + . '</script>' + + . '</head><body class="IndexPage" onLoad="NDOnLoad()">' + . $self->OpeningBrowserStyles() + + . $self->StandardComments() + + . "\n\n\n" + + . '<div id=Index>' + . '<div class=IPageTitle>' + . $indexTitle + . '</div>'; + + my $endIndexPage = + '</div><!--Index-->' + + . "\n\n\n" + . $self->BuildFooter() + . "\n\n\n" + . $self->BuildMenu(undef, $type) + . "\n\n\n" + . '<div id=MSearchResultsWindow>' + . '<iframe src="" frameborder=0 name=MSearchResults id=MSearchResults></iframe>' + . '<a href="javascript:searchPanel.CloseResultsWindow()" id=MSearchResultsWindowClose>Close</a>' + . '</div>' + . "\n\n\n" + + . $self->ClosingBrowserStyles() + . '</body></html>'; + + + my $startSearchResultsPage = + + '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" ' + . '"http://www.w3.org/TR/REC-html40/strict.dtd">' . "\n\n" + + . '<html><head>' + + . (NaturalDocs::Settings->CharSet() ? + '<meta http-equiv="Content-Type" content="text/html; charset=' . NaturalDocs::Settings->CharSet() . '">' : '') + + . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($self->SearchResultsDirectory(), + $self->MainCSSFile()) . '">' + + . '<script language=JavaScript src="' . $self->MakeRelativeURL($self->SearchResultsDirectory(), + $self->MainJavaScriptFile()) . '"></script>' + + . '</head><body class="PopupSearchResultsPage" onLoad="NDOnLoad()">' + . $self->OpeningBrowserStyles() + + . $self->StandardComments() + + . "\n\n\n" + + . '<div id=Index>'; + + + my $endSearchResultsPage = + '</div>' + . $self->ClosingBrowserStyles() + . '</body></html>'; + + my $indexContent = NaturalDocs::SymbolTable->Index($type); + my $pageCount = $self->BuildIndexPages($type, $indexContent, $startIndexPage, $endIndexPage, + $startSearchResultsPage, $endSearchResultsPage); + $self->PurgeIndexFiles($type, $indexContent, $pageCount + 1); + }; + + +# +# Function: UpdateMenu +# +# Updates the menu in all the output files that weren't rebuilt. Also generates index.html. +# +sub UpdateMenu + { + my $self = shift; + + + # Update the menu on unbuilt files. + + my $filesToUpdate = NaturalDocs::Project->UnbuiltFilesWithContent(); + + foreach my $sourceFile (keys %$filesToUpdate) + { + $self->UpdateFile($sourceFile); + }; + + + # Update the menu on unchanged index files. + + my $indexes = NaturalDocs::Menu->Indexes(); + + foreach my $index (keys %$indexes) + { + if (!NaturalDocs::SymbolTable->IndexChanged($index)) + { + $self->UpdateIndex($index); + }; + }; + + + # Update index.html + + my $firstMenuEntry = $self->FindFirstFile(); + my $indexFile = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'index.html' ); + + # We have to check because it's possible that there may be no files with Natural Docs content and thus no files on the menu. + if (defined $firstMenuEntry) + { + open(INDEXFILEHANDLE, '>' . $indexFile) + or die "Couldn't create output file " . $indexFile . ".\n"; + + print INDEXFILEHANDLE + '<html><head>' + . '<meta http-equiv="Refresh" CONTENT="0; URL=' + . $self->MakeRelativeURL( NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'index.html'), + $self->OutputFileOf($firstMenuEntry->Target()), 1 ) . '">' + . '</head></html>'; + + close INDEXFILEHANDLE; + } + + elsif (-e $indexFile) + { + unlink($indexFile); + }; + }; + + + +############################################################################### +# Group: Support Functions + + +# +# Function: UpdateFile +# +# Updates an output file. Replaces the menu, HTML title, and footer. It opens the output file, makes the changes, and saves it +# back to disk, which is much quicker than rebuilding the file from scratch if these were the only things that changed. +# +# Parameters: +# +# sourceFile - The source <FileName>. +# +# Dependencies: +# +# - Requires <Builder::BuildMenu()> to surround its content with the exact strings "<div id=Menu>" and "</div><!--Menu-->". +# - Requires <Builder::BuildFooter()> to surround its content with the exact strings "<div id=Footer>" and +# "</div><!--Footer-->". +# +sub UpdateFile #(sourceFile) + { + my ($self, $sourceFile) = @_; + + my $outputFile = $self->OutputFileOf($sourceFile); + + if (open(OUTPUTFILEHANDLE, '<' . $outputFile)) + { + my $content; + + read(OUTPUTFILEHANDLE, $content, -s OUTPUTFILEHANDLE); + close(OUTPUTFILEHANDLE); + + + $content =~ s{<title>[^<]*<\/title>}{'<title>' . $self->BuildTitle($sourceFile) . '</title>'}e; + + $content =~ s/<div id=Menu>.*?<\/div><!--Menu-->/$self->BuildMenu($sourceFile, undef)/es; + + $content =~ s/<div id=Footer>.*?<\/div><!--Footer-->/$self->BuildFooter()/e; + + + open(OUTPUTFILEHANDLE, '>' . $outputFile); + print OUTPUTFILEHANDLE $content; + close(OUTPUTFILEHANDLE); + }; + }; + + +# +# Function: UpdateIndex +# +# Updates an index's output file. Replaces the menu and footer. It opens the output file, makes the changes, and saves it +# back to disk, which is much quicker than rebuilding the file from scratch if these were the only things that changed. +# +# Parameters: +# +# type - The index <TopicType>, or undef if none. +# +sub UpdateIndex #(type) + { + my ($self, $type) = @_; + + my $page = 1; + + my $outputFile = $self->IndexFileOf($type, $page); + + my $newMenu = $self->BuildMenu(undef, $type); + my $newFooter = $self->BuildFooter(); + + while (-e $outputFile) + { + open(OUTPUTFILEHANDLE, '<' . $outputFile) + or die "Couldn't open output file " . $outputFile . ".\n"; + + my $content; + + read(OUTPUTFILEHANDLE, $content, -s OUTPUTFILEHANDLE); + close(OUTPUTFILEHANDLE); + + + $content =~ s/<div id=Menu>.*?<\/div><!--Menu-->/$newMenu/es; + + $content =~ s/<div id=Footer>.*<\/div><!--Footer-->/$newFooter/e; + + + open(OUTPUTFILEHANDLE, '>' . $outputFile) + or die "Couldn't save output file " . $outputFile . ".\n"; + + print OUTPUTFILEHANDLE $content; + close(OUTPUTFILEHANDLE); + + $page++; + $outputFile = $self->IndexFileOf($type, $page); + }; + }; + + +1; |