blob: 8c49194cdfba9644fc2018c493a6491a0455aa73 [file] [log] [blame]
#=== HTML::TocGenerator =======================================================
# function: Generate 'HTML::Toc' table of contents.
# note: - 'TT' is an abbrevation of 'TocToken'.
package HTML::TocGenerator;
use strict;
use HTML::Parser;
BEGIN {
use vars qw(@ISA $VERSION);
$VERSION = '0.91';
@ISA = qw(HTML::Parser);
}
# Warnings
use constant WARNING_NESTED_ANCHOR_PS_WITHIN_PS => 1;
use constant WARNING_TOC_ATTRIBUTE_PS_NOT_AVAILABLE_WITHIN_PS => 2;
use constant TOC_TOKEN_ID => 0;
use constant TOC_TOKEN_INCLUDE => 1;
use constant TOC_TOKEN_EXCLUDE => 2;
use constant TOC_TOKEN_TOKENS => 3;
use constant TOC_TOKEN_GROUP => 4;
use constant TOC_TOKEN_TOC => 5;
# Token types
use constant TT_TAG_BEGIN => 0;
use constant TT_TAG_END => 1;
use constant TT_TAG_TYPE_END => 2;
use constant TT_INCLUDE_ATTRIBUTES_BEGIN => 3;
use constant TT_EXCLUDE_ATTRIBUTES_BEGIN => 4;
use constant TT_INCLUDE_ATTRIBUTES_END => 5;
use constant TT_EXCLUDE_ATTRIBUTES_END => 6;
use constant TT_GROUP => 7;
use constant TT_TOC => 8;
use constant TT_ATTRIBUTES_TOC => 9;
use constant CONTAINMENT_INCLUDE => 0;
use constant CONTAINMENT_EXCLUDE => 1;
use constant TEMPLATE_ANCHOR => '$groupId."-".$node';
use constant TEMPLATE_ANCHOR_HREF =>
'"<a href=#".' . TEMPLATE_ANCHOR . '.">"';
use constant TEMPLATE_ANCHOR_HREF_FILE =>
'"<a href=".$file."#".' . TEMPLATE_ANCHOR . '.">"';
use constant TEMPLATE_ANCHOR_NAME =>
'"<a name=".' . TEMPLATE_ANCHOR . '.">"';
use constant TEMPLATE_TOKEN_NUMBER => '"$node &nbsp"';
use constant TT_TOKENTYPE_START => 0;
use constant TT_TOKENTYPE_END => 1;
use constant TT_TOKENTYPE_TEXT => 2;
use constant TT_TOKENTYPE_COMMENT => 3;
use constant TT_TOKENTYPE_DECLARATION => 4;
END {}
#--- HTML::TocGenerator::new() ------------------------------------------------
# function: Constructor
sub new {
# Get arguments
my ($aType) = @_;
my $self = $aType->SUPER::new;
# Bias to not generate ToC
$self->{_doGenerateToc} = 0;
# Bias to not use global groups
$self->{_doUseGroupsGlobal} = 0;
# Output
$self->{output} = "";
# Reset internal variables
$self->_resetBatchVariables();
$self->{options} = {};
return $self;
} # new()
#--- HTML::TocGenerator::_deinitializeBatch() ---------------------------------
sub _deinitializeBatch() {
# Get arguments
my ($self) = @_;
} # _deinitializeBatch()
#--- HTML::TocGenerator::_deinitializeExtenderBatch() -------------------------
sub _deinitializeExtenderBatch() {
# Get arguments
my ($self) = @_;
# Do general batch deinitialization
$self->_deinitializeBatch();
# Indicate end of ToC generation
$self->{_doGenerateToc} = 0;
# Reset batch variables
$self->_resetBatchVariables();
} # _deinitializeExtenderBatch()
#--- HTML::TocGenerator::_deinitializeGeneratorBatch() ------------------------
sub _deinitializeGeneratorBatch() {
# Get arguments
my ($self) = @_;
# Do 'extender' batch deinitialization
$self->_deinitializeExtenderBatch();
} # _deinitializeBatchGenerator()
#--- HTML::TocGenerator::_doesHashContainHash() -------------------------------
# function: Determines whether hash1 matches regular expressions of hash2.
# args: - $aHash1
# - $aHash2
# - $aContainmentType: 0 (include) or 1 (exclude)
# returns: True (1) if hash1 satisfies hash2, 0 if not. For example, with the
# following hashes:
#
# %hash1 = { %hash2 = {
# 'class' => 'header' 'class' => '^h'
# 'id' => 'intro' }
# }
#
# the routine will return 1 if 'aContainmentType' equals 0, cause
# 'hash1' satisfies the conditions of 'hash2'. The routine will
# return 0 if 'aContainmentType' equals 1, cause 'hash1' doesn't
# exclude the conditions of 'hash2'.
# note: Class function.
sub _doesHashContainHash {
# Get arguments
my ($aHash1, $aHash2, $aContainmentType) = @_;
# Local variables
my ($key1, $value1, $key2, $value2, $result);
# Bias to success
$result = 1;
# Loop through hash2
HASH2: while (($key2, $value2) = each %$aHash2) {
# Yes, values are available;
# Get value1
$value1 = $aHash1->{$key2};
# Does value1 match criteria of value2?
if (defined($value1) && $value1 =~ m/$value2/) {
# Yes, value1 matches criteria of value2;
# Containment type was exclude?
if ($aContainmentType == CONTAINMENT_EXCLUDE) {
# Yes, containment type was exclude;
# Indicate condition fails
$result = 0;
# Reset 'each' iterator which we're going to break
keys %$aHash2;
# Break loop
last HASH2;
}
}
else {
# No, value1 didn't match criteria of value2;
# Containment type was include?
if ($aContainmentType == CONTAINMENT_INCLUDE) {
# Yes, containment type was include;
# Indicate condition fails
$result = 0;
# Reset 'each' iterator which we're going to break
keys %$aHash2;
# Break loop
last HASH2;
}
}
}
# Return value
return $result;
} # _doesHashContainHash()
#--- HTML::TocGenerator::_extend() --------------------------------------------
# function: Extend ToC.
# - $aString: String to parse.
sub _extend {
# Get arguments
my ($self, $aFile) = @_;
# Local variables
my ($file);
# Parse string
$self->parse($aFile);
# Flush remaining buffered text
$self->eof();
} # _extend()
#--- HTML::TocGenerator::_extendFromFile() ------------------------------------
# function: Extend ToC.
# - $aFile: (reference to array of) file to parse.
sub _extendFromFile {
# Get arguments
my ($self, $aFile) = @_;
# Local variables
my ($file, @files);
# Dereference array reference or make array of file specification
@files = (ref($aFile) =~ m/ARRAY/) ? @$aFile : ($aFile);
# Loop through files
foreach $file (@files) {
# Store filename
$self->{_currentFile} = $file;
# Parse file
$self->parse_file($file);
# Flush remaining buffered text
$self->eof();
}
} # _extendFromFile()
#--- HTML::TocGenerator::_formatHeadingLevel() --------------------------------
# function: Format heading level.
# args: - $aLevel: Level of current heading
# - $aClass: Class of current heading
# - $aGroup: Group of current heading
# - $aToc: Toc of current heading
sub _formatHeadingLevel {
# Get arguments
my ($self, $aLevel, $aClass, $aGroup, $aToc) = @_;
# Local variables
my ($result, $headingNumber, $numberingStyle);
$headingNumber = $self->_getGroupIdManager($aToc)->
{levels}{$aClass}[$aLevel - 1] || 0;
# Alias numbering style of current group
$numberingStyle = $aGroup->{numberingStyle};
SWITCH: {
if ($numberingStyle eq "decimal") {
$result = $headingNumber;
last SWITCH;
}
if ($numberingStyle eq "lower-alpha") {
$result = chr($headingNumber + ord('a') - 1);
last SWITCH;
}
if ($numberingStyle eq "upper-alpha") {
$result = chr($headingNumber + ord('A') - 1);
last SWITCH;
}
if ($numberingStyle eq "lower-roman") {
require Roman;
$result = Roman::roman($headingNumber);
last SWITCH;
}
if ($numberingStyle eq "upper-roman") {
require Roman;
$result = Roman::Roman($headingNumber);
last SWITCH;
}
die "Unknown case: $numberingStyle";
}
# Return value
return $result;
} # _formatHeadingLevel()
#--- HTML::TocGenerator::_formatTocNode() -------------------------------------
# function: Format heading node.
# args: - $aLevel: Level of current heading
# - $aClass: Class of current heading
# - $aGroup: Group of current heading
# - $aToc: Toc of current heading
sub _formatTocNode {
# Get arguments
my ($self, $aLevel, $aClass, $aGroup, $aToc) = @_;
# Local variables
my ($result, $level, $levelGroups);
# Alias 'levelGroups' of right 'groupId'
$levelGroups = $aToc->{_levelGroups}{$aGroup->{'groupId'}};
# Loop through levels
for ($level = 1; $level <= $aLevel; $level++) {
# If not first level, add dot
$result = ($result ? $result . "." : $result);
# Format heading level using argument group
$result .= $self->_formatHeadingLevel(
$level, $aClass, @{$levelGroups}[$level - 1], $aToc
);
}
# Return value
return $result;
} # _formatTocNode()
#--- HTML::TocGenerator::_generate() ------------------------------------------
# function: Generate ToC.
# args: - $aString: Reference to string to parse
sub _generate {
# Get arguments
my ($self, $aString) = @_;
# Local variables
my ($toc);
# Loop through ToCs
foreach $toc (@{$self->{_tocs}}) {
# Clear ToC
$toc->clear();
}
# Extend ToCs
$self->_extend($aString);
} # _generate()
#--- HTML::TocGenerator::_generateFromFile() ----------------------------------
# function: Generate ToC.
# args: - $aFile: (reference to array of) file to parse.
sub _generateFromFile {
# Get arguments
my ($self, $aFile) = @_;
# Local variables
my ($toc);
# Loop through ToCs
foreach $toc (@{$self->{_tocs}}) {
# Clear ToC
$toc->clear();
}
# Extend ToCs
$self->_extendFromFile($aFile);
} # _generateFromFile()
#--- HTML::TocGenerator::_getGroupIdManager() ---------------------------------
# function: Get group id manager.
# args: - $aToc: Active ToC.
# returns: Group id levels.
sub _getGroupIdManager {
# Get arguments
my ($self, $aToc) = @_;
# Local variables
my ($result);
# Global groups?
if ($self->{options}{'doUseGroupsGlobal'}) {
# Yes, global groups;
$result = $self;
}
else {
# No, local groups;
$result = $aToc;
}
# Return value
return $result;
} # _getGroupIdManager()
#--- HTML::TocGenerator::_initializeBatch() -----------------------------------
# function: Initialize batch. This function is called once when a parse batch
# is started.
# args: - $aTocs: Reference to array of tocs.
sub _initializeBatch {
# Get arguments
my ($self, $aTocs) = @_;
# Local variables
my ($toc);
# Store reference to tocs
# Is ToC specification reference to array?
if (ref($aTocs) =~ m/ARRAY/) {
# Yes, ToC specification is reference to array;
# Store array reference
$self->{_tocs} = $aTocs;
}
else {
# No, ToC specification is reference to ToC object;
# Wrap reference in array reference, containing only one element
$self->{_tocs} = [$aTocs];
}
# Loop through ToCs
foreach $toc (@{$self->{_tocs}}) {
# Parse ToC options
$toc->parseOptions();
}
} # _initializeBatch()
#--- HTML::TocGenerator::_initializeExtenderBatch() --------------------------
# function: Initialize 'extender' batch. This function is called once when a
# parse batch is started.
# args: - $aTocs: Reference to array of tocs.
sub _initializeExtenderBatch {
# Get arguments
my ($self, $aTocs) = @_;
# Do general batch initialization
$self->_initializeBatch($aTocs);
# Parse ToC options
$self->_parseTocOptions();
# Indicate start of batch
$self->{_doGenerateToc} = 1;
} # _initializeExtenderBatch()
#--- HTML::TocGenerator::_initializeGeneratorBatch() --------------------------
# function: Initialize generator batch. This function is called once when a
# parse batch is started.
# args: - $aTocs: Reference to array of tocs.
# - $aOptions: optional options
sub _initializeGeneratorBatch {
# Get arguments
my ($self, $aTocs, $aOptions) = @_;
# Add invocation options
$self->setOptions($aOptions);
# Option 'doUseGroupsGlobal' specified?
if (!defined($self->{options}{'doUseGroupsGlobal'})) {
# No, options 'doUseGroupsGlobal' not specified;
# Default to no 'doUseGroupsGlobal'
$self->{options}{'doUseGroupsGlobal'} = 0;
}
# Global groups?
if ($self->{options}{'doUseGroupsGlobal'}) {
# Yes, global groups;
# Reset groups and levels
$self->_resetStackVariables();
}
# Do 'extender' batch initialization
$self->_initializeExtenderBatch($aTocs);
} # _initializeGeneratorBatch()
#--- HTML::TocGenerator::_linkTocToToken() ------------------------------------
# function: Link ToC to token.
# args: - $aToc: ToC to add token to.
# - $aFile
# - $aGroupId
# - $aLevel
# - $aNode
# - $aGroupLevel
# - $aLinkType
# - $aTokenAttributes: reference to hash containing attributes of
# currently parsed token
sub _linkTocToToken {
# Get arguments
my (
$self, $aToc, $aFile, $aGroupId, $aLevel, $aNode, $aGroupLevel,
$aDoLinkToId, $aTokenAttributes
) = @_;
# Local variables
my ($file, $groupId, $level, $node, $anchorName);
my ($doInsertAnchor, $doInsertId);
# Fill local arguments to be used by templates
$file = $aFile;
$groupId = $aGroupId;
$level = $aLevel;
$node = $aNode;
# Assemble anchor name
$anchorName =
ref($aToc->{_templateAnchorName}) eq "CODE" ?
&{$aToc->{_templateAnchorName}}(
$aFile, $aGroupId, $aLevel, $aNode
) :
eval($aToc->{_templateAnchorName});
# Bias to insert anchor name
$doInsertAnchor = 1;
$doInsertId = 0;
# Link to 'id'?
if ($aDoLinkToId) {
# Yes, link to 'id';
# Indicate to insert anchor id
$doInsertAnchor = 0;
$doInsertId = 1;
# Id attribute is available?
if (defined($aTokenAttributes->{id})) {
# Yes, id attribute is available;
# Use existing ids?
if ($aToc->{options}{'doUseExistingIds'}) {
# Yes, use existing ids;
# Use existing id
$anchorName = $aTokenAttributes->{id};
# Indicate to not insert id
$doInsertId = 0;
}
}
}
else {
# No, link to 'name';
# Anchor name is currently active?
if (defined($self->{_activeAnchorName})) {
# Yes, anchor name is currently active;
# Use existing anchors?
if ($aToc->{options}{'doUseExistingAnchors'}) {
# Yes, use existing anchors;
# Use existing anchor name
$anchorName = $self->{_activeAnchorName};
# Indicate to not insert anchor name
$doInsertAnchor = 0;
}
else {
# No, don't use existing anchors; insert new anchor;
#
}
}
}
# Add reference to ToC
$aToc->{_toc} .=
ref($aToc->{_templateAnchorHrefBegin}) eq "CODE" ?
&{$aToc->{_templateAnchorHrefBegin}}(
$aFile, $aGroupId, $aLevel, $aNode, $anchorName
) :
eval($aToc->{_templateAnchorHrefBegin});
# Bias to not output anchor name end
$self->{_doOutputAnchorNameEnd} = 0;
# Must anchor be inserted?
if ($doInsertAnchor) {
# Yes, anchor must be inserted;
# Allow adding of anchor name begin token to text by calling
# 'anchorNameBegin' method
$self->anchorNameBegin(
ref($aToc->{_templateAnchorNameBegin}) eq "CODE" ?
&{$aToc->{_templateAnchorNameBegin}}(
$aFile, $aGroupId, $aLevel, $aNode, $anchorName
) :
eval($aToc->{_templateAnchorNameBegin}),
$aToc
);
}
# Must anchorId attribute be inserted?
if ($doInsertId) {
# Yes, anchorId attribute must be inserted;
# Allow adding of anchorId attribute to text by calling 'anchorId'
# method
$self->anchorId($anchorName);
}
} # _linkTocToToken()
#--- HTML::TocGenerator::_outputAnchorNameEndConditionally() ------------------
# function: Output 'anchor name end' if necessary
# args: - $aToc: ToC of which 'anchor name end' must be output.
sub _outputAnchorNameEndConditionally {
# Get arguments
my ($self, $aToc) = @_;
# Must anchor name end be output?
if ($self->{_doOutputAnchorNameEnd}) {
# Yes, output anchor name end;
# Allow adding of anchor to text by calling 'anchorNameEnd'
# method
$self->anchorNameEnd(
ref($aToc->{_templateAnchorNameEnd}) eq "CODE" ?
&{$aToc->{_templateAnchorNameEnd}} :
eval($aToc->{_templateAnchorNameEnd}),
$aToc
);
}
} # _outputAnchorNameEndConditionally()
#--- HTML::TocGenerator::_parseTocOptions() -----------------------------------
# function: Parse ToC options.
sub _parseTocOptions {
# Get arguments
my ($self) = @_;
# Local variables
my ($toc, $group, $tokens, $tokenType, $i);
# Create parsers for ToC tokens
$self->{_tokensTocBegin} = [];
my $tokenTocBeginParser = HTML::_TokenTocBeginParser->new(
$self->{_tokensTocBegin}
);
my $tokenTocEndParser = HTML::_TokenTocEndParser->new();
# Loop through ToCs
foreach $toc (@{$self->{_tocs}}) {
# Reference parser ToC to current ToC
$tokenTocBeginParser->setToc($toc);
# Loop through 'tokenToToc' groups
foreach $group (@{$toc->{options}{'tokenToToc'}}) {
# Reference parser group to current group
$tokenTocBeginParser->setGroup($group);
# Parse 'tokenToToc' group
$tokenTocBeginParser->parse($group->{'tokenBegin'});
# Flush remaining buffered text
$tokenTocBeginParser->eof();
$tokenTocEndParser->parse(
$group->{'tokenEnd'},
$tokenTocBeginParser->{_lastAddedToken},
$tokenTocBeginParser->{_lastAddedTokenType}
);
# Flush remaining buffered text
$tokenTocEndParser->eof();
}
}
} # _parseTocOptions()
#--- HTML::TocGenerator::_processTocEndingToken() -----------------------------
# function: Process ToC-ending-token.
# args: - $aTocToken: token which acts as ToC-ending-token.
sub _processTocEndingToken {
# Get arguments
my ($self, $aTocToken) = @_;
# Local variables
my ($toc);
# Aliases
$toc = $aTocToken->[TT_TOC];
# Link ToC to tokens?
if ($toc->{options}{'doLinkToToken'}) {
# Yes, link ToC to tokens;
# Add anchor href end
$toc->{_toc} .=
(ref($toc->{_templateAnchorHrefEnd}) eq "CODE") ?
&{$toc->{_templateAnchorHrefEnd}} :
eval($toc->{_templateAnchorHrefEnd});
# Output anchor name end only if necessary
$self->_outputAnchorNameEndConditionally($toc);
}
} # _processTocEndingToken()
#--- HTML::TocGenerator::_processTocStartingToken() ---------------------------
# function: Process ToC-starting-token.
# args: - $aTocToken: token which acts as ToC-starting-token.
# - $aTokenType: type of token. Can be either TT_TOKENTYPE_START,
# _END, _TEXT, _COMMENT or _DECLARATION.
# - $aTokenAttributes: reference to hash containing attributes of
# currently parsed token
# - $aTokenOrigText: reference to original token text
sub _processTocStartingToken {
# Get arguments
my ($self, $aTocToken, $aTokenType, $aTokenAttributes, $aTokenOrigText) = @_;
# Local variables
my ($i, $level, $doLinkToId, $node, $groupLevel);
my ($file, $tocTokenId, $groupId, $toc, $attribute);
# Aliases
$file = $self->{_currentFile};
$toc = $aTocToken->[TT_TOC];
$level = $aTocToken->[TT_GROUP]{'level'};
$groupId = $aTocToken->[TT_GROUP]{'groupId'};
# Retrieve 'doLinkToId' setting from either group options or toc options
$doLinkToId = (defined($aTocToken->[TT_GROUP]{'doLinkToId'})) ?
$aTocToken->[TT_GROUP]{'doLinkToId'} : $toc->{options}{'doLinkToId'};
# Link to 'id' and tokenType isn't 'start'?
if (($doLinkToId) && ($aTokenType != TT_TOKENTYPE_START)) {
# Yes, link to 'id' and tokenType isn't 'start';
# Indicate to *not* link to 'id'
$doLinkToId = 0;
}
if (ref($level) eq "CODE") {
$level = &$level($self->{_currentFile}, $node);
}
if (ref($groupId) eq "CODE") {
$groupId = &$groupId($self->{_currentFile}, $node);
}
# Determine class level
my $groupIdManager = $self->_getGroupIdManager($toc);
# Known group?
if (!exists($groupIdManager->{groupIdLevels}{$groupId})) {
# No, unknown group;
# Add group
$groupIdManager->{groupIdLevels}{$groupId} = keys(
%{$groupIdManager->{groupIdLevels}}
) + 1;
}
$groupLevel = $groupIdManager->{groupIdLevels}{$groupId};
# Temporarily allow symbolic references
#no strict qw(refs);
# Increase level
$groupIdManager->{levels}{$groupId}[$level - 1] += 1;
# Reset remaining levels of same group
for ($i = $level; $i < @{$groupIdManager->{levels}{$groupId}}; $i++) {
$groupIdManager->{levels}{$groupId}[$i] = 0;
}
# Assemble numeric string indicating current level
$node = $self->_formatTocNode(
$level, $groupId, $aTocToken->[TT_GROUP], $toc
);
# Add newline if _toc not empty
if ($toc->{_toc}) {
$toc->{_toc} .= "\n";
}
# Add toc item info
$toc->{_toc} .= "$level $groupLevel $groupId $node " .
$groupIdManager->{levels}{$groupId}[$level - 1] . " ";
# Add value of 'id' attribute if available
if (defined($aTokenAttributes->{id})) {
$toc->{_toc} .= $aTokenAttributes->{id};
}
$toc->{_toc} .= " ";
# Link ToC to tokens?
if ($toc->{options}{'doLinkToToken'}) {
# Yes, link ToC to tokens;
# Link ToC to token
$self->_linkTocToToken(
$toc, $file, $groupId, $level, $node, $groupLevel, $doLinkToId,
$aTokenAttributes
);
}
# Number tokens?
if (
$aTocToken->[TT_GROUP]{'doNumberToken'} ||
(
! defined($aTocToken->[TT_GROUP]{'doNumberToken'}) &&
$toc->{options}{'doNumberToken'}
)
) {
# Yes, number tokens;
# Add number by calling 'number' method
$self->number(
ref($toc->{_templateTokenNumber}) eq "CODE" ?
&{$toc->{_templateTokenNumber}}(
$node, $groupId, $file, $groupLevel, $level, $toc
) :
eval($toc->{_templateTokenNumber}),
$toc
);
}
# Must attribute be used as ToC text?
if (defined($aTocToken->[TT_ATTRIBUTES_TOC])) {
# Yes, attribute must be used as ToC text;
# Loop through attributes
foreach $attribute (@{$aTocToken->[TT_ATTRIBUTES_TOC]}) {
# Attribute is available?
if (defined($$aTokenAttributes{$attribute})) {
# Yes, attribute is available;
# Add attribute value to ToC
$self->_processTocText($$aTokenAttributes{$attribute}, $toc);
}
else {
# No, attribute isn't available;
# Show warning
$self->_showWarning(
WARNING_TOC_ATTRIBUTE_PS_NOT_AVAILABLE_WITHIN_PS,
[$attribute, $$aTokenOrigText]
);
}
# Output anchor name end only if necessary
#$self->_outputAnchorNameEndConditionally($toc);
# End attribute
$self->_processTocEndingToken($aTocToken);
}
}
else {
# No, attribute mustn't be used as ToC text;
# Add end token to 'end token array'
push(
@{$self->{_tokensTocEnd}[$aTocToken->[TT_TAG_TYPE_END]]}, $aTocToken
);
}
} # _processTocStartingToken()
#--- HTML::TocGenerator::_processTocText() ------------------------------------
# function: This function processes text which must be added to the preliminary
# ToC.
# args: - $aText: Text to add to ToC.
# - $aToc: ToC to add text to.
sub _processTocText {
# Get arguments
my ($self, $aText, $aToc) = @_;
# Add text to ToC
$aToc->{_toc} .= $aText;
} # _processTocText()
#--- HTML::TocGenerator::_processTokenAsTocEndingToken() ----------------------
# function: Check for token being a token to use for triggering the end of
# a ToC line and process it accordingly.
# args: - $aTokenType: type of token: 'start', 'end', 'comment' or 'text'.
# - $aTokenId: token id of currently parsed token
sub _processTokenAsTocEndingToken {
# Get arguments
my ($self, $aTokenType, $aTokenId) = @_;
# Local variables
my ($i, $tokenId, $toc, $tokens);
# Loop through dirty start tokens
$i = 0;
# Alias token array of right type
$tokens = $self->{_tokensTocEnd}[$aTokenType];
# Loop through token array
while ($i < scalar @$tokens) {
# Aliases
$tokenId = $tokens->[$i][TT_TAG_END];
# Does current end tag equals dirty tag?
if ($aTokenId eq $tokenId) {
# Yes, current end tag equals dirty tag;
# Process ToC-ending-token
$self->_processTocEndingToken($tokens->[$i]);
# Remove dirty tag from array, automatically advancing to
# next token
splice(@$tokens, $i, 1);
}
else {
# No, current end tag doesn't equal dirty tag;
# Advance to next token
$i++;
}
}
} # _processTokenAsTocEndingToken()
#--- HTML::TocGenerator::_processTokenAsTocStartingToken() --------------------
# function: Check for token being a ToC-starting-token and process it
# accordingly.
# args: - $aTokenType: type of token. Can be either TT_TOKENTYPE_START,
# _END, _TEXT, _COMMENT or _DECLARATION.
# - $aTokenId: token id of currently parsed token
# - $aTokenAttributes: reference to hash containing attributes of
# currently parsed token
# - $aTokenOrigText: reference to original text of token
# returns: 1 if successful, i.e. token is processed as ToC-starting-token, 0
# if not.
sub _processTokenAsTocStartingToken {
# Get arguments
my ($self, $aTokenType, $aTokenId, $aTokenAttributes, $aTokenOrigText) = @_;
# Local variables
my ($level, $levelToToc, $groupId, $groupToToc);
my ($result, $tocToken, $tagBegin, @tokensTocBegin, $fileSpec);
# Bias to token not functioning as ToC-starting-token
$result = 0;
# Loop through start tokens of right type
foreach $tocToken (@{$self->{_tokensTocBegin}[$aTokenType]}) {
# Alias file filter
$fileSpec = $tocToken->[TT_GROUP]{'fileSpec'};
# File matches?
if (!defined($fileSpec) || (
defined($fileSpec) &&
($self->{_currentFile} =~ m/$fileSpec/)
)) {
# Yes, file matches;
# Alias tag begin
$tagBegin = $tocToken->[TT_TAG_BEGIN];
# Tag and attributes match?
if (
defined($tagBegin) &&
($aTokenId =~ m/$tagBegin/) &&
HTML::TocGenerator::_doesHashContainHash(
$aTokenAttributes, $tocToken->[TT_INCLUDE_ATTRIBUTES_BEGIN], 0
) &&
HTML::TocGenerator::_doesHashContainHash(
$aTokenAttributes, $tocToken->[TT_EXCLUDE_ATTRIBUTES_BEGIN], 1
)
) {
# Yes, tag and attributes match;
# Aliases
$level = $tocToken->[TT_GROUP]{'level'};
$levelToToc = $tocToken->[TT_TOC]{options}{'levelToToc'};
$groupId = $tocToken->[TT_GROUP]{'groupId'};
$groupToToc = $tocToken->[TT_TOC]{options}{'groupToToc'};
# Must level and group be processed?
if (
($level =~ m/$levelToToc/) &&
($groupId =~ m/$groupToToc/)
) {
# Yes, level and group must be processed;
# Indicate token acts as ToC-starting-token
$result = 1;
# Process ToC-starting-token
$self->_processTocStartingToken(
$tocToken, $aTokenType, $aTokenAttributes, $aTokenOrigText
);
}
}
}
}
# Return value
return $result;
} # _processTokenAsTocStartingToken()
#--- HTML::TocGenerator::_resetBatchVariables() -------------------------------
# function: Reset variables which are set because of batch invocation.
sub _resetBatchVariables {
# Get arguments
my ($self) = @_;
# Filename of current file being parsed, empty string if not available
$self->{_currentFile} = "";
# Arrays containing start, end, comment, text & declaration tokens which
# must trigger the ToC assembling. Each array element may contain a
# reference to an array containing the following elements:
#
# TT_TAG_BEGIN => 0;
# TT_TAG_END => 1;
# TT_TAG_TYPE_END => 2;
# TT_INCLUDE_ATTRIBUTES_BEGIN => 3;
# TT_EXCLUDE_ATTRIBUTES_BEGIN => 4;
# TT_INCLUDE_ATTRIBUTES_END => 5;
# TT_EXCLUDE_ATTRIBUTES_END => 6;
# TT_GROUP => 7;
# TT_TOC => 8;
# TT_ATTRIBUTES_TOC => 9;
#
$self->{_tokensTocBegin} = [
[], # TT_TOKENTYPE_START
[], # TT_TOKENTYPE_END
[], # TT_TOKENTYPE_COMMENT
[], # TT_TOKENTYPE_TEXT
[] # TT_TOKENTYPE_DECLARATION
];
$self->{_tokensTocEnd} = [
[], # TT_TOKENTYPE_START
[], # TT_TOKENTYPE_END
[], # TT_TOKENTYPE_COMMENT
[], # TT_TOKENTYPE_TEXT
[] # TT_TOKENTYPE_DECLARATION
];
# TRUE if ToCs have been initialized, FALSE if not.
$self->{_doneInitializeTocs} = 0;
# Array of ToCs to process
$self->{_tocs} = [];
# Active anchor name
$self->{_activeAnchorName} = undef;
} # _resetBatchVariables()
#--- HTML::TocGenerator::_resetStackVariables() -------------------------------
# function: Reset variables which cumulate during ToC generation.
sub _resetStackVariables {
# Get arguments
my ($self) = @_;
# Reset variables
$self->{levels} = undef;
$self->{groupIdLevels} = undef;
} # _resetStackVariables()
#--- HTML::TocGenerator::_setActiveAnchorName() -------------------------------
# function: Set active anchor name.
# args: - aAnchorName: Name of anchor name to set active.
sub _setActiveAnchorName {
# Get arguments
my ($self, $aAnchorName) = @_;
# Set active anchor name
$self->{_activeAnchorName} = $aAnchorName;
} # _setActiveAnchorName()
#--- HTML::TocGenerator::_showWarning() ---------------------------------------
# function: Show warning.
# args: - aWarningNr: Number of warning to show.
# - aWarningArgs: Arguments to display within the warning.
sub _showWarning {
# Get arguments
my ($self, $aWarningNr, $aWarningArgs) = @_;
# Local variables
my (%warnings);
# Set warnings
%warnings = (
WARNING_NESTED_ANCHOR_PS_WITHIN_PS() =>
"Nested anchor '%s' within anchor '%s'.",
WARNING_TOC_ATTRIBUTE_PS_NOT_AVAILABLE_WITHIN_PS() =>
"ToC attribute '%s' not available within token '%s'.",
);
# Show warning
print STDERR "warning ($aWarningNr): " . sprintf($warnings{"$aWarningNr"}, @$aWarningArgs) . "\n";
} # _showWarning()
#--- HTML::TocGenerator::anchorId() -------------------------------------------
# function: Anchor id processing method. Leave it up to the descendant to do
# something useful with it.
# args: - $aAnchorId
# - $aToc: Reference to ToC to which anchorId belongs.
sub anchorId {
} # anchorId()
#--- HTML::TocGenerator::anchorNameBegin() ------------------------------------
# function: Anchor name begin processing method. Leave it up to the descendant
# to do something useful with it.
# args: - $aAnchorName
# - $aToc: Reference to ToC to which anchorname belongs.
sub anchorNameBegin {
} # anchorNameBegin()
#--- HTML::TocGenerator::anchorNameEnd() --------------------------------------
# function: Anchor name end processing method. Leave it up to the descendant
# to do something useful with it.
# args: - $aAnchorName
# - $aToc: Reference to ToC to which anchorname belongs.
sub anchorNameEnd {
} # anchorNameEnd()
#--- HTML::TocGenerator::comment() --------------------------------------------
# function: Process comment.
# args: - $aComment: comment text with '<!--' and '-->' tags stripped off.
sub comment {
# Get arguments
my ($self, $aComment) = @_;
# Must a ToC be generated?
if ($self->{_doGenerateToc}) {
# Yes, a ToC must be generated
# Process end tag as ToC-starting-token
$self->_processTokenAsTocStartingToken(
TT_TOKENTYPE_COMMENT, $aComment, undef, \$aComment
);
# Process end tag as token which ends ToC registration
$self->_processTokenAsTocEndingToken(
TT_TOKENTYPE_COMMENT, $aComment
);
}
} # comment()
#--- HTML::TocGenerator::end() ------------------------------------------------
# function: This function is called every time a closing tag is encountered.
# args: - $aTag: tag name (in lower case).
# - $aOrigText: tag name including brackets.
sub end {
# Get arguments
my ($self, $aTag, $aOrigText) = @_;
# Local variables
my ($tag, $toc, $i);
# Must a ToC be generated?
if ($self->{_doGenerateToc}) {
# Yes, a ToC must be generated
# Process end tag as ToC-starting-token
$self->_processTokenAsTocStartingToken(
TT_TOKENTYPE_END, $aTag, undef, \$aOrigText
);
# Process end tag as ToC-ending-token
$self->_processTokenAsTocEndingToken(
TT_TOKENTYPE_END, $aTag
);
# Tag is of type 'anchor'?
if (defined($self->{_activeAnchorName}) && ($aTag eq "a")) {
# Yes, tag is of type 'anchor';
# Reset dirty anchor
$self->{_activeAnchorName} = undef;
}
}
} # end()
#--- HTML::TocGenerator::extend() ---------------------------------------------
# function: Extend ToCs.
# args: - $aTocs: Reference to array of ToC objects
# - $aString: String to parse.
sub extend {
# Get arguments
my ($self, $aTocs, $aString) = @_;
# Initialize TocGenerator batch
$self->_initializeExtenderBatch($aTocs);
# Extend ToCs
$self->_extend($aString);
# Deinitialize TocGenerator batch
$self->_deinitializeExtenderBatch();
} # extend()
#--- HTML::TocGenerator::extendFromFile() -------------------------------------
# function: Extend ToCs.
# args: - @aTocs: Reference to array of ToC objects
# - @aFiles: Reference to array of files to parse.
sub extendFromFile {
# Get arguments
my ($self, $aTocs, $aFiles) = @_;
# Initialize TocGenerator batch
$self->_initializeExtenderBatch($aTocs);
# Extend ToCs
$self->_extendFromFile($aFiles);
# Deinitialize TocGenerator batch
$self->_deinitializeExtenderBatch();
} # extendFromFile()
#--- HTML::TocGenerator::generate() -------------------------------------------
# function: Generate ToC.
# args: - $aToc: Reference to (array of) ToC object(s)
# - $aString: Reference to string to parse
# - $aOptions: optional options
sub generate {
# Get arguments
my ($self, $aToc, $aString, $aOptions) = @_;
# Initialize TocGenerator batch
$self->_initializeGeneratorBatch($aToc, $aOptions);
# Do generate ToC
$self->_generate($aString);
# Deinitialize TocGenerator batch
$self->_deinitializeGeneratorBatch();
} # generate()
#--- HTML::TocGenerator::generateFromFile() -----------------------------------
# function: Generate ToC.
# args: - $aToc: Reference to (array of) ToC object(s)
# - $aFile: (reference to array of) file to parse.
# - $aOptions: optional options
sub generateFromFile {
# Get arguments
my ($self, $aToc, $aFile, $aOptions) = @_;
# Initialize TocGenerator batch
$self->_initializeGeneratorBatch($aToc, $aOptions);
# Do generate ToC
$self->_generateFromFile($aFile);
# Deinitialize TocGenerator batch
$self->_deinitializeGeneratorBatch();
} # generateFromFile()
#--- HTML::TocGenerator::number() ---------------------------------------------
# function: Heading number processing method. Leave it up to the descendant
# to do something useful with it.
# args: - $aNumber
# - $aToc: Reference to ToC to which anchorname belongs.
sub number {
# Get arguments
my ($self, $aNumber, $aToc) = @_;
} # number()
#--- HTML::TocGenerator::parse() ----------------------------------------------
# function: Parse scalar.
# args: - $aString: string to parse
sub parse {
# Get arguments
my ($self, $aString) = @_;
# Call ancestor
$self->SUPER::parse($aString);
} # parse()
#--- HTML::TocGenerator::parse_file() -----------------------------------------
# function: Parse file.
sub parse_file {
# Get arguments
my ($self, $aFile) = @_;
# Call ancestor
$self->SUPER::parse_file($aFile);
} # parse_file()
#--- HTML::TocGenerator::setOptions() -----------------------------------------
# function: Set options.
# args: - aOptions: Reference to hash containing options.
sub setOptions {
# Get arguments
my ($self, $aOptions) = @_;
# Options are defined?
if (defined($aOptions)) {
# Yes, options are defined; add to options
%{$self->{options}} = (%{$self->{options}}, %$aOptions);
}
} # setOptions()
#--- HTML::TocGenerator::start() ----------------------------------------------
# function: This function is called every time an opening tag is encountered.
# args: - $aTag: tag name (in lower case).
# - $aAttr: reference to hash containing all tag attributes (in lower
# case).
# - $aAttrSeq: reference to array containing all tag attributes (in
# lower case) in the original order
# - $aOrigText: the original HTML text
sub start {
# Get arguments
my ($self, $aTag, $aAttr, $aAttrSeq, $aOrigText) = @_;
$self->{isTocToken} = 0;
# Start tag is of type 'anchor name'?
if ($aTag eq "a" && defined($aAttr->{name})) {
# Yes, start tag is of type 'anchor name';
# Is another anchor already active?
if (defined($self->{_activeAnchorName})) {
# Yes, another anchor is already active;
# Is the first anchor inserted by 'TocGenerator'?
if ($self->{_doOutputAnchorNameEnd}) {
# Yes, the first anchor is inserted by 'TocGenerator';
# Show warning
$self->_showWarning(
WARNING_NESTED_ANCHOR_PS_WITHIN_PS,
[$aOrigText, $self->{_activeAnchorName}]
);
}
}
# Set active anchor name
$self->_setActiveAnchorName($aAttr->{name});
}
# Must a ToC be generated?
if ($self->{_doGenerateToc}) {
# Yes, a ToC must be generated
# Process start tag as ToC token
$self->{isTocToken} = $self->_processTokenAsTocStartingToken(
TT_TOKENTYPE_START, $aTag, $aAttr, \$aOrigText
);
# Process end tag as ToC-ending-token
$self->_processTokenAsTocEndingToken(
TT_TOKENTYPE_START, $aTag
);
}
} # start()
#--- HTML::TocGenerator::text() -----------------------------------------------
# function: This function is called every time plain text is encountered.
# args: - @_: array containing data.
sub text {
# Get arguments
my ($self, $aText) = @_;
# Local variables
my ($text, $toc, $i, $token, $tokens);
# Must a ToC be generated?
if ($self->{_doGenerateToc}) {
# Yes, a ToC must be generated
# Are there dirty start tags?
# Loop through token types
foreach $tokens (@{$self->{_tokensTocEnd}}) {
# Loop though tokens
foreach $token (@$tokens) {
# Add text to toc
# Alias
$toc = $token->[TT_TOC];
# Remove possible newlines from text
($text = $aText) =~ s/\s*\n\s*/ /g;
# Add text to toc
$self->_processTocText($text, $toc);
}
}
}
} # text()
#=== HTML::_TokenTocParser ====================================================
# function: Parse 'toc tokens'. 'Toc tokens' mark HTML code which is to be
# inserted into the ToC.
# note: Used internally.
package HTML::_TokenTocParser;
BEGIN {
use vars qw(@ISA);
@ISA = qw(HTML::Parser);
}
END {}
#--- HTML::_TokenTocParser::new() ---------------------------------------------
# function: Constructor
sub new {
# Get arguments
my ($aType) = @_;
# Create instance
my $self = $aType->SUPER::new;
# Return instance
return $self;
} # new()
#--- HTML::_TokenTocParser::_parseAttributes() --------------------------------
# function: Parse attributes.
# args: - $aAttr: Reference to hash containing all tag attributes (in lower
# case).
# - $aIncludeAttributes: Reference to hash to which 'include
# attributes' must be added.
# - $aExcludeAttributes: Reference to hash to which 'exclude
# attributes' must be added.
# - $aTocAttributes: Reference to hash to which 'ToC attributes'
# must be added.
sub _parseAttributes {
# Get arguments
my (
$self, $aAttr, $aIncludeAttributes, $aExcludeAttributes,
$aTocAttributes
) = @_;
# Local variables
my ($key, $value);
my ($attributeToExcludeToken, $attributeToTocToken);
# Get token which marks attributes which must be excluded
$attributeToExcludeToken = $self->{_toc}{options}{'attributeToExcludeToken'};
$attributeToTocToken = $self->{_toc}{options}{'attributeToTocToken'};
# Loop through attributes
while (($key, $value) = each %$aAttr) {
# Attribute value equals 'ToC token'?
if ($value =~ m/$attributeToTocToken/) {
# Yes, attribute value equals 'ToC token';
# Add attribute to 'ToC attributes'
push @$aTocAttributes, $key;
}
else {
# No, attribute isn't 'ToC' token;
# Attribute value starts with 'exclude token'?
if ($value =~ m/^$attributeToExcludeToken(.*)/) {
# Yes, attribute value starts with 'exclude token';
# Add attribute to 'exclude attributes'
$$aExcludeAttributes{$key} = "$1";
}
else {
# No, attribute key doesn't start with '-';
# Add attribute to 'include attributes'
$$aIncludeAttributes{$key} = $value;
}
}
}
} # _parseAttributes()
#=== HTML::_TokenTocBeginParser ===============================================
# function: Parse 'toc tokens'. 'Toc tokens' mark HTML code which is to be
# inserted into the ToC.
# note: Used internally.
package HTML::_TokenTocBeginParser;
BEGIN {
use vars qw(@ISA);
@ISA = qw(HTML::_TokenTocParser);
}
END {}
#--- HTML::_TokenTocBeginParser::new() ----------------------------------------
# function: Constructor
sub new {
# Get arguments
my ($aType, $aTokenArray) = @_;
# Create instance
my $self = $aType->SUPER::new;
# Reference token array
$self->{tokens} = $aTokenArray;
# Reference to last added token
$self->{_lastAddedToken} = undef;
$self->{_lastAddedTokenType} = undef;
# Return instance
return $self;
} # new()
#--- HTML::_TokenTocBeginParser::_processAttributes() -------------------------
# function: Process attributes.
# args: - $aAttributes: Attributes to parse.
sub _processAttributes {
# Get arguments
my ($self, $aAttributes) = @_;
# Local variables
my (%includeAttributes, %excludeAttributes, @tocAttributes);
# Parse attributes
$self->_parseAttributes(
$aAttributes, \%includeAttributes, \%excludeAttributes, \@tocAttributes
);
# Include attributes are specified?
if (keys(%includeAttributes) > 0) {
# Yes, include attributes are specified;
# Store include attributes
@${$self->{_lastAddedToken}}[
HTML::TocGenerator::TT_INCLUDE_ATTRIBUTES_BEGIN
] = \%includeAttributes;
}
# Exclude attributes are specified?
if (keys(%excludeAttributes) > 0) {
# Yes, exclude attributes are specified;
# Store exclude attributes
@${$self->{_lastAddedToken}}[
HTML::TocGenerator::TT_EXCLUDE_ATTRIBUTES_BEGIN
] = \%excludeAttributes;
}
# Toc attributes are specified?
if (@tocAttributes > 0) {
# Yes, toc attributes are specified;
# Store toc attributes
@${$self->{_lastAddedToken}}[
HTML::TocGenerator::TT_ATTRIBUTES_TOC
] = \@tocAttributes;
}
} # _processAttributes()
#--- HTML::_TokenTocBeginParser::_processToken() ------------------------------
# function: Process token.
# args: - $aTokenType: Type of token to process.
# - $aTag: Tag of token.
sub _processToken {
# Get arguments
my ($self, $aTokenType, $aTag) = @_;
# Local variables
my ($tokenArray, $index);
# Push element on array of update tokens
$index = push(@{$self->{tokens}[$aTokenType]}, []) - 1;
# Alias token array to add element to
$tokenArray = $self->{tokens}[$aTokenType];
# Indicate last updated token array element
$self->{_lastAddedTokenType} = $aTokenType;
$self->{_lastAddedToken} = \$$tokenArray[$index];
# Add fields
$$tokenArray[$index][HTML::TocGenerator::TT_TAG_BEGIN] = $aTag;
$$tokenArray[$index][HTML::TocGenerator::TT_GROUP] = $self->{_group};
$$tokenArray[$index][HTML::TocGenerator::TT_TOC] = $self->{_toc};
} # _processToken()
#--- HTML::_TokenTocBeginParser::comment() ------------------------------------
# function: Process comment.
# args: - $aComment: comment text with '<!--' and '-->' tags stripped off.
sub comment {
# Get arguments
my ($self, $aComment) = @_;
# Process token
$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_COMMENT, $aComment);
} # comment()
#--- HTML::_TokenTocBeginParser::declaration() --------------------------------
# function: This function is called every time a markup declaration is
# encountered by HTML::Parser.
# args: - $aDeclaration: Markup declaration.
sub declaration {
# Get arguments
my ($self, $aDeclaration) = @_;
# Process token
$self->_processToken(
HTML::TocGenerator::TT_TOKENTYPE_DECLARATION, $aDeclaration
);
} # declaration()
#--- HTML::_TokenTocBeginParser::end() ----------------------------------------
# function: This function is called every time a closing tag is encountered
# by HTML::Parser.
# args: - $aTag: tag name (in lower case).
sub end {
# Get arguments
my ($self, $aTag, $aOrigText) = @_;
# Process token
$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_END, $aTag);
} # end()
#--- HTML::_TokenTocBeginParser::parse() --------------------------------------
# function: Parse begin token.
# args: - $aToken: 'toc token' to parse
sub parse {
# Get arguments
my ($self, $aString) = @_;
# Call ancestor
$self->SUPER::parse($aString);
} # parse()
#--- HTML::_TokenTocBeginParser->setGroup() -----------------------------------
# function: Set current 'tokenToToc' group.
sub setGroup {
# Get arguments
my ($self, $aGroup) = @_;
# Set current 'tokenToToc' group
$self->{_group} = $aGroup;
} # setGroup()
#--- HTML::_TokenTocBeginParser->setToc() -------------------------------------
# function: Set current ToC.
sub setToc {
# Get arguments
my ($self, $aToc) = @_;
# Set current ToC
$self->{_toc} = $aToc;
} # setToc()
#--- HTML::_TokenTocBeginParser::start() --------------------------------------
# function: This function is called every time an opening tag is encountered.
# args: - $aTag: tag name (in lower case).
# - $aAttr: reference to hash containing all tag attributes (in lower
# case).
# - $aAttrSeq: reference to array containing all attribute keys (in
# lower case) in the original order
# - $aOrigText: the original HTML text
sub start {
# Get arguments
my ($self, $aTag, $aAttr, $aAttrSeq, $aOrigText) = @_;
# Process token
$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_START, $aTag);
# Process attributes
$self->_processAttributes($aAttr);
} # start()
#--- HTML::_TokenTocBeginParser::text() ---------------------------------------
# function: This function is called every time plain text is encountered.
# args: - @_: array containing data.
sub text {
# Get arguments
my ($self, $aText) = @_;
# Was token already created and is last added token of type 'text'?
if (
defined($self->{_lastAddedToken}) &&
$self->{_lastAddedTokenType} == HTML::TocGenerator::TT_TOKENTYPE_TEXT
) {
# Yes, token is already created;
# Add tag to existing token
@${$self->{_lastAddedToken}}[HTML::TocGenerator::TT_TAG_BEGIN] .= $aText;
}
else {
# No, token isn't created;
# Process token
$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_TEXT, $aText);
}
} # text()
#=== HTML::_TokenTocEndParser =================================================
# function: Parse 'toc tokens'. 'Toc tokens' mark HTML code which is to be
# inserted into the ToC.
# note: Used internally.
package HTML::_TokenTocEndParser;
BEGIN {
use vars qw(@ISA);
@ISA = qw(HTML::_TokenTocParser);
}
END {}
#--- HTML::_TokenTocEndParser::new() ------------------------------------------
# function: Constructor
# args: - $aType: Class type.
sub new {
# Get arguments
my ($aType) = @_;
# Create instance
my $self = $aType->SUPER::new;
# Reference to last added token
$self->{_lastAddedToken} = undef;
# Return instance
return $self;
} # new()
#--- HTML::_TokenTocEndParser::_processAttributes() ---------------------------
# function: Process attributes.
# args: - $aAttributes: Attributes to parse.
sub _processAttributes {
# Get arguments
my ($self, $aAttributes) = @_;
# Local variables
my (%includeAttributes, %excludeAttributes);
# Parse attributes
$self->_parseAttributes(
$aAttributes, \%includeAttributes, \%excludeAttributes
);
# Include attributes are specified?
if (keys(%includeAttributes) > 0) {
# Yes, include attributes are specified;
# Store include attributes
@${$self->{_Token}}[
HTML::TocGenerator::TT_INCLUDE_ATTRIBUTES_END
] = \%includeAttributes;
}
# Exclude attributes are specified?
if (keys(%excludeAttributes) > 0) {
# Yes, exclude attributes are specified;
# Store exclude attributes
@${$self->{_Token}}[
HTML::TocGenerator::TT_EXCLUDE_ATTRIBUTES_END
] = \%excludeAttributes;
}
} # _processAttributes()
#--- HTML::_TokenTocEndParser::_processToken() --------------------------------
# function: Process token.
# args: - $aTokenType: Type of token to process.
# - $aTag: Tag of token.
sub _processToken {
# Get arguments
my ($self, $aTokenType, $aTag) = @_;
# Update token
@${$self->{_token}}[HTML::TocGenerator::TT_TAG_TYPE_END] = $aTokenType;
@${$self->{_token}}[HTML::TocGenerator::TT_TAG_END] = $aTag;
# Indicate token type which has been processed
$self->{_lastAddedTokenType} = $aTokenType;
} # _processToken()
#--- HTML::_TokenTocEndParser::comment() --------------------------------------
# function: Process comment.
# args: - $aComment: comment text with '<!--' and '-->' tags stripped off.
sub comment {
# Get arguments
my ($self, $aComment) = @_;
# Process token
$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_COMMENT, $aComment);
} # comment()
#--- HTML::_TokenTocDeclarationParser::declaration() --------------------------
# function: This function is called every time a markup declaration is
# encountered by HTML::Parser.
# args: - $aDeclaration: Markup declaration.
sub declaration {
# Get arguments
my ($self, $aDeclaration) = @_;
# Process token
$self->_processToken(
HTML::TocGenerator::TT_TOKENTYPE_DECLARATION, $aDeclaration
);
} # declaration()
#--- HTML::_TokenTocEndParser::end() ------------------------------------------
# function: This function is called every time a closing tag is encountered
# by HTML::Parser.
# args: - $aTag: tag name (in lower case).
sub end {
# Get arguments
my ($self, $aTag, $aOrigText) = @_;
# Process token
$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_END, $aTag);
} # end()
#--- HTML::_TokenTocEndParser::parse() ----------------------------------------
# function: Parse token.
# args: - $aString: 'toc token' to parse
# - $aToken: Reference to token
# - $aTokenTypeBegin: Type of begin token
sub parse {
# Get arguments
my ($self, $aString, $aToken, $aTokenTypeBegin) = @_;
# Token argument specified?
if (defined($aToken)) {
# Yes, token argument is specified;
# Store token reference
$self->{_token} = $aToken;
}
# End tag defined?
if (! defined($aString)) {
# No, end tag isn't defined;
# Last added tokentype was of type 'start'?
if (
(defined($aTokenTypeBegin)) &&
($aTokenTypeBegin == HTML::TocGenerator::TT_TOKENTYPE_START)
) {
# Yes, last added tokentype was of type 'start';
# Assume end tag
$self->_processToken(
HTML::TocGenerator::TT_TAG_END,
@${$self->{_token}}[HTML::TocGenerator::TT_TAG_BEGIN]
);
}
}
else {
# Call ancestor
$self->SUPER::parse($aString);
}
} # parse()
#--- HTML::_TokenTocEndParser::start() ----------------------------------------
# function: This function is called every time an opening tag is encountered.
# args: - $aTag: tag name (in lower case).
# - $aAttr: reference to hash containing all tag attributes (in lower
# case).
# - $aAttrSeq: reference to array containing all attribute keys (in
# lower case) in the original order
# - $aOrigText: the original HTML text
sub start {
# Get arguments
my ($self, $aTag, $aAttr, $aAttrSeq, $aOrigText) = @_;
# Process token
$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_START, $aTag);
# Process attributes
$self->_processAttributes($aAttr);
} # start()
#--- HTML::_TokenTocEndParser::text() -----------------------------------------
# function: This function is called every time plain text is encountered.
# args: - @_: array containing data.
sub text {
# Get arguments
my ($self, $aText) = @_;
# Is token already created?
if (defined($self->{_lastAddedTokenType})) {
# Yes, token is already created;
# Add tag to existing token
@${$self->{_token}}[HTML::TocGenerator::TT_TAG_END] .= $aText;
}
else {
# No, token isn't created;
# Process token
$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_TEXT, $aText);
}
} # text()
1;