| #=== 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  "'; |
| |
| |
| 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; |