File Coverage

blib/lib/HTML/TocInsertor.pm
Criterion Covered Total %
statement 320 351 91.1
branch 78 102 76.4
condition 20 24 83.3
subroutine 55 58 94.8
pod 6 14 42.8
total 479 549 87.2


line stmt bran cond sub pod time code
1             #--- TocInsertor.pm -----------------------------------------------------------
2             # function: Insert Table of Contents HTML::Toc, generated by
3             # HTML::TocGenerator.
4             # note: - The term 'propagate' is used as a shortcut for the process of
5             # both generating and inserting a ToC at the same time.
6             # - 'TIP' is an abbreviation of 'Toc Insertion Point'.
7             # - `scene' ?
8             # - The term `scenario' is used for the output, which is seen as one
9             # long story (scenario), split in scenes:
10             # +-------------------scenario--------------------+
11             # +--scene--+--toc--+--scene--+--scene--+--scene--+
12              
13              
14              
15             package HTML::TocInsertor;
16              
17              
18 10     10   13065 use strict;
  10         26  
  10         632  
19 10     10   2494 use HTML::TocGenerator;
  10         24  
  10         333  
20              
21              
22             BEGIN {
23 10     10   56 use vars qw(@ISA $VERSION);
  10         18  
  10         615  
24              
25 10     10   23 $VERSION = '1.12';
26              
27 10         353 @ISA = qw(HTML::TocGenerator);
28             }
29              
30             # TocInsertionPoint (TIP) constants
31            
32 10     10   61 use constant TIP_PREPOSITION_REPLACE => 'replace';
  10         17  
  10         700  
33 10     10   55 use constant TIP_PREPOSITION_BEFORE => 'before';
  10         19  
  10         717  
34 10     10   68 use constant TIP_PREPOSITION_AFTER => 'after';
  10         28  
  10         486  
35              
36 10     10   52 use constant TIP_TOKEN_ID => 0;
  10         18  
  10         1031  
37 10     10   126 use constant TIP_PREPOSITION => 1;
  10         19  
  10         523  
38 10     10   50 use constant TIP_INCLUDE_ATTRIBUTES => 2;
  10         25  
  10         558  
39 10     10   47 use constant TIP_EXCLUDE_ATTRIBUTES => 3;
  10         26  
  10         606  
40 10     10   54 use constant TIP_TOC => 4;
  10         22  
  10         409  
41              
42 10     10   51 use constant MODE_DO_NOTHING => 0; # 0b00
  10         21  
  10         490  
43 10     10   61 use constant MODE_DO_INSERT => 1; # 0b01
  10         25  
  10         494  
44 10     10   52 use constant MODE_DO_PROPAGATE => 3; # 0b11
  10         75  
  10         44806  
