Home | History | Annotate | Download | only in HTML-Toc-0.91
      1 #=== HTML::TocGenerator =======================================================
      2 # function: Generate 'HTML::Toc' table of contents.
      3 # note:     - 'TT' is an abbrevation of 'TocToken'.
      4 
      5 
      6 package HTML::TocGenerator;
      7 
      8 
      9 use strict;
     10 use HTML::Parser;
     11 
     12 
     13 BEGIN {
     14 	use vars qw(@ISA $VERSION);
     15 
     16 	$VERSION = '0.91';
     17 
     18 	@ISA = qw(HTML::Parser);
     19 }
     20 
     21 
     22 	# Warnings
     23 use constant WARNING_NESTED_ANCHOR_PS_WITHIN_PS               => 1;
     24 use constant WARNING_TOC_ATTRIBUTE_PS_NOT_AVAILABLE_WITHIN_PS => 2;
     25 
     26 
     27 use constant TOC_TOKEN_ID       => 0;
     28 use constant TOC_TOKEN_INCLUDE  => 1;
     29 use constant TOC_TOKEN_EXCLUDE  => 2;
     30 use constant TOC_TOKEN_TOKENS   => 3;
     31 use constant TOC_TOKEN_GROUP    => 4;
     32 use constant TOC_TOKEN_TOC      => 5;
     33 
     34 	# Token types
     35 use constant TT_TAG_BEGIN                => 0;
     36 use constant TT_TAG_END                  => 1;
     37 use constant TT_TAG_TYPE_END             => 2;
     38 use constant TT_INCLUDE_ATTRIBUTES_BEGIN => 3;
     39 use constant TT_EXCLUDE_ATTRIBUTES_BEGIN => 4;
     40 use constant TT_INCLUDE_ATTRIBUTES_END   => 5;
     41 use constant TT_EXCLUDE_ATTRIBUTES_END   => 6;
     42 use constant TT_GROUP                    => 7;
     43 use constant TT_TOC                      => 8;
     44 use constant TT_ATTRIBUTES_TOC           => 9;
     45 
     46 
     47 use constant CONTAINMENT_INCLUDE => 0;
     48 use constant CONTAINMENT_EXCLUDE => 1;
     49 
     50 use constant TEMPLATE_ANCHOR            => '$groupId."-".$node';
     51 use constant TEMPLATE_ANCHOR_HREF       => 
     52 					'"<a href=#".' . TEMPLATE_ANCHOR . '.">"';
     53 use constant TEMPLATE_ANCHOR_HREF_FILE  => 
     54 					'"<a href=".$file."#".' . TEMPLATE_ANCHOR . '.">"';
     55 use constant TEMPLATE_ANCHOR_NAME       => 
     56 					'"<a name=".' . TEMPLATE_ANCHOR . '.">"';
     57 
     58 use constant TEMPLATE_TOKEN_NUMBER      => '"$node &nbsp"';
     59 
     60 
     61 use constant TT_TOKENTYPE_START        => 0;
     62 use constant TT_TOKENTYPE_END          => 1;
     63 use constant TT_TOKENTYPE_TEXT         => 2;
     64 use constant TT_TOKENTYPE_COMMENT      => 3;
     65 use constant TT_TOKENTYPE_DECLARATION  => 4;
     66 
     67 
     68 END {}
     69 
     70 
     71 #--- HTML::TocGenerator::new() ------------------------------------------------
     72 # function: Constructor
     73 
     74 sub new {
     75 		# Get arguments
     76 	my ($aType) = @_;
     77 	my $self = $aType->SUPER::new;
     78 		# Bias to not generate ToC
     79 	$self->{_doGenerateToc} = 0;
     80 		# Bias to not use global groups
     81 	$self->{_doUseGroupsGlobal} = 0;
     82 		# Output
     83 	$self->{output} = "";
     84 		# Reset internal variables
     85 	$self->_resetBatchVariables();
     86 
     87 	$self->{options} = {};
     88 
     89 	return $self;
     90 }  # new()
     91 
     92 
     93 #--- HTML::TocGenerator::_deinitializeBatch() ---------------------------------
     94 
     95 sub _deinitializeBatch() {
     96 		# Get arguments
     97 	my ($self) = @_;
     98 }  # _deinitializeBatch()
     99 
    100 
    101 #--- HTML::TocGenerator::_deinitializeExtenderBatch() -------------------------
    102 
    103 sub _deinitializeExtenderBatch() {
    104 		# Get arguments
    105 	my ($self) = @_;
    106 		# Do general batch deinitialization
    107 	$self->_deinitializeBatch();
    108 		# Indicate end of ToC generation
    109 	$self->{_doGenerateToc} = 0;
    110 		# Reset batch variables
    111 	$self->_resetBatchVariables();
    112 }  # _deinitializeExtenderBatch()
    113 
    114 
    115 #--- HTML::TocGenerator::_deinitializeGeneratorBatch() ------------------------
    116 
    117 sub _deinitializeGeneratorBatch() {
    118 		# Get arguments
    119 	my ($self) = @_;
    120 		# Do 'extender' batch deinitialization
    121 	$self->_deinitializeExtenderBatch();
    122 }  # _deinitializeBatchGenerator()
    123 
    124 
    125 #--- HTML::TocGenerator::_doesHashContainHash() -------------------------------
    126 # function: Determines whether hash1 matches regular expressions of hash2.
    127 # args:     - $aHash1
    128 #           - $aHash2
    129 #           - $aContainmentType: 0 (include) or 1 (exclude)
    130 # returns:  True (1) if hash1 satisfies hash2, 0 if not.  For example, with the
    131 #           following hashes:
    132 #
    133 #              %hash1 = {							%hash2 = {
    134 #                 'class' => 'header'				'class' => '^h'
    135 #                 'id'    => 'intro'         }
    136 #              }
    137 #
    138 #           the routine will return 1 if 'aContainmentType' equals 0, cause
    139 #           'hash1' satisfies the conditions of 'hash2'.  The routine will
    140 #           return 0 if 'aContainmentType' equals 1, cause 'hash1' doesn't
    141 #           exclude the conditions of 'hash2'.
    142 # note:     Class function.
    143 
    144 sub _doesHashContainHash {
    145 		# Get arguments
    146 	my ($aHash1, $aHash2, $aContainmentType) = @_;
    147 		# Local variables
    148 	my ($key1, $value1, $key2, $value2, $result);
    149 		# Bias to success
    150 	$result = 1;
    151 		# Loop through hash2
    152 	HASH2: while (($key2, $value2) = each %$aHash2) {
    153 		# Yes, values are available;
    154 			# Get value1
    155 		$value1 = $aHash1->{$key2};
    156 			# Does value1 match criteria of value2?
    157 		if (defined($value1) && $value1 =~ m/$value2/) {
    158 			# Yes, value1 matches criteria of value2;
    159 				# Containment type was exclude?
    160 			if ($aContainmentType == CONTAINMENT_EXCLUDE) {
    161 				# Yes, containment type was exclude;
    162 					# Indicate condition fails
    163 				$result = 0;
    164 					# Reset 'each' iterator which we're going to break
    165 				keys %$aHash2;
    166 					# Break loop
    167 				last HASH2;
    168 			}
    169 		}
    170 		else {
    171 			# No, value1 didn't match criteria of value2;
    172 				# Containment type was include?
    173 			if ($aContainmentType == CONTAINMENT_INCLUDE) {
    174 				# Yes, containment type was include;
    175 					# Indicate condition fails
    176 				$result = 0;
    177 					# Reset 'each' iterator which we're going to break
    178 				keys %$aHash2;
    179 					# Break loop
    180 				last HASH2;
    181 			}
    182 		}
    183 	}
    184 		# Return value
    185 	return $result;
    186 }  # _doesHashContainHash()
    187 
    188 
    189 #--- HTML::TocGenerator::_extend() --------------------------------------------
    190 # function: Extend ToC.
    191 #           - $aString: String to parse.
    192 
    193 sub _extend {
    194 		# Get arguments
    195 	my ($self, $aFile) = @_;
    196 		# Local variables
    197 	my ($file);
    198 		# Parse string
    199 	$self->parse($aFile);
    200 		# Flush remaining buffered text
    201 	$self->eof();
    202 }  # _extend()
    203 
    204 
    205 #--- HTML::TocGenerator::_extendFromFile() ------------------------------------
    206 # function: Extend ToC.
    207 #           - $aFile: (reference to array of) file to parse.
    208 
    209 sub _extendFromFile {
    210 		# Get arguments
    211 	my ($self, $aFile) = @_;
    212 		# Local variables
    213 	my ($file, @files);
    214 		# Dereference array reference or make array of file specification
    215 	@files = (ref($aFile) =~ m/ARRAY/) ? @$aFile : ($aFile);
    216 		# Loop through files
    217 	foreach $file (@files) {
    218 			# Store filename
    219 		$self->{_currentFile} = $file;
    220 			# Parse file
    221 		$self->parse_file($file);
    222 			# Flush remaining buffered text
    223 		$self->eof();
    224 	}
    225 }  # _extendFromFile()
    226 
    227 
    228 #--- HTML::TocGenerator::_formatHeadingLevel() --------------------------------
    229 # function: Format heading level.
    230 # args:     - $aLevel: Level of current heading
    231 #           - $aClass: Class of current heading
    232 #           - $aGroup: Group of current heading
    233 #           - $aToc: Toc of current heading
    234 
    235 sub _formatHeadingLevel {
    236 		# Get arguments
    237 	my ($self, $aLevel, $aClass, $aGroup, $aToc) = @_;
    238 		# Local variables
    239 	my ($result, $headingNumber, $numberingStyle);
    240 
    241 	$headingNumber = $self->_getGroupIdManager($aToc)->
    242 		{levels}{$aClass}[$aLevel - 1] || 0;
    243 
    244 		# Alias numbering style of current group
    245 	$numberingStyle = $aGroup->{numberingStyle};
    246 
    247 	SWITCH: {
    248 		if ($numberingStyle eq "decimal") {
    249 			$result = $headingNumber;
    250 			last SWITCH;
    251 		}
    252 		if ($numberingStyle eq "lower-alpha") {
    253 			$result = chr($headingNumber + ord('a') - 1);
    254 			last SWITCH;
    255 		}
    256 		if ($numberingStyle eq "upper-alpha") {
    257 			$result = chr($headingNumber + ord('A') - 1);
    258 			last SWITCH;
    259 		}
    260 		if ($numberingStyle eq "lower-roman") {
    261 			require Roman;
    262 			$result = Roman::roman($headingNumber);
    263 			last SWITCH;
    264 		}
    265 		if ($numberingStyle eq "upper-roman") {
    266 			require Roman;
    267 			$result = Roman::Roman($headingNumber);
    268 			last SWITCH;
    269 		}
    270 		die "Unknown case: $numberingStyle";
    271 	}
    272 		# Return value
    273 	return $result;
    274 }	# _formatHeadingLevel()
    275 
    276 
    277 #--- HTML::TocGenerator::_formatTocNode() -------------------------------------
    278 # function: Format heading node.
    279 # args:     - $aLevel: Level of current heading
    280 #           - $aClass: Class of current heading
    281 #           - $aGroup: Group of current heading
    282 #           - $aToc: Toc of current heading
    283 
    284 sub _formatTocNode {
    285 		# Get arguments
    286 	my ($self, $aLevel, $aClass, $aGroup, $aToc) = @_;
    287 		# Local variables
    288 	my ($result, $level, $levelGroups);
    289 
    290 		# Alias 'levelGroups' of right 'groupId'
    291 	$levelGroups = $aToc->{_levelGroups}{$aGroup->{'groupId'}};
    292 		# Loop through levels
    293 	for ($level = 1; $level <= $aLevel; $level++) {
    294 			# If not first level, add dot
    295 		$result = ($result ? $result . "." : $result);
    296 			# Format heading level using argument group
    297 		$result .= $self->_formatHeadingLevel(
    298 			$level, $aClass, @{$levelGroups}[$level - 1], $aToc
    299 		);
    300 	}
    301 		# Return value
    302 	return $result;
    303 }  # _formatTocNode()
    304      	
    305      	
    306 #--- HTML::TocGenerator::_generate() ------------------------------------------
    307 # function: Generate ToC.
    308 # args:     - $aString: Reference to string to parse
    309 
    310 sub _generate {
    311 		# Get arguments
    312 	my ($self, $aString) = @_;
    313 		# Local variables
    314 	my ($toc);
    315 		# Loop through ToCs
    316 	foreach $toc (@{$self->{_tocs}}) {
    317 			# Clear ToC
    318 		$toc->clear();
    319 	}
    320 		# Extend ToCs
    321 	$self->_extend($aString);
    322 }  # _generate()
    323 
    324 
    325 #--- HTML::TocGenerator::_generateFromFile() ----------------------------------
    326 # function: Generate ToC.
    327 # args:     - $aFile: (reference to array of) file to parse.
    328 
    329 sub _generateFromFile {
    330 		# Get arguments
    331 	my ($self, $aFile) = @_;
    332 		# Local variables
    333 	my ($toc);
    334 		# Loop through ToCs
    335 	foreach $toc (@{$self->{_tocs}}) {
    336 			# Clear ToC
    337 		$toc->clear();
    338 	}
    339 		# Extend ToCs
    340 	$self->_extendFromFile($aFile);
    341 }  # _generateFromFile()
    342 
    343 
    344 #--- HTML::TocGenerator::_getGroupIdManager() ---------------------------------
    345 # function: Get group id manager.
    346 # args:     - $aToc: Active ToC.
    347 # returns:  Group id levels.
    348 
    349 sub _getGroupIdManager {
    350 		# Get arguments
    351 	my ($self, $aToc) = @_;
    352 		# Local variables
    353 	my ($result);
    354 		# Global groups?
    355 	if ($self->{options}{'doUseGroupsGlobal'}) {
    356 		# Yes, global groups;
    357 		$result = $self;
    358 	}
    359 	else {
    360 		# No, local groups;
    361 		$result = $aToc;
    362 	}
    363 		# Return value
    364 	return $result;
    365 }  # _getGroupIdManager()
    366 
    367 
    368 #--- HTML::TocGenerator::_initializeBatch() -----------------------------------
    369 # function: Initialize batch.  This function is called once when a parse batch
    370 #           is started.
    371 # args:     - $aTocs: Reference to array of tocs.
    372 
    373 sub _initializeBatch {
    374 		# Get arguments
    375 	my ($self, $aTocs) = @_;
    376 		# Local variables
    377 	my ($toc);
    378 
    379 		# Store reference to tocs
    380 		
    381 		# Is ToC specification reference to array?
    382 	if (ref($aTocs) =~ m/ARRAY/) {
    383 		# Yes, ToC specification is reference to array;
    384 			# Store array reference
    385 		$self->{_tocs} = $aTocs;
    386 	}
    387 	else {
    388 		# No, ToC specification is reference to ToC object;
    389 			# Wrap reference in array reference, containing only one element
    390 		$self->{_tocs} = [$aTocs];
    391 	}
    392 		# Loop through ToCs
    393 	foreach $toc (@{$self->{_tocs}}) {
    394 			# Parse ToC options
    395 		$toc->parseOptions();
    396 	}
    397 }  # _initializeBatch()
    398 
    399 
    400 #--- HTML::TocGenerator::_initializeExtenderBatch() --------------------------
    401 # function: Initialize 'extender' batch.  This function is called once when a 
    402 #           parse batch is started.
    403 # args:     - $aTocs: Reference to array of tocs.
    404 
    405 sub _initializeExtenderBatch {
    406 		# Get arguments
    407 	my ($self, $aTocs) = @_;
    408 		# Do general batch initialization
    409 	$self->_initializeBatch($aTocs);
    410 		# Parse ToC options
    411 	$self->_parseTocOptions();
    412 		# Indicate start of batch
    413 	$self->{_doGenerateToc} = 1;
    414 }  # _initializeExtenderBatch()
    415 
    416 
    417 #--- HTML::TocGenerator::_initializeGeneratorBatch() --------------------------
    418 # function: Initialize generator batch.  This function is called once when a 
    419 #           parse batch is started.
    420 # args:     - $aTocs: Reference to array of tocs.
    421 #           - $aOptions: optional options
    422 
    423 sub _initializeGeneratorBatch {
    424 		# Get arguments
    425 	my ($self, $aTocs, $aOptions) = @_;
    426 		# Add invocation options
    427 	$self->setOptions($aOptions);
    428 		# Option 'doUseGroupsGlobal' specified?
    429 	if (!defined($self->{options}{'doUseGroupsGlobal'})) {
    430 		# No, options 'doUseGroupsGlobal' not specified;
    431 			# Default to no 'doUseGroupsGlobal'
    432 		$self->{options}{'doUseGroupsGlobal'} = 0;
    433 	}
    434 		# Global groups?
    435 	if ($self->{options}{'doUseGroupsGlobal'}) {
    436 		# Yes, global groups;
    437 			# Reset groups and levels
    438 		$self->_resetStackVariables();
    439 	}
    440 		# Do 'extender' batch initialization
    441 	$self->_initializeExtenderBatch($aTocs);
    442 }  # _initializeGeneratorBatch()
    443 
    444 
    445 #--- HTML::TocGenerator::_linkTocToToken() ------------------------------------
    446 # function: Link ToC to token.
    447 # args:     - $aToc: ToC to add token to.
    448 #           - $aFile
    449 #           - $aGroupId
    450 #           - $aLevel
    451 #           - $aNode
    452 #           - $aGroupLevel
    453 #           - $aLinkType
    454 #           - $aTokenAttributes: reference to hash containing attributes of 
    455 #                currently parsed token
    456 
    457 sub _linkTocToToken {
    458 		# Get arguments
    459 	my (
    460 		$self, $aToc, $aFile, $aGroupId, $aLevel, $aNode, $aGroupLevel, 
    461 		$aDoLinkToId, $aTokenAttributes
    462 	) = @_;
    463 		# Local variables
    464 	my ($file, $groupId, $level, $node, $anchorName);
    465 	my ($doInsertAnchor, $doInsertId);
    466 
    467 		# Fill local arguments to be used by templates
    468 	$file    = $aFile;
    469 	$groupId = $aGroupId;
    470 	$level   = $aLevel;
    471 	$node    = $aNode;
    472 	
    473 		# Assemble anchor name
    474 	$anchorName = 
    475 		ref($aToc->{_templateAnchorName}) eq "CODE" ?
    476 			&{$aToc->{_templateAnchorName}}(
    477 				$aFile, $aGroupId, $aLevel, $aNode
    478 			) : 
    479 			eval($aToc->{_templateAnchorName});
    480 
    481 		# Bias to insert anchor name
    482 	$doInsertAnchor = 1;
    483 	$doInsertId     = 0;
    484 		# Link to 'id'?
    485 	if ($aDoLinkToId) {
    486 		# Yes, link to 'id';
    487 			# Indicate to insert anchor id
    488 		$doInsertAnchor = 0;
    489 		$doInsertId     = 1;
    490 			# Id attribute is available?
    491 		if (defined($aTokenAttributes->{id})) {
    492 			# Yes, id attribute is available;
    493 				# Use existing ids?
    494 			if ($aToc->{options}{'doUseExistingIds'}) {
    495 				# Yes, use existing ids;
    496 					# Use existing id
    497 				$anchorName = $aTokenAttributes->{id};
    498 					# Indicate to not insert id
    499 				$doInsertId = 0;
    500 			}
    501 		}
    502 
    503 	}
    504 	else {
    505 		# No, link to 'name';
    506 			# Anchor name is currently active?
    507 		if (defined($self->{_activeAnchorName})) {
    508 			# Yes, anchor name is currently active;
    509 				# Use existing anchors?
    510 			if ($aToc->{options}{'doUseExistingAnchors'}) {
    511 				# Yes, use existing anchors;
    512 					# Use existing anchor name
    513 				$anchorName = $self->{_activeAnchorName};
    514 					# Indicate to not insert anchor name
    515 				$doInsertAnchor = 0;
    516 			}
    517 			else {
    518 				# No, don't use existing anchors; insert new anchor;
    519 					# 
    520 			}
    521 		}
    522 	}
    523 
    524 		# Add reference to ToC
    525 	$aToc->{_toc} .= 
    526 		ref($aToc->{_templateAnchorHrefBegin}) eq "CODE" ?
    527 			&{$aToc->{_templateAnchorHrefBegin}}(
    528 				$aFile, $aGroupId, $aLevel, $aNode, $anchorName
    529 			) : 
    530 			eval($aToc->{_templateAnchorHrefBegin});
    531 
    532 		# Bias to not output anchor name end
    533 	$self->{_doOutputAnchorNameEnd} = 0;
    534 		# Must anchor be inserted?
    535 	if ($doInsertAnchor) {
    536 		# Yes, anchor must be inserted;
    537 			# Allow adding of anchor name begin token to text by calling 
    538 			# 'anchorNameBegin' method
    539 		$self->anchorNameBegin(
    540 			ref($aToc->{_templateAnchorNameBegin}) eq "CODE" ?
    541 				&{$aToc->{_templateAnchorNameBegin}}(
    542 					$aFile, $aGroupId, $aLevel, $aNode, $anchorName
    543 				) :
    544 				eval($aToc->{_templateAnchorNameBegin}),
    545 			$aToc
    546 		);
    547 	}
    548 
    549 		# Must anchorId attribute be inserted?
    550 	if ($doInsertId) {
    551 		# Yes, anchorId attribute must be inserted;
    552 			# Allow adding of anchorId attribute to text by calling 'anchorId'
    553 			# method
    554 		$self->anchorId($anchorName);
    555 	}
    556 }  # _linkTocToToken()
    557 
    558 
    559 #--- HTML::TocGenerator::_outputAnchorNameEndConditionally() ------------------
    560 # function: Output 'anchor name end' if necessary
    561 # args:     - $aToc: ToC of which 'anchor name end' must be output.
    562 
    563 sub _outputAnchorNameEndConditionally {
    564 		# Get arguments
    565 	my ($self, $aToc) = @_;
    566 		# Must anchor name end be output?
    567 	if ($self->{_doOutputAnchorNameEnd}) {
    568 		# Yes, output anchor name end;
    569 			# Allow adding of anchor to text by calling 'anchorNameEnd' 
    570 			# method
    571 		$self->anchorNameEnd(
    572 			ref($aToc->{_templateAnchorNameEnd}) eq "CODE" ?
    573 				&{$aToc->{_templateAnchorNameEnd}} :
    574 				eval($aToc->{_templateAnchorNameEnd}),
    575 			$aToc
    576 		);
    577 	}
    578 }  # _outputAnchorNameEndConditionally()
    579 
    580 
    581 #--- HTML::TocGenerator::_parseTocOptions() -----------------------------------
    582 # function: Parse ToC options.
    583 
    584 sub _parseTocOptions {
    585 		# Get arguments
    586 	my ($self) = @_;
    587 		# Local variables
    588 	my ($toc, $group, $tokens, $tokenType, $i);
    589 		# Create parsers for ToC tokens
    590 	$self->{_tokensTocBegin} = [];
    591 	my $tokenTocBeginParser = HTML::_TokenTocBeginParser->new(
    592 		$self->{_tokensTocBegin}
    593 	);
    594 	my $tokenTocEndParser = HTML::_TokenTocEndParser->new();
    595 		# Loop through ToCs
    596 	foreach $toc (@{$self->{_tocs}}) {
    597 			# Reference parser ToC to current ToC
    598 		$tokenTocBeginParser->setToc($toc);
    599 			# Loop through 'tokenToToc' groups
    600 		foreach $group (@{$toc->{options}{'tokenToToc'}}) {
    601 				# Reference parser group to current group
    602 			$tokenTocBeginParser->setGroup($group);
    603 				# Parse 'tokenToToc' group
    604 			$tokenTocBeginParser->parse($group->{'tokenBegin'});
    605 				# Flush remaining buffered text
    606 			$tokenTocBeginParser->eof();
    607 			$tokenTocEndParser->parse(
    608 				$group->{'tokenEnd'}, 
    609 				$tokenTocBeginParser->{_lastAddedToken},
    610 				$tokenTocBeginParser->{_lastAddedTokenType}
    611 			);
    612 				# Flush remaining buffered text
    613 			$tokenTocEndParser->eof();
    614 		}
    615 	}
    616 }  # _parseTocOptions()
    617 
    618 
    619 #--- HTML::TocGenerator::_processTocEndingToken() -----------------------------
    620 # function: Process ToC-ending-token.
    621 # args:     - $aTocToken: token which acts as ToC-ending-token.
    622 
    623 sub _processTocEndingToken {
    624 		# Get arguments
    625 	my ($self, $aTocToken) = @_;
    626 		# Local variables
    627 	my ($toc);
    628 		# Aliases
    629 	$toc = $aTocToken->[TT_TOC];
    630 		# Link ToC to tokens?
    631 	if ($toc->{options}{'doLinkToToken'}) {
    632 		# Yes, link ToC to tokens;
    633 			# Add anchor href end
    634 		$toc->{_toc} .= 
    635 			(ref($toc->{_templateAnchorHrefEnd}) eq "CODE") ?
    636 				&{$toc->{_templateAnchorHrefEnd}} : 
    637 				eval($toc->{_templateAnchorHrefEnd});
    638 
    639 			# Output anchor name end only if necessary
    640 		$self->_outputAnchorNameEndConditionally($toc);
    641 	}
    642 }  # _processTocEndingToken()
    643 
    644 
    645 #--- HTML::TocGenerator::_processTocStartingToken() ---------------------------
    646 # function: Process ToC-starting-token.
    647 # args:     - $aTocToken: token which acts as ToC-starting-token.
    648 #           - $aTokenType: type of token.  Can be either TT_TOKENTYPE_START,
    649 #                _END, _TEXT, _COMMENT or _DECLARATION.
    650 #           - $aTokenAttributes: reference to hash containing attributes of 
    651 #                currently parsed token
    652 #           - $aTokenOrigText: reference to original token text
    653 
    654 sub _processTocStartingToken {
    655 		# Get arguments
    656 	my ($self, $aTocToken, $aTokenType, $aTokenAttributes, $aTokenOrigText) = @_;
    657 		# Local variables
    658 	my ($i, $level, $doLinkToId, $node, $groupLevel);
    659 	my ($file, $tocTokenId, $groupId, $toc, $attribute);
    660 		# Aliases
    661 	$file        = $self->{_currentFile};
    662 	$toc		    = $aTocToken->[TT_TOC];
    663 	$level	    = $aTocToken->[TT_GROUP]{'level'};
    664 	$groupId	    = $aTocToken->[TT_GROUP]{'groupId'};
    665 
    666 		# Retrieve 'doLinkToId' setting from either group options or toc options
    667 	$doLinkToId = (defined($aTocToken->[TT_GROUP]{'doLinkToId'})) ?
    668 		$aTocToken->[TT_GROUP]{'doLinkToId'} : $toc->{options}{'doLinkToId'}; 
    669 	
    670 		# Link to 'id' and tokenType isn't 'start'?
    671 	if (($doLinkToId) && ($aTokenType != TT_TOKENTYPE_START)) {
    672 		# Yes, link to 'id' and tokenType isn't 'start';
    673 			# Indicate to *not* link to 'id'
    674 		$doLinkToId = 0;
    675 	}
    676 
    677 	if (ref($level) eq "CODE") {
    678 		$level = &$level($self->{_currentFile}, $node);
    679 	}
    680 	if (ref($groupId) eq "CODE") {
    681 		$groupId = &$groupId($self->{_currentFile}, $node);
    682 	}
    683 
    684 		# Determine class level
    685 
    686 	my $groupIdManager = $self->_getGroupIdManager($toc);
    687 		# Known group?
    688 	if (!exists($groupIdManager->{groupIdLevels}{$groupId})) {
    689 		# No, unknown group;
    690 			# Add group
    691 		$groupIdManager->{groupIdLevels}{$groupId} = keys(
    692 			%{$groupIdManager->{groupIdLevels}}
    693 		) + 1;
    694 	}
    695 	$groupLevel = $groupIdManager->{groupIdLevels}{$groupId};
    696 
    697 		# Temporarily allow symbolic references
    698 	#no strict qw(refs);
    699 		# Increase level
    700 	$groupIdManager->{levels}{$groupId}[$level - 1] += 1;
    701 		# Reset remaining levels of same group
    702 	for ($i = $level; $i < @{$groupIdManager->{levels}{$groupId}}; $i++) {
    703 		$groupIdManager->{levels}{$groupId}[$i] = 0;
    704 	}
    705 
    706 		# Assemble numeric string indicating current level
    707 	$node = $self->_formatTocNode(
    708 		$level, $groupId, $aTocToken->[TT_GROUP], $toc
    709 	);
    710 
    711 		# Add newline if _toc not empty
    712 	if ($toc->{_toc}) { 
    713 		$toc->{_toc} .= "\n";
    714 	}
    715 
    716 		# Add toc item info
    717 	$toc->{_toc} .= "$level $groupLevel $groupId $node " .
    718 		$groupIdManager->{levels}{$groupId}[$level - 1] . " ";
    719 
    720 		# Add value of 'id' attribute if available
    721 	if (defined($aTokenAttributes->{id})) {
    722 		$toc->{_toc} .= $aTokenAttributes->{id};
    723 	}
    724 	$toc->{_toc} .= " ";
    725 		# Link ToC to tokens?
    726 	if ($toc->{options}{'doLinkToToken'}) {
    727 		# Yes, link ToC to tokens;
    728 			# Link ToC to token
    729 		$self->_linkTocToToken(
    730 			$toc, $file, $groupId, $level, $node, $groupLevel, $doLinkToId,
    731 			$aTokenAttributes
    732 		);
    733 	}
    734 
    735 		# Number tokens?
    736 	if (
    737 		$aTocToken->[TT_GROUP]{'doNumberToken'} || 
    738 		(
    739 			! defined($aTocToken->[TT_GROUP]{'doNumberToken'}) && 
    740 			$toc->{options}{'doNumberToken'}
    741 		)
    742 	) {
    743 		# Yes, number tokens;
    744 			# Add number by calling 'number' method
    745 		$self->number(
    746 			ref($toc->{_templateTokenNumber}) eq "CODE" ?
    747 				&{$toc->{_templateTokenNumber}}(
    748 					$node, $groupId, $file, $groupLevel, $level, $toc
    749 				) : 
    750 				eval($toc->{_templateTokenNumber}),
    751 			$toc
    752 		);
    753 	}
    754 
    755 		# Must attribute be used as ToC text?
    756 	if (defined($aTocToken->[TT_ATTRIBUTES_TOC])) {
    757 		# Yes, attribute must be used as ToC text;
    758 			# Loop through attributes
    759 		foreach $attribute (@{$aTocToken->[TT_ATTRIBUTES_TOC]}) {
    760 				# Attribute is available?
    761 			if (defined($$aTokenAttributes{$attribute})) {
    762 				# Yes, attribute is available;
    763 					# Add attribute value to ToC
    764 				$self->_processTocText($$aTokenAttributes{$attribute}, $toc);
    765 			}
    766 			else {
    767 				# No, attribute isn't available;
    768 					# Show warning
    769 				$self->_showWarning(
    770 					WARNING_TOC_ATTRIBUTE_PS_NOT_AVAILABLE_WITHIN_PS,
    771 					[$attribute, $$aTokenOrigText]
    772 				);
    773 			}
    774 				# Output anchor name end only if necessary
    775 			#$self->_outputAnchorNameEndConditionally($toc);
    776 				# End attribute
    777 			$self->_processTocEndingToken($aTocToken);
    778 		}
    779 	}
    780 	else {
    781 		# No, attribute mustn't be used as ToC text;
    782 			# Add end token to 'end token array'
    783 		push(
    784 			@{$self->{_tokensTocEnd}[$aTocToken->[TT_TAG_TYPE_END]]}, $aTocToken
    785 		);
    786 	}
    787 }  # _processTocStartingToken()
    788 
    789 
    790 #--- HTML::TocGenerator::_processTocText() ------------------------------------
    791 # function: This function processes text which must be added to the preliminary
    792 #           ToC.
    793 # args:     - $aText: Text to add to ToC.
    794 #           - $aToc: ToC to add text to.
    795 
    796 sub _processTocText {
    797 		# Get arguments
    798 	my ($self, $aText, $aToc) = @_;
    799 		# Add text to ToC
    800 	$aToc->{_toc} .= $aText;
    801 }  # _processTocText()
    802 
    803 
    804 #--- HTML::TocGenerator::_processTokenAsTocEndingToken() ----------------------
    805 # function: Check for token being a token to use for triggering the end of
    806 #           a ToC line and process it accordingly.
    807 # args:     - $aTokenType: type of token: 'start', 'end', 'comment' or 'text'.
    808 #           - $aTokenId: token id of currently parsed token
    809 
    810 sub _processTokenAsTocEndingToken {
    811 		# Get arguments
    812 	my ($self, $aTokenType, $aTokenId) = @_;
    813 		# Local variables
    814 	my ($i, $tokenId, $toc, $tokens);
    815 		# Loop through dirty start tokens
    816 	$i = 0;
    817 
    818 		# Alias token array of right type
    819 	$tokens = $self->{_tokensTocEnd}[$aTokenType];
    820 		# Loop through token array
    821 	while ($i < scalar @$tokens) {
    822 			# Aliases
    823 		$tokenId = $tokens->[$i][TT_TAG_END];
    824 			# Does current end tag equals dirty tag?
    825 		if ($aTokenId eq $tokenId) {
    826 			# Yes, current end tag equals dirty tag;
    827 				# Process ToC-ending-token
    828 			$self->_processTocEndingToken($tokens->[$i]);
    829 				# Remove dirty tag from array, automatically advancing to
    830 				# next token
    831 			splice(@$tokens, $i, 1);
    832 		}
    833 		else {
    834 			# No, current end tag doesn't equal dirty tag;
    835 				# Advance to next token
    836 			$i++;
    837 		}
    838 	}
    839 }  # _processTokenAsTocEndingToken()
    840 
    841 
    842 #--- HTML::TocGenerator::_processTokenAsTocStartingToken() --------------------
    843 # function: Check for token being a ToC-starting-token and process it 
    844 #           accordingly.
    845 # args:     - $aTokenType: type of token.  Can be either TT_TOKENTYPE_START,
    846 #                _END, _TEXT, _COMMENT or _DECLARATION.
    847 #           - $aTokenId: token id of currently parsed token
    848 #           - $aTokenAttributes: reference to hash containing attributes of 
    849 #                currently parsed token
    850 #           - $aTokenOrigText: reference to original text of token
    851 # returns:  1 if successful, i.e. token is processed as ToC-starting-token, 0
    852 #           if not.
    853 
    854 sub _processTokenAsTocStartingToken {
    855 		# Get arguments
    856 	my ($self, $aTokenType, $aTokenId, $aTokenAttributes, $aTokenOrigText) = @_;
    857 		# Local variables
    858 	my ($level, $levelToToc, $groupId, $groupToToc);
    859 	my ($result, $tocToken, $tagBegin, @tokensTocBegin, $fileSpec);
    860 		# Bias to token not functioning as ToC-starting-token
    861 	$result = 0;
    862 		# Loop through start tokens of right type
    863 	foreach $tocToken (@{$self->{_tokensTocBegin}[$aTokenType]}) {
    864 			# Alias file filter
    865 		$fileSpec = $tocToken->[TT_GROUP]{'fileSpec'};
    866 			# File matches?
    867 		if (!defined($fileSpec) || (
    868 			defined($fileSpec) &&
    869 			($self->{_currentFile} =~ m/$fileSpec/)
    870 		)) {
    871 			# Yes, file matches;
    872 				# Alias tag begin
    873 			$tagBegin = $tocToken->[TT_TAG_BEGIN];
    874 				# Tag and attributes match?
    875 			if (
    876 				defined($tagBegin) && 
    877 				($aTokenId =~ m/$tagBegin/) && 
    878 				HTML::TocGenerator::_doesHashContainHash(
    879 					$aTokenAttributes, $tocToken->[TT_INCLUDE_ATTRIBUTES_BEGIN], 0
    880 				) &&
    881 				HTML::TocGenerator::_doesHashContainHash(
    882 					$aTokenAttributes, $tocToken->[TT_EXCLUDE_ATTRIBUTES_BEGIN], 1
    883 				)
    884 			) {
    885 				# Yes, tag and attributes match;
    886 					# Aliases
    887 				$level	    = $tocToken->[TT_GROUP]{'level'};
    888 				$levelToToc = $tocToken->[TT_TOC]{options}{'levelToToc'};
    889 				$groupId     = $tocToken->[TT_GROUP]{'groupId'}; 
    890 				$groupToToc = $tocToken->[TT_TOC]{options}{'groupToToc'};
    891 					# Must level and group be processed?
    892 				if (
    893 					($level =~ m/$levelToToc/) &&
    894 					($groupId =~ m/$groupToToc/)
    895 				) {
    896 					# Yes, level and group must be processed;
    897 						# Indicate token acts as ToC-starting-token
    898 					$result = 1;
    899 						# Process ToC-starting-token
    900 					$self->_processTocStartingToken(
    901 						$tocToken, $aTokenType, $aTokenAttributes, $aTokenOrigText
    902 					);
    903 				}
    904 			}
    905 		}
    906 	}
    907 		# Return value
    908 	return $result;
    909 }  # _processTokenAsTocStartingToken()
    910 
    911 
    912 #--- HTML::TocGenerator::_resetBatchVariables() -------------------------------
    913 # function: Reset variables which are set because of batch invocation.
    914 
    915 sub _resetBatchVariables {
    916 		# Get arguments
    917 	my ($self) = @_;
    918 
    919 		# Filename of current file being parsed, empty string if not available
    920 	$self->{_currentFile} = "";
    921 		# Arrays containing start, end, comment, text & declaration tokens which 
    922 		# must trigger the ToC assembling.  Each array element may contain a 
    923 		# reference to an array containing the following elements:
    924 		#
    925       #    TT_TAG_BEGIN                => 0;
    926       #    TT_TAG_END                  => 1;
    927       #    TT_TAG_TYPE_END             => 2;
    928       #    TT_INCLUDE_ATTRIBUTES_BEGIN => 3;
    929       #    TT_EXCLUDE_ATTRIBUTES_BEGIN => 4;
    930       #    TT_INCLUDE_ATTRIBUTES_END   => 5;
    931       #    TT_EXCLUDE_ATTRIBUTES_END   => 6;
    932       #    TT_GROUP                    => 7;
    933       #    TT_TOC                      => 8;
    934 		#    TT_ATTRIBUTES_TOC           => 9;
    935 		#
    936 	$self->{_tokensTocBegin} = [
    937 		[],  # TT_TOKENTYPE_START      
    938 		[],  # TT_TOKENTYPE_END        
    939 		[],  # TT_TOKENTYPE_COMMENT    
    940 		[],  # TT_TOKENTYPE_TEXT       
    941 		[]   # TT_TOKENTYPE_DECLARATION
    942 	];
    943 	$self->{_tokensTocEnd} = [
    944 		[],  # TT_TOKENTYPE_START      
    945 		[],  # TT_TOKENTYPE_END        
    946 		[],  # TT_TOKENTYPE_COMMENT    
    947 		[],  # TT_TOKENTYPE_TEXT       
    948 		[]   # TT_TOKENTYPE_DECLARATION
    949 	];
    950 		# TRUE if ToCs have been initialized, FALSE if not.
    951 	$self->{_doneInitializeTocs} = 0;
    952 		# Array of ToCs to process
    953 	$self->{_tocs} = [];
    954 		# Active anchor name
    955 	$self->{_activeAnchorName} = undef;
    956 }  # _resetBatchVariables()
    957 
    958 
    959 #--- HTML::TocGenerator::_resetStackVariables() -------------------------------
    960 # function: Reset variables which cumulate during ToC generation.
    961 
    962 sub _resetStackVariables {
    963 		# Get arguments
    964 	my ($self) = @_;
    965 		# Reset variables
    966 	$self->{levels}        = undef;
    967 	$self->{groupIdLevels} = undef;
    968 }  # _resetStackVariables()
    969 
    970 
    971 #--- HTML::TocGenerator::_setActiveAnchorName() -------------------------------
    972 # function: Set active anchor name.
    973 # args:     - aAnchorName: Name of anchor name to set active.
    974 
    975 sub _setActiveAnchorName {
    976 		# Get arguments
    977 	my ($self, $aAnchorName) = @_;
    978 		# Set active anchor name
    979 	$self->{_activeAnchorName} = $aAnchorName;
    980 }  # _setActiveAnchorName()
    981 
    982 
    983 #--- HTML::TocGenerator::_showWarning() ---------------------------------------
    984 # function: Show warning.
    985 # args:     - aWarningNr: Number of warning to show.
    986 #           - aWarningArgs: Arguments to display within the warning.
    987 
    988 sub _showWarning {
    989 		# Get arguments
    990 	my ($self, $aWarningNr, $aWarningArgs) = @_;
    991 		# Local variables
    992 	my (%warnings);
    993 		# Set warnings
    994 	%warnings = (
    995 		WARNING_NESTED_ANCHOR_PS_WITHIN_PS()               => 
    996 			"Nested anchor '%s' within anchor '%s'.", 
    997 		WARNING_TOC_ATTRIBUTE_PS_NOT_AVAILABLE_WITHIN_PS() =>
    998 			"ToC attribute '%s' not available within token '%s'.",
    999 	);
   1000 		# Show warning
   1001 	print STDERR "warning ($aWarningNr): " . sprintf($warnings{"$aWarningNr"}, @$aWarningArgs) . "\n";
   1002 }  # _showWarning()
   1003 
   1004 
   1005 #--- HTML::TocGenerator::anchorId() -------------------------------------------
   1006 # function: Anchor id processing method.  Leave it up to the descendant to do 
   1007 #           something useful with it.
   1008 # args:     - $aAnchorId
   1009 #           - $aToc: Reference to ToC to which anchorId belongs.
   1010 
   1011 sub anchorId {
   1012 }  # anchorId()
   1013 
   1014 
   1015 #--- HTML::TocGenerator::anchorNameBegin() ------------------------------------
   1016 # function: Anchor name begin processing method.  Leave it up to the descendant
   1017 #           to do something useful with it.
   1018 # args:     - $aAnchorName
   1019 #           - $aToc: Reference to ToC to which anchorname belongs.
   1020 
   1021 sub anchorNameBegin {
   1022 }  # anchorNameBegin()
   1023 
   1024 
   1025 #--- HTML::TocGenerator::anchorNameEnd() --------------------------------------
   1026 # function: Anchor name end processing method.  Leave it up to the descendant
   1027 #           to do something useful with it.
   1028 # args:     - $aAnchorName
   1029 #           - $aToc: Reference to ToC to which anchorname belongs.
   1030 
   1031 sub anchorNameEnd {
   1032 }  # anchorNameEnd()
   1033 
   1034 
   1035 #--- HTML::TocGenerator::comment() --------------------------------------------
   1036 # function: Process comment.
   1037 # args:     - $aComment: comment text with '<!--' and '-->' tags stripped off.
   1038 
   1039 sub comment {
   1040 		# Get arguments
   1041 	my ($self, $aComment) = @_;
   1042 		# Must a ToC be generated?
   1043 	if ($self->{_doGenerateToc}) {
   1044 		# Yes, a ToC must be generated
   1045 			# Process end tag as ToC-starting-token
   1046 		$self->_processTokenAsTocStartingToken(
   1047 			TT_TOKENTYPE_COMMENT, $aComment, undef, \$aComment
   1048 		);
   1049 			# Process end tag as token which ends ToC registration
   1050 		$self->_processTokenAsTocEndingToken(
   1051 			TT_TOKENTYPE_COMMENT, $aComment
   1052 		);
   1053 	}
   1054 }  # comment()
   1055 
   1056 
   1057 #--- HTML::TocGenerator::end() ------------------------------------------------
   1058 # function: This function is called every time a closing tag is encountered.
   1059 # args:     - $aTag: tag name (in lower case).
   1060 #           - $aOrigText: tag name including brackets.
   1061 
   1062 sub end {
   1063 		# Get arguments
   1064 	my ($self, $aTag, $aOrigText) = @_;
   1065 		# Local variables
   1066 	my ($tag, $toc, $i);
   1067 		# Must a ToC be generated?
   1068 	if ($self->{_doGenerateToc}) {
   1069 		# Yes, a ToC must be generated
   1070 			# Process end tag as ToC-starting-token
   1071 		$self->_processTokenAsTocStartingToken(
   1072 			TT_TOKENTYPE_END, $aTag, undef, \$aOrigText
   1073 		);
   1074 			# Process end tag as ToC-ending-token
   1075 		$self->_processTokenAsTocEndingToken(
   1076 			TT_TOKENTYPE_END, $aTag
   1077 		);
   1078 			# Tag is of type 'anchor'?
   1079 		if (defined($self->{_activeAnchorName}) && ($aTag eq "a")) {
   1080 			# Yes, tag is of type 'anchor';
   1081 				# Reset dirty anchor
   1082 			$self->{_activeAnchorName} = undef;
   1083 		}
   1084 	}
   1085 }  # end()
   1086 
   1087 
   1088 #--- HTML::TocGenerator::extend() ---------------------------------------------
   1089 # function: Extend ToCs.
   1090 # args:     - $aTocs: Reference to array of ToC objects
   1091 #           - $aString: String to parse.
   1092 
   1093 sub extend {
   1094 		# Get arguments
   1095 	my ($self, $aTocs, $aString) = @_;
   1096 		# Initialize TocGenerator batch
   1097 	$self->_initializeExtenderBatch($aTocs);
   1098 		# Extend ToCs
   1099 	$self->_extend($aString);
   1100 		# Deinitialize TocGenerator batch
   1101 	$self->_deinitializeExtenderBatch();
   1102 }  # extend()
   1103 
   1104 
   1105 #--- HTML::TocGenerator::extendFromFile() -------------------------------------
   1106 # function: Extend ToCs.
   1107 # args:     - @aTocs: Reference to array of ToC objects
   1108 #           - @aFiles: Reference to array of files to parse.
   1109 
   1110 sub extendFromFile {
   1111 		# Get arguments
   1112 	my ($self, $aTocs, $aFiles) = @_;
   1113 		# Initialize TocGenerator batch
   1114 	$self->_initializeExtenderBatch($aTocs);
   1115 		# Extend ToCs
   1116 	$self->_extendFromFile($aFiles);
   1117 		# Deinitialize TocGenerator batch
   1118 	$self->_deinitializeExtenderBatch();
   1119 }  # extendFromFile()
   1120 
   1121 
   1122 #--- HTML::TocGenerator::generate() -------------------------------------------
   1123 # function: Generate ToC.
   1124 # args:     - $aToc: Reference to (array of) ToC object(s)
   1125 #           - $aString: Reference to string to parse
   1126 #           - $aOptions: optional options
   1127 
   1128 sub generate {
   1129 		# Get arguments
   1130 	my ($self, $aToc, $aString, $aOptions) = @_;
   1131 		# Initialize TocGenerator batch
   1132 	$self->_initializeGeneratorBatch($aToc, $aOptions);
   1133 		# Do generate ToC
   1134 	$self->_generate($aString);
   1135 		# Deinitialize TocGenerator batch
   1136 	$self->_deinitializeGeneratorBatch();
   1137 }  # generate()
   1138 
   1139 
   1140 #--- HTML::TocGenerator::generateFromFile() -----------------------------------
   1141 # function: Generate ToC.
   1142 # args:     - $aToc: Reference to (array of) ToC object(s)
   1143 #           - $aFile: (reference to array of) file to parse.
   1144 #           - $aOptions: optional options
   1145 
   1146 sub generateFromFile {
   1147 		# Get arguments
   1148 	my ($self, $aToc, $aFile, $aOptions) = @_;
   1149 		# Initialize TocGenerator batch
   1150 	$self->_initializeGeneratorBatch($aToc, $aOptions);
   1151 		# Do generate ToC
   1152 	$self->_generateFromFile($aFile);
   1153 		# Deinitialize TocGenerator batch
   1154 	$self->_deinitializeGeneratorBatch();
   1155 }  # generateFromFile()
   1156 
   1157 
   1158 #--- HTML::TocGenerator::number() ---------------------------------------------
   1159 # function: Heading number processing method.  Leave it up to the descendant
   1160 #           to do something useful with it.
   1161 # args:     - $aNumber
   1162 #           - $aToc: Reference to ToC to which anchorname belongs.
   1163 
   1164 sub number {
   1165 		# Get arguments
   1166 	my ($self, $aNumber, $aToc) = @_;
   1167 }  # number()
   1168 
   1169 
   1170 #--- HTML::TocGenerator::parse() ----------------------------------------------
   1171 # function: Parse scalar.
   1172 # args:     - $aString: string to parse
   1173 
   1174 sub parse {
   1175 		# Get arguments
   1176 	my ($self, $aString) = @_;
   1177 		# Call ancestor
   1178 	$self->SUPER::parse($aString);
   1179 }  # parse()
   1180 
   1181 
   1182 #--- HTML::TocGenerator::parse_file() -----------------------------------------
   1183 # function: Parse file.
   1184 
   1185 sub parse_file {
   1186 		# Get arguments
   1187 	my ($self, $aFile) = @_;
   1188 		# Call ancestor
   1189 	$self->SUPER::parse_file($aFile);
   1190 }  # parse_file()
   1191 
   1192 
   1193 #--- HTML::TocGenerator::setOptions() -----------------------------------------
   1194 # function: Set options.
   1195 # args:     - aOptions: Reference to hash containing options.
   1196 
   1197 sub setOptions {
   1198 		# Get arguments
   1199 	my ($self, $aOptions) = @_;
   1200 		# Options are defined?
   1201 	if (defined($aOptions)) {
   1202 		# Yes, options are defined; add to options
   1203 		%{$self->{options}} = (%{$self->{options}}, %$aOptions);
   1204 	}
   1205 }  # setOptions()
   1206 
   1207 
   1208 #--- HTML::TocGenerator::start() ----------------------------------------------
   1209 # function: This function is called every time an opening tag is encountered.
   1210 # args:     - $aTag: tag name (in lower case).
   1211 #           - $aAttr: reference to hash containing all tag attributes (in lower
   1212 #                case).
   1213 #           - $aAttrSeq: reference to array containing all tag attributes (in 
   1214 #                lower case) in the original order
   1215 #           - $aOrigText: the original HTML text
   1216 
   1217 sub start {
   1218 		# Get arguments
   1219 	my ($self, $aTag, $aAttr, $aAttrSeq, $aOrigText) = @_;
   1220 	$self->{isTocToken} = 0;
   1221 		# Start tag is of type 'anchor name'?
   1222 	if ($aTag eq "a" && defined($aAttr->{name})) {
   1223 		# Yes, start tag is of type 'anchor name';
   1224 			# Is another anchor already active?
   1225 		if (defined($self->{_activeAnchorName})) {
   1226 			# Yes, another anchor is already active;
   1227 				# Is the first anchor inserted by 'TocGenerator'?
   1228 			if ($self->{_doOutputAnchorNameEnd}) {
   1229 				# Yes, the first anchor is inserted by 'TocGenerator';
   1230 					# Show warning
   1231 				$self->_showWarning(
   1232 					WARNING_NESTED_ANCHOR_PS_WITHIN_PS,
   1233 					[$aOrigText, $self->{_activeAnchorName}]
   1234 				);
   1235 			}
   1236 		}
   1237 			# Set active anchor name
   1238 		$self->_setActiveAnchorName($aAttr->{name});
   1239 	}
   1240 		# Must a ToC be generated?
   1241 	if ($self->{_doGenerateToc}) {
   1242 		# Yes, a ToC must be generated
   1243 			# Process start tag as ToC token
   1244 		$self->{isTocToken} = $self->_processTokenAsTocStartingToken(
   1245 			TT_TOKENTYPE_START, $aTag, $aAttr, \$aOrigText
   1246 		);
   1247 			# Process end tag as ToC-ending-token
   1248 		$self->_processTokenAsTocEndingToken(
   1249 			TT_TOKENTYPE_START, $aTag
   1250 		);
   1251 	}
   1252 }  # start()
   1253 
   1254 
   1255 #--- HTML::TocGenerator::text() -----------------------------------------------
   1256 # function: This function is called every time plain text is encountered.
   1257 # args:     - @_: array containing data.
   1258 
   1259 sub text {
   1260 		# Get arguments
   1261 	my ($self, $aText) = @_;
   1262 		# Local variables
   1263 	my ($text, $toc, $i, $token, $tokens);
   1264 		# Must a ToC be generated?
   1265 	if ($self->{_doGenerateToc}) {
   1266 		# Yes, a ToC must be generated
   1267 			# Are there dirty start tags?
   1268 
   1269 			# Loop through token types
   1270 		foreach $tokens (@{$self->{_tokensTocEnd}}) {
   1271 				# Loop though tokens
   1272 			foreach $token (@$tokens) {
   1273 					# Add text to toc
   1274 
   1275 					# Alias
   1276 				$toc = $token->[TT_TOC];
   1277 					# Remove possible newlines from text
   1278 				($text = $aText) =~ s/\s*\n\s*/ /g;
   1279 					# Add text to toc
   1280 				$self->_processTocText($text, $toc);
   1281 			}
   1282 		}
   1283 	}
   1284 }  # text()
   1285 
   1286 
   1287 
   1288 
   1289 #=== HTML::_TokenTocParser ====================================================
   1290 # function: Parse 'toc tokens'.  'Toc tokens' mark HTML code which is to be
   1291 #           inserted into the ToC.
   1292 # note:     Used internally.
   1293 
   1294 package HTML::_TokenTocParser;
   1295 
   1296 
   1297 BEGIN {
   1298 	use vars qw(@ISA);
   1299 
   1300 	@ISA = qw(HTML::Parser);
   1301 }
   1302 
   1303 
   1304 END {}
   1305 
   1306 
   1307 #--- HTML::_TokenTocParser::new() ---------------------------------------------
   1308 # function: Constructor
   1309 
   1310 sub new {
   1311 		# Get arguments
   1312 	my ($aType) = @_;
   1313 		# Create instance
   1314 	my $self = $aType->SUPER::new;
   1315 
   1316 		# Return instance
   1317 	return $self;
   1318 }  # new()
   1319 
   1320 
   1321 #--- HTML::_TokenTocParser::_parseAttributes() --------------------------------
   1322 # function: Parse attributes.
   1323 # args:     - $aAttr: Reference to hash containing all tag attributes (in lower
   1324 #                case).
   1325 #           - $aIncludeAttributes: Reference to hash to which 'include
   1326 #                attributes' must be added.
   1327 #           - $aExcludeAttributes: Reference to hash to which 'exclude
   1328 #                attributes' must be added.
   1329 #           - $aTocAttributes: Reference to hash to which 'ToC attributes' 
   1330 #                must be added.
   1331 
   1332 sub _parseAttributes {
   1333 		# Get arguments
   1334 	my (
   1335 		$self, $aAttr, $aIncludeAttributes, $aExcludeAttributes,
   1336 		$aTocAttributes
   1337 	) = @_;
   1338 		# Local variables
   1339 	my ($key, $value);
   1340 	my ($attributeToExcludeToken, $attributeToTocToken);
   1341 		# Get token which marks attributes which must be excluded
   1342 	$attributeToExcludeToken = $self->{_toc}{options}{'attributeToExcludeToken'};
   1343 	$attributeToTocToken     = $self->{_toc}{options}{'attributeToTocToken'};
   1344 		# Loop through attributes
   1345 	while (($key, $value) = each %$aAttr) {
   1346 			# Attribute value equals 'ToC token'?
   1347 		if ($value =~ m/$attributeToTocToken/) {
   1348 			# Yes, attribute value equals 'ToC token';
   1349 				# Add attribute to 'ToC attributes'
   1350 			push @$aTocAttributes, $key;
   1351 		}
   1352 		else {
   1353 			# No, attribute isn't 'ToC' token;
   1354 				# Attribute value starts with 'exclude token'?
   1355 			if ($value =~ m/^$attributeToExcludeToken(.*)/) {
   1356 				# Yes, attribute value starts with 'exclude token';
   1357 					# Add attribute to 'exclude attributes'
   1358 				$$aExcludeAttributes{$key} = "$1";
   1359 			}
   1360 			else {
   1361 				# No, attribute key doesn't start with '-';
   1362 					# Add attribute to 'include attributes'
   1363 				$$aIncludeAttributes{$key} = $value;
   1364 			}
   1365 		}
   1366 	}
   1367 }  # _parseAttributes()
   1368 
   1369 
   1370 
   1371 
   1372 #=== HTML::_TokenTocBeginParser ===============================================
   1373 # function: Parse 'toc tokens'.  'Toc tokens' mark HTML code which is to be
   1374 #           inserted into the ToC.
   1375 # note:     Used internally.
   1376 
   1377 package HTML::_TokenTocBeginParser;
   1378 
   1379 
   1380 BEGIN {
   1381 	use vars qw(@ISA);
   1382 
   1383 	@ISA = qw(HTML::_TokenTocParser);
   1384 }
   1385 
   1386 END {}
   1387 
   1388 
   1389 #--- HTML::_TokenTocBeginParser::new() ----------------------------------------
   1390 # function: Constructor
   1391 
   1392 sub new {
   1393 		# Get arguments
   1394 	my ($aType, $aTokenArray) = @_;
   1395 		# Create instance
   1396 	my $self = $aType->SUPER::new;
   1397 		# Reference token array
   1398 	$self->{tokens} = $aTokenArray;
   1399 		# Reference to last added token
   1400 	$self->{_lastAddedToken}     = undef;
   1401 	$self->{_lastAddedTokenType} = undef;
   1402 		# Return instance
   1403 	return $self;
   1404 }  # new()
   1405 
   1406 
   1407 #--- HTML::_TokenTocBeginParser::_processAttributes() -------------------------
   1408 # function: Process attributes.
   1409 # args:     - $aAttributes: Attributes to parse.
   1410 
   1411 sub _processAttributes {
   1412 		# Get arguments
   1413 	my ($self, $aAttributes) = @_;
   1414 		# Local variables
   1415 	my (%includeAttributes, %excludeAttributes, @tocAttributes);
   1416 
   1417 		# Parse attributes
   1418 	$self->_parseAttributes(
   1419 		$aAttributes, \%includeAttributes, \%excludeAttributes, \@tocAttributes
   1420 	);
   1421 		# Include attributes are specified?
   1422 	if (keys(%includeAttributes) > 0) {
   1423 		# Yes, include attributes are specified;
   1424 			# Store include attributes
   1425 		@${$self->{_lastAddedToken}}[
   1426 			HTML::TocGenerator::TT_INCLUDE_ATTRIBUTES_BEGIN
   1427 		] = \%includeAttributes;
   1428 	}
   1429 		# Exclude attributes are specified?
   1430 	if (keys(%excludeAttributes) > 0) {
   1431 		# Yes, exclude attributes are specified;
   1432 			# Store exclude attributes
   1433 		@${$self->{_lastAddedToken}}[
   1434 			HTML::TocGenerator::TT_EXCLUDE_ATTRIBUTES_BEGIN
   1435 		] = \%excludeAttributes;
   1436 	}
   1437 		# Toc attributes are specified?
   1438 	if (@tocAttributes > 0) {
   1439 		# Yes, toc attributes are specified;
   1440 			# Store toc attributes
   1441 		@${$self->{_lastAddedToken}}[
   1442 			HTML::TocGenerator::TT_ATTRIBUTES_TOC
   1443 		] = \@tocAttributes;
   1444 	}
   1445 }  # _processAttributes()
   1446 
   1447 
   1448 #--- HTML::_TokenTocBeginParser::_processToken() ------------------------------
   1449 # function: Process token.
   1450 # args:     - $aTokenType: Type of token to process.
   1451 #           - $aTag: Tag of token.
   1452 
   1453 sub _processToken {
   1454 		# Get arguments
   1455 	my ($self, $aTokenType, $aTag) = @_;
   1456 		# Local variables
   1457 	my ($tokenArray, $index);
   1458 		# Push element on array of update tokens
   1459 	$index = push(@{$self->{tokens}[$aTokenType]}, []) - 1;
   1460 		# Alias token array to add element to
   1461 	$tokenArray = $self->{tokens}[$aTokenType];
   1462 		# Indicate last updated token array element
   1463 	$self->{_lastAddedTokenType} = $aTokenType;
   1464 	$self->{_lastAddedToken}     = \$$tokenArray[$index];
   1465 		# Add fields
   1466 	$$tokenArray[$index][HTML::TocGenerator::TT_TAG_BEGIN] = $aTag;
   1467 	$$tokenArray[$index][HTML::TocGenerator::TT_GROUP]     = $self->{_group};
   1468 	$$tokenArray[$index][HTML::TocGenerator::TT_TOC]       = $self->{_toc};
   1469 }  # _processToken()
   1470 
   1471 
   1472 #--- HTML::_TokenTocBeginParser::comment() ------------------------------------
   1473 # function: Process comment.
   1474 # args:     - $aComment: comment text with '<!--' and '-->' tags stripped off.
   1475 
   1476 sub comment {
   1477 		# Get arguments
   1478 	my ($self, $aComment) = @_;
   1479 		# Process token
   1480 	$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_COMMENT, $aComment);
   1481 }  # comment()
   1482 
   1483 
   1484 #--- HTML::_TokenTocBeginParser::declaration() --------------------------------
   1485 # function: This function is called every time a markup declaration is
   1486 #           encountered by HTML::Parser.
   1487 # args:     - $aDeclaration: Markup declaration.
   1488 
   1489 sub declaration {
   1490 		# Get arguments
   1491 	my ($self, $aDeclaration) = @_;
   1492 		# Process token
   1493 	$self->_processToken(
   1494 		HTML::TocGenerator::TT_TOKENTYPE_DECLARATION, $aDeclaration
   1495 	);
   1496 }  # declaration()
   1497 
   1498 	
   1499 #--- HTML::_TokenTocBeginParser::end() ----------------------------------------
   1500 # function: This function is called every time a closing tag is encountered
   1501 #           by HTML::Parser.
   1502 # args:     - $aTag: tag name (in lower case).
   1503 
   1504 sub end {
   1505 		# Get arguments
   1506 	my ($self, $aTag, $aOrigText) = @_;
   1507 		# Process token
   1508 	$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_END, $aTag);
   1509 }  # end()
   1510 
   1511 
   1512 #--- HTML::_TokenTocBeginParser::parse() --------------------------------------
   1513 # function: Parse begin token.
   1514 # args:     - $aToken: 'toc token' to parse
   1515 
   1516 sub parse {
   1517 		# Get arguments
   1518 	my ($self, $aString) = @_;
   1519 		# Call ancestor
   1520 	$self->SUPER::parse($aString);
   1521 }  # parse()
   1522 
   1523 
   1524 #--- HTML::_TokenTocBeginParser->setGroup() -----------------------------------
   1525 # function: Set current 'tokenToToc' group.
   1526 
   1527 sub setGroup {
   1528 		# Get arguments
   1529 	my ($self, $aGroup) = @_;
   1530 		# Set current 'tokenToToc' group
   1531 	$self->{_group} = $aGroup;
   1532 }  # setGroup()
   1533 
   1534 
   1535 #--- HTML::_TokenTocBeginParser->setToc() -------------------------------------
   1536 # function: Set current ToC.
   1537 
   1538 sub setToc {
   1539 		# Get arguments
   1540 	my ($self, $aToc) = @_;
   1541 		# Set current ToC
   1542 	$self->{_toc} = $aToc;
   1543 }  # setToc()
   1544 
   1545 
   1546 #--- HTML::_TokenTocBeginParser::start() --------------------------------------
   1547 # function: This function is called every time an opening tag is encountered.
   1548 # args:     - $aTag: tag name (in lower case).
   1549 #           - $aAttr: reference to hash containing all tag attributes (in lower
   1550 #                case).
   1551 #           - $aAttrSeq: reference to array containing all attribute keys (in 
   1552 #                lower case) in the original order
   1553 #           - $aOrigText: the original HTML text
   1554 
   1555 sub start {
   1556 		# Get arguments
   1557 	my ($self, $aTag, $aAttr, $aAttrSeq, $aOrigText) = @_;
   1558 		# Process token
   1559 	$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_START, $aTag);
   1560 		# Process attributes
   1561 	$self->_processAttributes($aAttr);
   1562 }  # start()
   1563 
   1564 
   1565 #--- HTML::_TokenTocBeginParser::text() ---------------------------------------
   1566 # function: This function is called every time plain text is encountered.
   1567 # args:     - @_: array containing data.
   1568 
   1569 sub text {
   1570 		# Get arguments
   1571 	my ($self, $aText) = @_;
   1572 		# Was token already created and is last added token of type 'text'?
   1573 	if (
   1574 		defined($self->{_lastAddedToken}) && 
   1575 		$self->{_lastAddedTokenType} == HTML::TocGenerator::TT_TOKENTYPE_TEXT
   1576 	) {
   1577 		# Yes, token is already created;
   1578 			# Add tag to existing token
   1579 		@${$self->{_lastAddedToken}}[HTML::TocGenerator::TT_TAG_BEGIN] .= $aText;
   1580 	}
   1581 	else {
   1582 		# No, token isn't created;
   1583 			# Process token
   1584 		$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_TEXT, $aText);
   1585 	}
   1586 }  # text()
   1587 
   1588 
   1589 
   1590 
   1591 #=== HTML::_TokenTocEndParser =================================================
   1592 # function: Parse 'toc tokens'.  'Toc tokens' mark HTML code which is to be
   1593 #           inserted into the ToC.
   1594 # note:     Used internally.
   1595 
   1596 package HTML::_TokenTocEndParser;
   1597 
   1598 
   1599 BEGIN {
   1600 	use vars qw(@ISA);
   1601 
   1602 	@ISA = qw(HTML::_TokenTocParser);
   1603 }
   1604 
   1605 
   1606 END {}
   1607 
   1608 
   1609 #--- HTML::_TokenTocEndParser::new() ------------------------------------------
   1610 # function: Constructor
   1611 # args:     - $aType: Class type.
   1612 
   1613 sub new {
   1614 		# Get arguments
   1615 	my ($aType) = @_;
   1616 		# Create instance
   1617 	my $self = $aType->SUPER::new;
   1618 		# Reference to last added token
   1619 	$self->{_lastAddedToken} = undef;
   1620 		# Return instance
   1621 	return $self;
   1622 }  # new()
   1623 
   1624 
   1625 #--- HTML::_TokenTocEndParser::_processAttributes() ---------------------------
   1626 # function: Process attributes.
   1627 # args:     - $aAttributes: Attributes to parse.
   1628 
   1629 sub _processAttributes {
   1630 		# Get arguments
   1631 	my ($self, $aAttributes) = @_;
   1632 		# Local variables
   1633 	my (%includeAttributes, %excludeAttributes);
   1634 
   1635 		# Parse attributes
   1636 	$self->_parseAttributes(
   1637 		$aAttributes, \%includeAttributes, \%excludeAttributes
   1638 	);
   1639 		# Include attributes are specified?
   1640 	if (keys(%includeAttributes) > 0) {
   1641 		# Yes, include attributes are specified;
   1642 			# Store include attributes
   1643 		@${$self->{_Token}}[
   1644 			HTML::TocGenerator::TT_INCLUDE_ATTRIBUTES_END
   1645 		] = \%includeAttributes;
   1646 	}
   1647 		# Exclude attributes are specified?
   1648 	if (keys(%excludeAttributes) > 0) {
   1649 		# Yes, exclude attributes are specified;
   1650 			# Store exclude attributes
   1651 		@${$self->{_Token}}[
   1652 			HTML::TocGenerator::TT_EXCLUDE_ATTRIBUTES_END
   1653 		] = \%excludeAttributes;
   1654 	}
   1655 }  # _processAttributes()
   1656 
   1657 
   1658 #--- HTML::_TokenTocEndParser::_processToken() --------------------------------
   1659 # function: Process token.
   1660 # args:     - $aTokenType: Type of token to process.
   1661 #           - $aTag: Tag of token.
   1662 
   1663 sub _processToken {
   1664 		# Get arguments
   1665 	my ($self, $aTokenType, $aTag) = @_;
   1666 		# Update token
   1667 	@${$self->{_token}}[HTML::TocGenerator::TT_TAG_TYPE_END] = $aTokenType;
   1668 	@${$self->{_token}}[HTML::TocGenerator::TT_TAG_END]      = $aTag;
   1669 		# Indicate token type which has been processed
   1670 	$self->{_lastAddedTokenType} = $aTokenType;
   1671 }  # _processToken()
   1672 
   1673 
   1674 #--- HTML::_TokenTocEndParser::comment() --------------------------------------
   1675 # function: Process comment.
   1676 # args:     - $aComment: comment text with '<!--' and '-->' tags stripped off.
   1677 
   1678 sub comment {
   1679 		# Get arguments
   1680 	my ($self, $aComment) = @_;
   1681 		# Process token
   1682 	$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_COMMENT, $aComment);
   1683 }  # comment()
   1684 
   1685 
   1686 #--- HTML::_TokenTocDeclarationParser::declaration() --------------------------
   1687 # function: This function is called every time a markup declaration is
   1688 #           encountered by HTML::Parser.
   1689 # args:     - $aDeclaration: Markup declaration.
   1690 
   1691 sub declaration {
   1692 		# Get arguments
   1693 	my ($self, $aDeclaration) = @_;
   1694 		# Process token
   1695 	$self->_processToken(
   1696 		HTML::TocGenerator::TT_TOKENTYPE_DECLARATION, $aDeclaration
   1697 	);
   1698 }  # declaration()
   1699 
   1700 	
   1701 #--- HTML::_TokenTocEndParser::end() ------------------------------------------
   1702 # function: This function is called every time a closing tag is encountered
   1703 #           by HTML::Parser.
   1704 # args:     - $aTag: tag name (in lower case).
   1705 
   1706 sub end {
   1707 		# Get arguments
   1708 	my ($self, $aTag, $aOrigText) = @_;
   1709 		# Process token
   1710 	$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_END, $aTag);
   1711 }  # end()
   1712 
   1713 
   1714 #--- HTML::_TokenTocEndParser::parse() ----------------------------------------
   1715 # function: Parse token.
   1716 # args:     - $aString: 'toc token' to parse
   1717 #           - $aToken: Reference to token
   1718 #           - $aTokenTypeBegin: Type of begin token
   1719 
   1720 sub parse {
   1721 		# Get arguments
   1722 	my ($self, $aString, $aToken, $aTokenTypeBegin) = @_;
   1723 		# Token argument specified?
   1724 	if (defined($aToken)) {
   1725 		# Yes, token argument is specified;
   1726 			# Store token reference
   1727 		$self->{_token} = $aToken;
   1728 	}
   1729 		# End tag defined?
   1730 	if (! defined($aString)) {
   1731 		# No, end tag isn't defined;
   1732 			# Last added tokentype was of type 'start'?
   1733 		if (
   1734 			(defined($aTokenTypeBegin)) &&
   1735 			($aTokenTypeBegin == HTML::TocGenerator::TT_TOKENTYPE_START) 
   1736 		) {
   1737 			# Yes, last added tokentype was of type 'start';
   1738 				# Assume end tag
   1739 			$self->_processToken(
   1740 				HTML::TocGenerator::TT_TAG_END,
   1741 				@${$self->{_token}}[HTML::TocGenerator::TT_TAG_BEGIN]
   1742 			);
   1743 		}
   1744 	}
   1745 	else {
   1746 			# Call ancestor
   1747 		$self->SUPER::parse($aString);
   1748 	}
   1749 }  # parse()
   1750 
   1751 
   1752 #--- HTML::_TokenTocEndParser::start() ----------------------------------------
   1753 # function: This function is called every time an opening tag is encountered.
   1754 # args:     - $aTag: tag name (in lower case).
   1755 #           - $aAttr: reference to hash containing all tag attributes (in lower
   1756 #                case).
   1757 #           - $aAttrSeq: reference to array containing all attribute keys (in 
   1758 #                lower case) in the original order
   1759 #           - $aOrigText: the original HTML text
   1760 
   1761 sub start {
   1762 		# Get arguments
   1763 	my ($self, $aTag, $aAttr, $aAttrSeq, $aOrigText) = @_;
   1764 		# Process token
   1765 	$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_START, $aTag);
   1766 		# Process attributes
   1767 	$self->_processAttributes($aAttr);
   1768 }  # start()
   1769 
   1770 
   1771 #--- HTML::_TokenTocEndParser::text() -----------------------------------------
   1772 # function: This function is called every time plain text is encountered.
   1773 # args:     - @_: array containing data.
   1774 
   1775 sub text {
   1776 		# Get arguments
   1777 	my ($self, $aText) = @_;
   1778 
   1779 		# Is token already created?
   1780 	if (defined($self->{_lastAddedTokenType})) {
   1781 		# Yes, token is already created;
   1782 			# Add tag to existing token
   1783 		@${$self->{_token}}[HTML::TocGenerator::TT_TAG_END] .= $aText;
   1784 	}
   1785 	else {
   1786 		# No, token isn't created;
   1787 			# Process token
   1788 		$self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_TEXT, $aText);
   1789 	}
   1790 }  # text()
   1791 
   1792 
   1793 1;
   1794