--- lib/TWiki.pm.org 2008-08-12 09:21:47.000000000 +0200 +++ lib/TWiki.pm 2008-08-12 09:26:52.000000000 +0200 @@ -2000,7 +1996,32 @@ } + # NB: While we're processing $text line by line here, $this->renderer->getRendereredVersion() + # 'allocates' unique anchor names by first replacing regex{headerPatternHt} followed by + # regex{headerPatternDa}. In order to stay in sync and not 'clutter'/slow down the + # renderer code, we have to mimick the procedure here + my @regexps = ($regex{headerPatternHt}, $regex{headerPatternDa}); + my @lines = split( /\r?\n/, $text ); + my %anchors = (); + foreach my $regexp (@regexps) { + my $lineno = 0; + foreach my $line (@lines) { + $lineno++; + if ($line =~ m/$regexp/) { + $line =~ s/\s*$regex{headerPatternNoTOC}.+$//go; + next unless $line; + my ($anchor, $compatAnchor) = $this->renderer->makeUniqueAnchorNames( $line ); + $anchors{$lineno} = $anchor; + } + } + } + + # The following is still not optimised--there's no need for a third iteration over array + # @lines if we store all needed information (e.g., headings) along with anchors above... + # SMELL: this handling of
is archaic. # SMELL: use forEachLine - foreach my $line ( split( /\r?\n/, $text ) ) { + my $lineno = 0; + foreach my $line (@lines) { + $lineno++; my $level; if ( $line =~ m/$regex{headerPatternDa}/o ) { @@ -2018,5 +2039,5 @@ $line =~ s/\s*$regex{headerPatternNoTOC}.+$//go; next unless $line; - my $anchor = $this->renderer->makeAnchorName( $line ); + my $anchor = $anchors{$lineno}; $highest = $level if( $level < $highest ); my $tabs = "\t" x $level; --- lib/TWiki/Render.pm.org 2008-08-11 12:49:41.000000000 +0200 +++ lib/TWiki/Render.pm 2008-08-12 09:35:50.000000000 +0200 @@ -21,4 +21,7 @@ $placeholderMarker = 0; +# Used to generate unique anchors +my %headings = (); + # defaults for trunctation of summary text my $TMLTRUNC = 162; @@ -355,6 +358,6 @@ # - Initial '' is needed to prevent subsequent matches. # - filter out $TWiki::regex{headerPatternNoTOC} ( '!!' and '%NOTOC%' ) - my $anchorName = $this->makeAnchorName( $text, 0 ); - my $compatAnchorName = $this->makeAnchorName( $text, 1 ); + my ($anchorName, $compatAnchorName) = $this->makeUniqueAnchorNames( $text ); + # filter '!!', '%NOTOC%' $text =~ s/$TWiki::regex{headerPatternNoTOC}//o; @@ -428,4 +431,42 @@ } +=pod + +---++ ObjectMethod makeUniqueAnchorNames($anchorName) -> ($anchorName, $compatAnchorName) + + * =$anchorName= - the unprocessed anchor name + * =$compatAnchorName= - SMELL: compatibility with *what*?? Who knows. :-( + +Build a valid pair of HTML anchor names (deemed unique w.r.t. the list stored in %headings) + +=cut + +sub makeUniqueAnchorNames { + my( $this, $text ) = @_; + + # - idea: call makeAnchorName() 'pairwise' to make sure that the generated + # anchor names will be unique in both cases + my $anchorName = $this->makeAnchorName( $text, 0 ); + my $compatAnchorName = $this->makeAnchorName( $text, 1 ); + + # ensure that all generated anchor names are unique + my $cnt = 2; + my $suffix = ''; + while ((exists $headings{$anchorName . $suffix}) || + (exists $headings{$compatAnchorName . $suffix})) { + $suffix = '_' . $cnt++; + # limit resulting names to 32 chars + $anchorName = substr($anchorName, 0, 32 - length($suffix)); + $compatAnchorName = substr($compatAnchorName, 0, 32 - length($suffix)); + } + $anchorName .= $suffix; + $headings{$anchorName} = 1; + $compatAnchorName .= $suffix; + $headings{$compatAnchorName} = 1; + + return ($anchorName, $compatAnchorName); +} + + # Returns =title='...'= tooltip info in case LINKTOOLTIPINFO perferences variable is set. # Warning: Slower performance if enabled. @@ -884,4 +925,6 @@ @{$this->{LIST}} = (); + %headings = (); + # Initial cleanup $text =~ s/\r//g;