45              
46 10     10   53 END {}
47              
48              
49             #--- HTML::TocInsertor::new() -------------------------------------------------
50             # function: Constructor.
51              
52             sub new {
53             # Get arguments
54 25     25 1 173 my ($aType) = @_;
55 25         182 my $self = $aType->SUPER::new;
56             # TRUE if insertion point token must be output, FALSE if not
57 25         65 $self->{_doOutputInsertionPointToken} = 1;
58             # True if anchor name is being written to output
59 25         57 $self->{_writingAnchorName} = 0;
60             # True if anchor name-begin is being written to output
61 25         69 $self->{_writingAnchorNameBegin} = 0;
62             # Reset batch variables
63 25         69 $self->_resetBatchVariables;
64             # Bias to not insert ToC
65 25         55 $self->{hti__Mode} = MODE_DO_NOTHING;
66              
67             # TODO: Initialize output
68              
69 25         72 return $self;
70             } # new()
71              
72              
73             #--- HTML::TocInsertor::_deinitializeOutput() ---------------------------------
74             # function: Deinitialize output.
75              
76             sub _deinitializeOutput {
77             # Get arguments
78 57     57   100 my ($self) = @_;
79             # Filehandle is defined?
80 57 100       218 if (defined($self->{_outputFileHandle})) {
81             # Yes, filehandle is defined;
82             # Restore selected filehandle
83 5         24 select($self->{_oldFileHandle});
84             # Undefine filehandle, closing it automatically
85 5         18 undef $self->{_outputFileHandle};
86             }
87             } # _deinitializeOutput()
88              
89              
90              
91             #--- HTML::TocInsertor::_initializeOutput() -----------------------------------
92             # function: Initialize output.
93              
94             sub _initializeOutput {
95             # Get arguments
96 57     57   103 my ($self) = @_;
97             # Bias to write to outputfile
98 57         94 my $doOutputToFile = 1;
99              
100             # Is output specified?
101 57 100       219 if (defined($self->{options}{'output'})) {
102             # Yes, output is specified;
103             # Indicate to not output to outputfile
104 56         83 $doOutputToFile = 0;
105             # Alias output reference
106 56         179 $self->{_output} = $self->{options}{'output'};
107             # Clear output
108 56         88 ${$self->{_output}} = "";
  56         155  
109             }
110              
111             # Is output file specified?
112 57 100       224 if (defined($self->{options}{'outputFile'})) {
113             # Yes, output file is specified;
114             # Indicate to output to outputfile
115 5         9 $doOutputToFile = 1;
116             # Open file
117 5 50       827 open $self->{_outputFileHandle}, ">", $self->{options}{'outputFile'}
118             or die "Can't create $self->{options}{'outputFile'}: $!";
119              
120             # Backup currently selected filehandle
121 5         30 $self->{_oldFileHandle} = select;
122             # Set new default filehandle
123 5         32 select($self->{_outputFileHandle});
124             }
125              
126             # Alias output-to-file indicator
127 57         134 $self->{_doOutputToFile} = $doOutputToFile;
128             } # _initializeOutput()
129              
130              
131             #--- HTML::TocInsertor::_deinitializeInsertorBatch() --------------------------
132             # function: Deinitialize insertor batch.
133              
134             sub _deinitializeInsertorBatch {
135             # Get arguments
136 57     57   100 my ($self) = @_;
137             # Indicate ToC insertion has finished
138 57         110 $self->{_isTocInsertionPointPassed} = 0;
139             # Write buffered output
140 57         172 $self->_writeBufferedOutput();
141             # Propagate?
142 57 100       193 if ($self->{hti__Mode} == MODE_DO_PROPAGATE) {
143             # Yes, propagate;
144             # Deinitialize generator batch
145 51         1188 $self->_deinitializeGeneratorBatch();
146             }
147             else {
148             # No, insert only;
149             # Do general batch deinitialization
150 6         24 $self->_deinitializeBatch();
151             }
152             # Deinitialize output
153 57         245 $self->_deinitializeOutput();
154             # Indicate end of batch
155 57         573 $self->{hti__Mode} = MODE_DO_NOTHING;
156             # Reset batch variables
157 57         192 $self->_resetBatchVariables();
158             } # _deinitializeInsertorBatch()
159              
160              
161             #--- HTML::TocInsertor::_initializeInsertorBatch() ----------------------------
162             # function: Initialize insertor batch.
163             # args: - $aTocs: Reference to array of tocs.
164             # - $aOptions: optional options
165              
166             sub _initializeInsertorBatch {
167             # Get arguments
168 57     57   122 my ($self, $aTocs, $aOptions) = @_;
169             # Add invocation options
170 57         289 $self->setOptions($aOptions);
171             # Option 'doGenerateToc' specified?
172 57 100       249 if (!defined($self->{options}{'doGenerateToc'})) {
173             # No, options 'doGenerateToc' not specified;
174             # Default to 'doGenerateToc'
175 24         78 $self->{options}{'doGenerateToc'} = 1;
176             }
177             # Propagate?
178 57 100       188 if ($self->{options}{'doGenerateToc'}) {
179             # Yes, propagate;
180             # Indicate mode
181 51         95 $self->{hti__Mode} = MODE_DO_PROPAGATE;
182             # Initialize generator batch
183             # NOTE: This method takes care of calling '_initializeBatch()'
184 51         233 $self->_initializeGeneratorBatch($aTocs);
185             }
186             else {
187             # No, insert;
188             # Indicate mode
189 6         16 $self->{hti__Mode} = MODE_DO_INSERT;
190             # Do general batch initialization
191 6         28 $self->_initializeBatch($aTocs);
192             }
193             # Initialize output
194 57         252 $self->_initializeOutput();
195             # Parse ToC insertion points
196 57         234 $self->_parseTocInsertionPoints();
197             } # _initializeInsertorBatch()
198              
199              
200             #--- HTML::TocInsertor::_insert() ---------------------------------------------
201             # function: Insert ToC in string.
202             # args: - $aString: Reference to string to parse.
203             # note: Used internally.
204              
205             sub _insert {
206             # Get arguments
207 44     44   94 my ($self, $aString) = @_;
208             # Propagate?
209 44 100       237 if ($self->{options}{'doGenerateToc'}) {
210             # Yes, propagate;
211             # Generate & insert ToC
212 38         213 $self->_generate($aString);
213             }
214             else {
215             # No, just insert ToC
216             # Insert by parsing file
217 6         24 $self->parse($aString);
218             # Flush remaining buffered text
219 6         29 $self->eof();
220             }
221             } # _insert()
222              
223              
224             #--- HTML::TocInsertor::_insertIntoFile() -------------------------------------
225             # function: Do insert generated ToCs in file.
226             # args: - $aToc: (reference to array of) ToC object(s) to insert.
227             # - $aFile: (reference to array of) file(s) to parse for insertion
228             # points.
229             # - $aOptions: optional insertor options
230             # note: Used internally.
231              
232             sub _insertIntoFile {
233             # Get arguments
234 13     13   31 my ($self, $aFile) = @_;
235             # Local variables;
236 13         21 my ($file, @files);
237             # Dereference array reference or make array of file specification
238 13 50       56 @files = (ref($aFile) =~ m/ARRAY/) ? @$aFile : ($aFile);
239             # Loop through files
240 13         26 foreach $file (@files) {
241             # Propagate?
242 13 50       40 if ($self->{options}{'doGenerateToc'}) {
243             # Yes, propagate;
244             # Generate and insert ToC
245 13         90 $self->_generateFromFile($file);
246             } else {
247             # No, just insert ToC
248             # Insert by parsing file
249 0         0 $self->parse_file($file);
250             }
251             }
252             } # _insertIntoFile()
253              
254              
255             #--- HTML::TocInsertor::_parseTocInsertionPoints() ----------------------------
256             # function: Parse ToC insertion point specifier.
257              
258             sub _parseTocInsertionPoints {
259             # Get arguments
260 57     57   94 my ($self) = @_;
261             # Local variables
262 57         91 my ($tipPreposition, $tipToken, $toc, $tokenTipParser);
263             # Create parser for TIP tokens
264 57         716 $tokenTipParser = HTML::_TokenTipParser->new(
265             $self->{_tokensTip}
266             );
267             # Loop through ToCs
268 57         108 foreach $toc (@{$self->{_tocs}}) {
  57         151  
269 64 50       248 if (length $toc->{options}{'insertionPoint'}) {
270             # Split TIP in preposition and token
271 64         315 ($tipPreposition, $tipToken) = split(
272             '\s+', $toc->{options}{'insertionPoint'}, 2
273             );
274             # Known preposition?
275 64 100 100     502 if (
      100        
276             ($tipPreposition ne TIP_PREPOSITION_REPLACE) &&
277             ($tipPreposition ne TIP_PREPOSITION_BEFORE) &&
278             ($tipPreposition ne TIP_PREPOSITION_AFTER)
279             ) {
280             # No, unknown preposition;
281             # Use default 'after '
282 3         4 $tipPreposition = TIP_PREPOSITION_AFTER;
283             # Use entire 'insertionPoint' as token
284 3         7 $tipToken = $toc->{options}{'insertionPoint'};
285             } # if
286             } else {
287             # No, insertion point is empty string;
288             # Use default `after '
289 0         0 $tipPreposition = TIP_PREPOSITION_AFTER;
290 0         0 $tipToken = '';
291             } # if
292             # Indicate current ToC to parser
293 64         217 $tokenTipParser->setToc($toc);
294             # Indicate current preposition to parser
295 64         180 $tokenTipParser->setPreposition($tipPreposition);
296             # Parse ToC Insertion Point
297 64         501 $tokenTipParser->parse($tipToken);
298             # Flush remaining buffered text
299 64         723 $tokenTipParser->eof();
300             }
301             } # _parseTocInsertionPoints()
302              
303              
304             #--- HTML::TocInsertor::_processTokenAsInsertionPoint() -----------------------
305             # function: Check for token being a ToC insertion point (Tip) token and
306             # process it accordingly.
307             # args: - $aTokenType: type of token: start, end, comment or text.
308             # - $aTokenId: token id of currently parsed token
309             # - $aTokenAttributes: attributes of currently parsed token
310             # - $aOrigText: complete token
311             # returns: 1 if successful -- token is processed as insertion point, 0
312             # if not.
313              
314             sub _processTokenAsInsertionPoint {
315             # Get arguments
316 1441     1441   2534 my ($self, $aTokenType, $aTokenId, $aTokenAttributes, $aOrigText) = @_;
317             # Local variables
318 1441         1586 my ($i, $result, $tipToken, $tipTokenId, $tipTokens);
319             # Does token happen to be a ToC token, or is tip-tokentype <> TEXT?
320 1441 100 100     4935 if ($self->{_doReleaseElement} || $aTokenType != HTML::TocGenerator::TT_TOKENTYPE_TEXT) {
321             # No, token isn't a ToC token;
322             # Bias to token not functioning as a ToC insertion point (Tip) token
323 1243         1551 $result = 0;
324             # Alias ToC insertion point (Tip) array of right type
325 1243         2077 $tipTokens = $self->{_tokensTip}[$aTokenType];
326             # Loop through tipTokens
327 1243         1392 $i = 0;
328 1243         1402 while ($i < scalar @{$tipTokens}) {
  1347         3933  
329             # Aliases
330 104         164 $tipToken = $tipTokens->[$i];
331 104         181 $tipTokenId = $tipToken->[TIP_TOKEN_ID];
332             # Id & attributes match?
333 104 100 33     1012 if (
      66        
334             ($aTokenId =~ m/$tipTokenId/) && (
335             HTML::TocGenerator::_doesHashContainHash(
336             $aTokenAttributes, $tipToken->[TIP_INCLUDE_ATTRIBUTES], 0
337             ) &&
338             HTML::TocGenerator::_doesHashContainHash(
339             $aTokenAttributes, $tipToken->[TIP_EXCLUDE_ATTRIBUTES], 1
340             )
341             )
342             ) {
343             # Yes, id and attributes match;
344             # Process ToC insertion point
345 59         346 $self->_processTocInsertionPoint($tipToken, $aTokenType);
346             # Indicate token functions as ToC insertion point
347 59         86 $result = 1;
348             # Remove Tip token, automatically advancing to next token
349 59         217 splice(@$tipTokens, $i, 1);
350             } else {
351             # No, tag doesn't match ToC insertion point
352             # Advance to next start token
353 45         71 $i++;
354             } # if
355             } # while
356             # Token functions as ToC insertion point?
357 1243 100       2805 if ($result) {
358             # Yes, token functions as ToC insertion point;
359             # Process insertion point(s)
360 58         200 $self->_processTocInsertionPoints($aOrigText);
361             } # if
362             } else {
363 198         262 $result = 0;
364 1441 0       4542 } // if
365             # Return value
366             return $result;
367             } # _processTokenAsInsertionPoint()
368              
369              
370             #--- HTML::TocInsertor::toc() -------------------------------------------------
371             # function: Toc processing method. Add toc reference to scenario.
372             # args: - $aScenario: Scenario to add ToC reference to.
373             # - $aToc: Reference to ToC to insert.
374             # note: The ToC hasn't been build yet; only a reference to the ToC to be
375             # build is inserted.
376              
377             sub toc {
378             # Get arguments
379 60     60 0 107 my ($self, $aScenario, $aToc) = @_;
380             # Add toc to scenario
381 60         129 push(@$aScenario, $aToc);
382             } # toc()
383              
384              
385             #--- HTML::TocInsertor::_processTocInsertionPoint() ----------------------------
386             # function: Process ToC insertion point.
387             # args: - $aTipToken: Reference to token array item which matches the ToC
388             # insertion point.
389             # - $aTokenType: type of token: start, end, comment or text.
390              
391             sub _processTocInsertionPoint {
392             # Get arguments
393 59     59   122 my ($self, $aTipToken, $aTokenType) = @_;
394             # Local variables
395 59         143 my ($tipToc, $tipPreposition);
396            
397             # Aliases
398 59         106 $tipToc = $aTipToken->[TIP_TOC];
399 59         238 $tipPreposition = $aTipToken->[TIP_PREPOSITION];
400              
401             # If TipToken is of type TEXT, prepend possible preceding string
402 59 100 100     240 if ($aTokenType == HTML::TocGenerator::TT_TOKENTYPE_TEXT && length $`) {
403 4         8 my $prepend = $`;
404 4         8 push(@{$self->{_scenarioBeforeToken}}, \$prepend);
  4         10  
405             } # if
406              
407             SWITCH: {
408             # Replace token with ToC?
409 59 100       78 if ($tipPreposition eq TIP_PREPOSITION_REPLACE) {
  59         160  
410             # Yes, replace token;
411             # Indicate ToC insertion point has been passed
412 13         27 $self->{_isTocInsertionPointPassed} = 1;
413             # Add ToC reference to scenario reference by calling 'toc' method
414 13         45 $self->toc($self->{_scenarioAfterToken}, $tipToc);
415             # Indicate token itself must not be output
416 13         22 $self->{_doOutputInsertionPointToken} = 0;
417 13         38 last SWITCH;
418             } # if
419             # Output ToC before token?
420 46 100       112 if ($tipPreposition eq TIP_PREPOSITION_BEFORE) {
421             # Yes, output ToC before token;
422             # Indicate ToC insertion point has been passed
423 23         50 $self->{_isTocInsertionPointPassed} = 1;
424             # Add ToC reference to scenario reference by calling 'toc' method
425 23         92 $self->toc($self->{_scenarioBeforeToken}, $tipToc);
426             # Add token text
427 23 100       52 if ($aTokenType == HTML::TocGenerator::TT_TOKENTYPE_TEXT) {
428 1         4 my $text = $&;
429 1         2 push(@{$self->{_scenarioBeforeToken}}, \$text);
  1         2  
430 1         3 $self->{_doOutputInsertionPointToken} = 0;
431             } else {
432 22         64 $self->{_doOutputInsertionPointToken} = ! $self->{_isTocToken};
433             } # if
434 23         54 last SWITCH;
435             } # if
436             # Output ToC after token?
437 23 50       59 if ($tipPreposition eq TIP_PREPOSITION_AFTER) {
438             # Yes, output ToC after token;
439             # Indicate ToC insertion point has been passed
440 23         45 $self->{_isTocInsertionPointPassed} = 1;
441             # Add token text
442 23 100       57 if ($aTokenType == HTML::TocGenerator::TT_TOKENTYPE_TEXT) {
443 1         3 my $text = $&;
444 1         4 $self->toc($self->{_scenarioAfterToken}, \$text);
445 1         2 $self->{_doOutputInsertionPointToken} = 0;
446             } else {
447 22         66 $self->{_doOutputInsertionPointToken} = ! $self->{_isTocToken};
448             } # if
449             # Add ToC reference to scenario reference by calling 'toc' method
450 23         92 $self->toc($self->{_scenarioAfterToken}, $tipToc);
451 23         171 last SWITCH;
452             } # if
453             } # SWITCH
454              
455             # If TipToken is of type TEXT, append possible following string
456 59 100 100     260 if ($aTokenType == HTML::TocGenerator::TT_TOKENTYPE_TEXT && length $') {
457 6         13 my $append = $';
458 6         21 push(@{$self->{_scenarioAfterToken}}, \$append);
  6         29  
459             } # if
460             } # _processTocInsertionPoint()
461              
462              
463             #--- HTML::TocInsertor::_processTocInsertionPoints() --------------------------
464             # function: Process ToC insertion points
465             # args: - $aTokenText: Text of token which acts as insertion point for one
466             # or multiple ToCs.
467              
468             sub _processTocInsertionPoints {
469             # Get arguments
470 58     58   116 my ($self, $aTokenText) = @_;
471             # Local variables
472 58         80 my ($outputPrefix, $outputSuffix);
473             # Extend scenario
474 58         82 push(@{$self->{_scenario}}, @{$self->{_scenarioBeforeToken}});
  58         123  
  58         126  
475              
476 58 50       188 if ($outputPrefix = $self->{_outputPrefix}) {
477 0         0 push(@{$self->{_scenario}}, \$outputPrefix);
  0         0  
478             #$self->_writeOrBufferOutput(\$outputPrefix);
479 0         0 $self->{_outputPrefix} = "";
480             }
481              
482             # Must insertion point token be output?
483 58 100       367 if ($self->{_doOutputInsertionPointToken}) {
484             # Yes, output insertion point token;
485 24         33 push(@{$self->{_scenario}}, \$aTokenText);
  24         68  
486             #$self->_writeOrBufferOutput(\$aTokenText);
487             }
488              
489 58 50       203 if ($outputSuffix = $self->{_outputSuffix}) {
490 0         0 push(@{$self->{_scenario}}, \$outputSuffix);
  0         0  
491             #$self->_writeOrBufferOutput(\$outputSuffix);
492 0         0 $self->{_outputSuffix} = "";
493             }
494              
495 58         91 push(@{$self->{_scenario}}, @{$self->{_scenarioAfterToken}});
  58         142  
  58         122  
496             # Add new act to scenario for output to come
497 58         107 my $output = "";
498 58         74 push(@{$self->{_scenario}}, \$output);
  58         118  
499             # Write output, processing possible '_outputSuffix'
500             #$self->_writeOrBufferOutput("");
501             # Reset helper scenario's
502 58         127 $self->{_scenarioBeforeToken} = [];
503 58         120 $self->{_scenarioAfterToken} = [];
504             # Reset bias value to output insertion point token
505 58         226 $self->{_doOutputInsertionPointToken} = 1;
506             } # _processTocInsertionPoints()
507              
508              
509             #--- HTML::Toc::_resetBatchVariables() ----------------------------------------
510             # function: Reset batch variables.
511              
512             sub _resetBatchVariables {
513 158     158   249 my ($self) = @_;
514             # Call ancestor
515 158         785 $self->SUPER::_resetBatchVariables();
516             # Array containing references to scalars. This array depicts the order
517             # in which output must be performed after the first ToC Insertion Point
518             # has been passed.
519 158         337 $self->{_scenario} = [];
520             # Helper scenario
521 158         315 $self->{_scenarioBeforeToken} = [];
522             # Helper scenario
523 158         277 $self->{_scenarioAfterToken} = [];
524             # Arrays containing start, end, comment, text & declaration tokens which
525             # must trigger the ToC insertion. Each array element may contain a
526             # reference to an array containing the following elements:
527 158         475 $self->{_tokensTip} = [
528             [], # TT_TOKENTYPE_START
529             [], # TT_TOKENTYPE_END
530             [], # TT_TOKENTYPE_COMMENT
531             [], # TT_TOKENTYPE_TEXT
532             [] # TT_TOKENTYPE_DECLARATION
533             ];
534             # 1 if ToC insertion point has been passed, 0 if not
535 158         339 $self->{_isTocInsertionPointPassed} = 0;
536             # Tokens after ToC
537 158         241 $self->{outputBuffer} = "";
538             # Trailing text after parsed token
539 158         219 $self->{_outputSuffix} = "";
540             # Preceding text before parsed token
541 158         441 $self->{_outputPrefix} = "";
542             } # _resetBatchVariables()
543              
544              
545             #--- HTML::TocInsertor::_writeBufferedOutput() --------------------------------
546             # function: Write buffered output to output device(s).
547              
548             sub _writeBufferedOutput {
549             # Get arguments
550 57     57   92 my ($self) = @_;
551             # Local variables
552 57         247 my ($scene);
553             # Must ToC be parsed?
554 57 50       188 if ($self->{options}{'parseToc'}) {
555             # Yes, ToC must be parsed;
556             # Parse ToC
557             #$self->parse($self->{toc});
558             # Output tokens after ToC
559             #$self->_writeOrBufferOutput($self->{outputBuffer});
560             }
561             else {
562             # No, ToC needn't be parsed;
563             # Output scenario
564 57         91 foreach $scene (@{$self->{_scenario}}) {
  57         145  
565             # Is scene a reference to a scalar?
566 185 100       556 if (ref($scene) eq "SCALAR") {
567             # Yes, scene is a reference to a scalar;
568             # Output scene
569 126         352 $self->_writeOutput($$scene);
570             }
571             else {
572             # No, scene must be reference to HTML::Toc;
573             # Output toc
574 59         251 $self->_writeOutput($scene->format());
575             }
576             }
577             }
578             } # _writeBufferedOutput()
579              
580              
581             #--- HTML::TocInsertor::_writeOrBufferOutput() --------------------------------
582             # function: Write processed HTML to output device(s).
583             # args: - aOutput: scalar to write
584             # note: If '_isTocInsertionPointPassed' text is buffered before being
585             # output because the ToC has to be generated before it can be output.
586             # Only after the entire data has been parsed, the ToC and the
587             # following text will be output.
588              
589             sub _writeOrBufferOutput {
590             # Get arguments
591 1402     1402   2052 my ($self, $aOutput) = @_;
592              
593             # Add possible output prefix and suffix
594 1402         2987 $aOutput = $self->{_outputPrefix} . $aOutput . $self->{_outputSuffix};
595             # Clear output prefix and suffix
596 1402         3374 $self->{_outputPrefix} = "";
597 1402         2153 $self->{_outputSuffix} = "";
598              
599 1402 100       5074 if ($self->{_doReleaseElement}) {
600             # Has ToC insertion point been passed?
601 1152 100       2201 if ($self->{_isTocInsertionPointPassed}) {
602             # Yes, ToC insertion point has been passed;
603             # Buffer output; add output to last '_scenario' item
604 981         1047 my $index = scalar(@{$self->{_scenario}}) - 1;
  981         2314  
605 981         1178 ${$self->{_scenario}[$index]} .= $aOutput;
  981         6932  
606             } else {
607             # No, ToC insertion point hasn't been passed;
608             # Write output
609 171         369 $self->_writeOutput($aOutput);
610             } # if
611             } # if
612             } # _writeOrBufferOutput()
613              
614              
615             #--- HTML::TocInsertor::_writeOutput() ----------------------------------------
616             # function: Write processed HTML to output device(s).
617             # args: - aOutput: scalar to write
618              
619             sub _writeOutput {
620             # Get arguments
621 356     356   556 my ($self, $aOutput) = @_;
622             # Write output to scalar;
623 356 100       815 ${$self->{_output}} .= $aOutput if (defined($self->{_output}));
  343         832  
624             # Write output to output file
625 356 100       4736 print $aOutput if ($self->{_doOutputToFile})
626             } # _writeOutput()
627              
628              
629             #--- HTML::TocGenerator::anchorId() -------------------------------------------
630             # function: Anchor id processing method.
631             # args: - $aAnchorId
632              
633             sub anchorId {
634             # Get arguments
635 0     0 0 0 my ($self, $aAnchorId) = @_;
636             # Indicate id must be added to start tag
637 0         0 $self->{_doAddAnchorIdToStartTag} = 1;
638 0         0 $self->{_anchorId} = $aAnchorId;
639             } # anchorId()
640              
641              
642             #--- HTML::TocInsertor::afterAnchorNameBegin() -------------------------
643             # Extend ancestor method.
644             # @see HTML::TocGenerator::afterAnchorNameBegin
645              
646             sub afterAnchorNameBegin {
647             # Get arguments
648 173     173 0 387 my ($self, $aAnchorNameBegin, $aToc) = @_;
649             # Store anchor name as output suffix
650             #$self->{_outputSuffix} = $aAnchorNameBegin;
651 173         444 $self->{_holdChildren} = $aAnchorNameBegin . $self->{_holdChildren};
652             # Indicate anchor name is being written
653 173         259 $self->{_writingAnchorNameBegin} = 1;
654             # Indicate anchor name end must be output
655 173         450 $self->{_doOutputAnchorNameEnd} = 1;
656             } # afterAnchorNameBegin()
657              
658              
659             #--- HTML::TocInsertor::anchorNameEnd() ---------------------------------------
660             # function: Process anchor name end, generated by HTML::TocGenerator.
661             # args: - $aAnchorNameEnd: Anchor name end tag to output.
662             # - $aToc: Reference to ToC to which anchorname belongs.
663              
664             sub anchorNameEnd {
665             # Get arguments
666 173     173 0 281 my ($self, $aAnchorNameEnd) = @_;
667             # Store anchor name as output prefix
668 173         367 $self->{_outputPrefix} .= $aAnchorNameEnd;
669             # Is anchor-name-begin being output this parsing round as well?
670 173 50       508 if ($self->{_writingAnchorNameBegin}) {
671             # Yes, anchor-name-begin is being output as well;
672             # Indicate both anchor name begin and anchor name end are being written
673 173         1114 $self->{_writingAnchorName} = 1;
674             } # if
675             } # anchorNameEnd()
676              
677              
678             #--- HTML::TocInsertor::comment() ---------------------------------------------
679             # function: Process comment.
680             # args: - $aComment: comment text with '' tags stripped off.
681              
682             sub comment {
683             # Get arguments
684 17     17 1 59 my ($self, $aComment) = @_;
685             # Local variables
686 17         21 my ($tocInsertionPointToken, $doOutput, $origText);
687             # Allow ancestor to process the comment tag
688 17         71 $self->SUPER::comment($aComment);
689             # Assemble original comment
690 17         36 $origText = "";
691             # Must ToCs be inserted?
692 17 50       49 if ($self->{hti__Mode} & MODE_DO_INSERT) {
693             # Yes, ToCs must be inserted;
694             # Processing comment as ToC insertion point is successful?
695 17 100       41 if (! $self->_processTokenAsInsertionPoint(
696             HTML::TocGenerator::TT_TOKENTYPE_COMMENT, $aComment, undef, $origText
697             )) {
698             # No, comment isn't a ToC insertion point;
699             # Output comment normally
700 6         19 $self->_writeOrBufferOutput($origText);
701             }
702             }
703             } # comment()
704              
705              
706             #--- HTML::TocInsertor::declaration() -----------------------------------------
707             # function: This function is called every time a declaration is encountered
708             # by HTML::Parser.
709              
710             sub declaration {
711             # Get arguments
712 4     4 1 47 my ($self, $aDeclaration) = @_;
713             # Allow ancestor to process the declaration tag
714 4         39 $self->SUPER::declaration($aDeclaration);
715             # Must ToCs be inserted?
716 4 50       19 if ($self->{hti__Mode} & MODE_DO_INSERT) {
717             # Yes, ToCs must be inserted;
718             # Processing declaration as ToC insertion point is successful?
719 4 50       33 if (! $self->_processTokenAsInsertionPoint(
720             HTML::TocGenerator::TT_TOKENTYPE_DECLARATION, $aDeclaration, undef,
721             ""
722             )) {
723             # No, declaration isn't a ToC insertion point;
724             # Output declaration normally
725 4         27 $self->_writeOrBufferOutput("");
726             }
727             }
728             } # declaration()
729              
730              
731             #--- HTML::TocInsertor::end() -------------------------------------------------
732             # function: This function is called every time a closing tag is encountered
733             # by HTML::Parser.
734             # args: - $aTag: tag name (in lower case).
735              
736             sub end {
737             # Get arguments
738 351     351 1 552 my ($self, $aTag, $aOrigText) = @_;
739             # Allow ancestor to process the end tag
740 351         1233 $self->SUPER::end($aTag, $aOrigText);
741             # Must ToCs be inserted?
742 351 50       877 if ($self->{hti__Mode} & MODE_DO_INSERT) {
743             # Yes, ToCs must be inserted;
744             # Processing end tag as ToC insertion point is successful?
745 351 100       990 if (! $self->_processTokenAsInsertionPoint(
746             HTML::TocGenerator::TT_TOKENTYPE_END, $aTag, undef, $aOrigText
747             )) {
748             # No, end tag isn't a ToC insertion point;
749             # Output end tag normally
750 349         1045 $self->_writeOrBufferOutput($aOrigText);
751             }
752             }
753             } # end()
754              
755              
756             #--- HTML::TocInsertor::insert() ----------------------------------------------
757             # function: Insert ToC in string.
758             # args: - $aToc: (reference to array of) ToC object to insert
759             # - $aString: string to insert ToC in
760             # - $aOptions: hash reference with optional insertor options
761              
762             sub insert {
763             # Get arguments
764 39     39 0 468 my ($self, $aToc, $aString, $aOptions) = @_;
765             # Initialize TocInsertor batch
766 39         184 $self->_initializeInsertorBatch($aToc, $aOptions);
767             # Do insert Toc
768 39         222 $self->_insert($aString);
769             # Deinitialize TocInsertor batch
770 39         140 $self->_deinitializeInsertorBatch();
771             } # insert()
772              
773              
774             #--- HTML::TocInsertor::insertIntoFile() --------------------------------------
775             # function: Insert ToCs in file.
776             # args: - $aToc: (reference to array of) ToC object(s) to insert.
777             # - $aFile: (reference to array of) file(s) to parse for insertion
778             # points.
779             # - $aOptions: optional insertor options
780              
781             sub insertIntoFile {
782             # Get arguments
783 6     6 0 1237 my ($self, $aToc, $aFile, $aOptions) = @_;
784             # Initialize TocInsertor batch
785 6         26 $self->_initializeInsertorBatch($aToc, $aOptions);
786             # Do insert ToCs into file
787 6         26 $self->_insertIntoFile($aFile);
788             # Deinitialize TocInsertor batch
789 6         23 $self->_deinitializeInsertorBatch();
790             } # insertIntoFile()
791              
792              
793             #--- HTML::TocInsertor::number() ----------------------------------------------
794             # function: Process heading number generated by HTML::Toc.
795             # args: - $aNumber
796              
797             sub number {
798             # Get arguments
799 88     88 0 180 my ($self, $aNumber, $aToc) = @_;
800             # Store heading number as output suffix
801             #$self->{_outputSuffix} .= $aNumber;
802             #$self->_writeOrBufferOutput($aNumber);
803 88         312 $self->{_holdChildren} = $aNumber . $self->{_holdChildren};
804             } # number()
805              
806              
807             #--- HTML::TocInsertor::_processTocStartingToken() ---------------------------
808             # Extend ancestor method.
809              
810             sub _processTocStartingToken {
811             # Get arguments
812 177     177   624 my ($self, $aTocToken, $aTokenType, $aTokenAttributes, $aTokenOrig) = @_;
813 177         692 $self->SUPER::_processTocStartingToken($aTocToken, $aTokenType, $aTokenAttributes, $aTokenOrig);
814             # Was attribute used as ToC text?
815 177 100       397 if (defined($aTocToken->[HTML::TocGenerator::TT_ATTRIBUTES_TOC])) {
816             # Yes, attribute was used as ToC text;
817             # Output children - containing anchor name only - before toc element
818 4         13 $self->_writeOrBufferOutput($self->{_holdChildren} . $self->{_holdBeginTokenOrig});
819             } else {
820             # No, attribute wasn't used as ToC text;
821             # Output children - including anchor name - within toc element
822 173         1045 $self->_writeOrBufferOutput($self->{_holdBeginTokenOrig} . $self->{_holdChildren});
823             } # if
824             } # _processTocStartingToken()
825              
826              
827             #--- HTML::TocInsertor::propagateFile() ---------------------------------------
828             # function: Propagate ToC; generate & insert ToC, using file as input.
829             # args: - $aToc: (reference to array of) ToC object to insert
830             # - $aFile: (reference to array of) file to parse for insertion
831             # points.
832             # - $aOptions: optional insertor options
833              
834             sub propagateFile {
835             # Get arguments
836 0     0 0 0 my ($self, $aToc, $aFile, $aOptions) = @_;
837             # Local variables;
838 0         0 my ($file, @files);
839             # Initialize TocInsertor batch
840 0         0 $self->_initializeInsertorBatch($aToc, $aOptions);
841             # Dereference array reference or make array of file specification
842 0 0       0 @files = (ref($aFile) =~ m/ARRAY/) ? @$aFile : ($aFile);
843             # Loop through files
844 0         0 foreach $file (@files) {
845             # Generate and insert ToC
846 0         0 $self->_generateFromFile($file);
847             }
848             # Deinitialize TocInsertor batch
849 0         0 $self->_deinitializeInsertorBatch();
850             } # propagateFile()
851              
852              
853             #--- HTML::TocInsertor::start() -----------------------------------------------
854             # function: This function is called every time an opening tag is encountered.
855             # args: - $aTag: tag name (in lower case).
856             # - $aAttr: reference to hash containing all tag attributes (in lower
857             # case).
858             # - $aAttrSeq: reference to array containing all tag attributes (in
859             # lower case) in the original order
860             # - $aTokenOrig: the original token string
861              
862             sub start {
863             # Get arguments
864 384     384 1 772 my ($self, $aTag, $aAttr, $aAttrSeq, $aTokenOrig) = @_;
865             # Local variables
866 384         485 my ($doOutput, $i, $tocToken, $tag, $anchorId);
867             # Let ancestor process the start tag
868 384         1389 $self->SUPER::start($aTag, $aAttr, $aAttrSeq, $aTokenOrig);
869             # Must ToC be inserted?
870 384 50       1096 if ($self->{hti__Mode} & MODE_DO_INSERT) {
871             # Yes, ToC must be inserted;
872             # Processing start tag as ToC insertion point is successful?
873 384 100       968 if (! $self->_processTokenAsInsertionPoint(
874             HTML::TocGenerator::TT_TOKENTYPE_START, $aTag, $aAttr, $aTokenOrig
875             )) {
876             # No, start tag isn't a ToC insertion point;
877             # Add anchor id?
878 350 50       877 if ($self->{_doAddAnchorIdToStartTag}) {
879             # Yes, anchor id must be added;
880             # Reset indicator;
881 0         0 $self->{_doAddAnchorIdToStartTag} = 0;
882             # Alias anchor id
883 0         0 $anchorId = $self->{_anchorId};
884             # Attribute 'id' already exists?
885 0 0       0 if (defined($aAttr->{id})) {
886             # Yes, attribute 'id' already exists;
887             # Show warning
888 0         0 print STDERR "WARNING: Overwriting existing id attribute '" .
889             $aAttr->{id} . "' of tag $aTokenOrig\n";
890            
891             # Add anchor id to start tag
892 0         0 $aTokenOrig =~ s/(id)=\S*([\s>])/$1=$anchorId$2/i;
893             }
894             else {
895             # No, attribute 'id' doesn't exist;
896             # Add anchor id to start tag
897 0         0 $aTokenOrig =~ s/>/ id=$anchorId>/;
898             }
899             } # if
900             # Is start tag a ToC token?
901 350 100       1827 if (! $self->{_isTocToken}) {
902             # No, start tag isn't a ToC token;
903             # Output start tag normally
904 192         550 $self->_writeOrBufferOutput($aTokenOrig);
905             } # if
906             }
907             }
908             } # start()
909              
910              
911             #--- HTML::TocInsertor::text() ------------------------------------------------
912             # function: This function is called every time plain text is encountered.
913             # args: - @_: array containing data.
914              
915             sub text {
916             # Get arguments
917 685     685 1 2057 my ($self, $aText) = @_;
918             # Let ancestor process the text
919 685         2065 $self->SUPER::text($aText);
920             # Must ToC be inserted?
921 685 50       2034 if ($self->{hti__Mode} & MODE_DO_INSERT) {
922             # Yes, ToC must be inserted;
923             # Processing text as ToC insertion point is successful?
924 685 100       1469 if (! $self->_processTokenAsInsertionPoint(
925             HTML::TocGenerator::TT_TOKENTYPE_TEXT, $aText, undef, $aText
926             )) {
927             # No, text isn't a ToC insertion point;
928             # Output text normally
929 674         1555 $self->_writeOrBufferOutput($aText);
930             }
931             }
932             } # text()
933              
934              
935              
936              
937             #=== HTML::_TokenTipParser ====================================================
938             # function: Parse 'TIP tokens'. 'TIP tokens' mark HTML code which is to be
939             # used as the ToC Insertion Point.
940             # note: Used internally.
941              
942             package HTML::_TokenTipParser;
943              
944              
945             BEGIN {
946 10     10   107 use vars qw(@ISA);
  10         20  
  10         943  
947              
948 10     10   7899 @ISA = qw(HTML::_TokenTocParser);
949             }
950              
951              
952 10     10   7508 END {}
953              
954              
955             #--- HTML::_TokenTipParser::new() ---------------------------------------------
956             # function: Constructor
957              
958             sub new {
959             # Get arguments
960 57     57   137 my ($aType, $aTokenArray) = @_;
961             # Create instance
962 57         256 my $self = $aType->SUPER::new;
963             # Reference token array
964 57         187 $self->{tokens} = $aTokenArray;
965             # Reference to last added token
966 57         127 $self->{_lastAddedToken} = undef;
967 57         102 $self->{_lastAddedTokenType} = undef;
968             # Return instance
969 57         138 return $self;
970             } # new()
971              
972              
973             #--- HTML::_TokenTipParser::_processAttributes() ------------------------------
974             # function: Process attributes.
975             # args: - $aAttributes: Attributes to parse.
976              
977             sub _processAttributes {
978             # Get arguments
979 39     39   59 my ($self, $aAttributes) = @_;
980             # Local variables
981 39         55 my (%includeAttributes, %excludeAttributes);
982              
983             # Parse attributes
984 39         216 $self->_parseAttributes(
985             $aAttributes, \%includeAttributes, \%excludeAttributes
986             );
987             # Include attributes are specified?
988 39 50       124 if (keys(%includeAttributes) > 0) {
989             # Yes, include attributes are specified;
990             # Store include attributes
991 0         0 @${$self->{_lastAddedToken}}[
  0         0  
992             HTML::TocInsertor::TIP_INCLUDE_ATTRIBUTES
993             ] = \%includeAttributes;
994             }
995             # Exclude attributes are specified?
996 39 50       231 if (keys(%excludeAttributes) > 0) {
997             # Yes, exclude attributes are specified;
998             # Store exclude attributes
999 0         0 @${$self->{_lastAddedToken}}[
  0         0  
1000             HTML::TocInsertor::TIP_EXCLUDE_ATTRIBUTES
1001             ] = \%excludeAttributes;
1002             }
1003             } # _processAttributes()
1004              
1005              
1006             #--- HTML::_TokenTipParser::_processToken() -----------------------------------
1007             # function: Process token.
1008             # args: - $aTokenType: Type of token to process.
1009             # - $aTag: Tag of token.
1010              
1011             sub _processToken {
1012             # Get arguments
1013 64     64   258 my ($self, $aTokenType, $aTag) = @_;
1014             # Local variables
1015 64         159 my ($tokenArray, $index);
1016             # Push element on array of update tokens
1017 64         90 $index = push(@{$self->{tokens}[$aTokenType]}, []) - 1;
  64         210  
1018             # Alias token array to add element to
1019 64         127 $tokenArray = $self->{tokens}[$aTokenType];
1020             # Indicate last updated token array element
1021 64         200 $self->{_lastAddedTokenType} = $aTokenType;
1022 64         130 $self->{_lastAddedToken} = \$$tokenArray[$index];
1023             # Add fields
1024 64         235 $$tokenArray[$index][HTML::TocInsertor::TIP_TOC] = $self->{_toc};
1025 64         153 $$tokenArray[$index][HTML::TocInsertor::TIP_TOKEN_ID] = $aTag;
1026 64         297 $$tokenArray[$index][HTML::TocInsertor::TIP_PREPOSITION] =
1027             $self->{_preposition};
1028             } # _processToken()
1029              
1030              
1031             #--- HTML::_TokenTipParser::comment() -----------------------------------------
1032             # function: Process comment.
1033             # args: - $aComment: comment text with '' tags stripped off.
1034              
1035             sub comment {
1036             # Get arguments
1037 11     11   77 my ($self, $aComment) = @_;
1038             # Process token
1039 11         31 $self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_COMMENT, $aComment);
1040             } # comment()
1041              
1042              
1043             #--- HTML::_TokenTipParser::declaration() --------------------------------
1044             # function: This function is called every time a markup declaration is
1045             # encountered by HTML::Parser.
1046             # args: - $aDeclaration: Markup declaration.
1047              
1048             sub declaration {
1049             # Get arguments
1050 0     0   0 my ($self, $aDeclaration) = @_;
1051             # Process token
1052 0         0 $self->_processToken(
1053             HTML::TocGenerator::TT_TOKENTYPE_DECLARATION, $aDeclaration
1054             );
1055             } # declaration()
1056              
1057            
1058             #--- HTML::_TokenTipParser::end() ----------------------------------------
1059             # function: This function is called every time a closing tag is encountered
1060             # by HTML::Parser.
1061             # args: - $aTag: tag name (in lower case).
1062              
1063             sub end {
1064             # Get arguments
1065 2     2   6 my ($self, $aTag, $aOrigText) = @_;
1066             # Process token
1067 2         10 $self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_END, $aTag);
1068             } # end()
1069              
1070              
1071             #--- HTML::_TokenTipParser->setPreposition() ----------------------------------
1072             # function: Set current preposition.
1073              
1074             sub setPreposition {
1075             # Get arguments
1076 64     64   107 my ($self, $aPreposition) = @_;
1077             # Set current ToC
1078 64         145 $self->{_preposition} = $aPreposition;
1079             } # setPreposition()
1080              
1081              
1082             #--- HTML::_TokenTipParser->setToc() ------------------------------------------
1083             # function: Set current ToC.
1084              
1085             sub setToc {
1086             # Get arguments
1087 64     64   104 my ($self, $aToc) = @_;
1088             # Set current ToC
1089 64         223 $self->{_toc} = $aToc;
1090             } # setToc()
1091              
1092              
1093             #--- HTML::_TokenTipParser::start() --------------------------------------
1094             # function: This function is called every time an opening tag is encountered.
1095             # args: - $aTag: tag name (in lower case).
1096             # - $aAttr: reference to hash containing all tag attributes (in lower
1097             # case).
1098             # - $aAttrSeq: reference to array containing all attribute keys (in
1099             # lower case) in the original order
1100             # - $aOrigText: the original HTML text
1101              
1102             sub start {
1103             # Get arguments
1104 39     39   90 my ($self, $aTag, $aAttr, $aAttrSeq, $aOrigText) = @_;
1105             # Process token
1106 39         137 $self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_START, $aTag);
1107             # Process attributes
1108 39         117 $self->_processAttributes($aAttr);
1109             } # start()
1110              
1111              
1112             #--- HTML::_TokenTipParser::text() ---------------------------------------
1113             # function: This function is called every time plain text is encountered.
1114             # args: - @_: array containing data.
1115              
1116             sub text {
1117             # Get arguments
1118 18     18   32 my ($self, $aText) = @_;
1119             # Was token already created and is last added token of type 'text'?
1120 18 100 66     82 if (
1121             defined($self->{_lastAddedToken}) &&
1122             $self->{_lastAddedTokenType} == HTML::TocGenerator::TT_TOKENTYPE_TEXT
1123             ) {
1124             # Yes, token is already created;
1125             # Add tag to existing token
1126 6         10 @${$self->{_lastAddedToken}}[HTML::TocGenerator::TT_TAG_BEGIN] .= $aText;
  6         72  
1127             }
1128             else {
1129             # No, token isn't created;
1130             # Process token
1131 12         201 $self->_processToken(HTML::TocGenerator::TT_TOKENTYPE_TEXT, $aText);
1132             }
1133             } # text()
1134              
1135              
1136             1;