Core changes for NavigableTOC extension:
* Always generate the section tree, even when we're not generating a TOC * Add Parser::mergeSectionTrees() to merge two section trees into one * Add Linker::generateTOC() to generate the HTML for a TOC from a section tree, and add the section anchor to the section tree to facilitate this. This adds the ability to generate TOCs in extensions; haven't converted Parser.php to use it (yet?). As a side effect, this fixes API bug 18720
This commit is contained in:
parent
e664c88c12
commit
82022f21bc
3 changed files with 188 additions and 71 deletions
|
|
@ -237,6 +237,7 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
|
|||
* (bug 19313) action=rollback returns wrong revid on master/slave setups
|
||||
* (bug 19323) action=parse doesn't return section tree on pages with Cite
|
||||
warnings
|
||||
* (bug 18720) Add anchor field to action=parse&prop=sections output
|
||||
|
||||
=== Languages updated in 1.16 ===
|
||||
|
||||
|
|
|
|||
|
|
@ -1176,6 +1176,32 @@ class Linker {
|
|||
. ' } '
|
||||
. "</script>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a table of contents from a section tree
|
||||
* @param $tree Return value of ParserOutput::getSections()
|
||||
* @return string HTML
|
||||
*/
|
||||
public function generateTOC( $tree ) {
|
||||
$toc = '';
|
||||
$lastLevel = 0;
|
||||
foreach ( $tree as $section ) {
|
||||
if ( $section['toclevel'] > $lastLevel )
|
||||
$toc .= $this->tocIndent();
|
||||
else if ( $secton['toclevel'] < $lastLevel )
|
||||
$toc .= $this->tocUnindent(
|
||||
$lastLevel - $section['toclevel'] );
|
||||
else
|
||||
$toc .= $this->tocLineEnd();
|
||||
|
||||
$toc .= $this->tocLine( $section['anchor'],
|
||||
$section['line'], $section['number'],
|
||||
$section['toclevel'], $section['index'] );
|
||||
$lastLevel = $section['toclevel'];
|
||||
}
|
||||
$toc .= $this->tocLineEnd();
|
||||
return $this->tocList( $toc );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a section edit link. This supersedes editSectionLink() and
|
||||
|
|
|
|||
|
|
@ -3489,64 +3489,61 @@ class Parser
|
|||
}
|
||||
$level = $matches[1][$headlineCount];
|
||||
|
||||
if( $doNumberHeadings || $enoughToc ) {
|
||||
if ( $level > $prevlevel ) {
|
||||
# Increase TOC level
|
||||
$toclevel++;
|
||||
$sublevelCount[$toclevel] = 0;
|
||||
if( $toclevel<$wgMaxTocLevel ) {
|
||||
$prevtoclevel = $toclevel;
|
||||
$toc .= $sk->tocIndent();
|
||||
$numVisible++;
|
||||
}
|
||||
}
|
||||
elseif ( $level < $prevlevel && $toclevel > 1 ) {
|
||||
# Decrease TOC level, find level to jump to
|
||||
|
||||
if ( $level > $prevlevel ) {
|
||||
# Increase TOC level
|
||||
$toclevel++;
|
||||
$sublevelCount[$toclevel] = 0;
|
||||
if( $toclevel<$wgMaxTocLevel ) {
|
||||
for ($i = $toclevel; $i > 0; $i--) {
|
||||
if ( $levelCount[$i] == $level ) {
|
||||
# Found last matching level
|
||||
$toclevel = $i;
|
||||
break;
|
||||
}
|
||||
elseif ( $levelCount[$i] < $level ) {
|
||||
# Found first matching level below current level
|
||||
$toclevel = $i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( $i == 0 ) $toclevel = 1;
|
||||
if( $toclevel<$wgMaxTocLevel ) {
|
||||
if($prevtoclevel < $wgMaxTocLevel) {
|
||||
# Unindent only if the previous toc level was shown :p
|
||||
$toc .= $sk->tocUnindent( $prevtoclevel - $toclevel );
|
||||
$prevtoclevel = $toclevel;
|
||||
$toc .= $sk->tocIndent();
|
||||
$numVisible++;
|
||||
}
|
||||
}
|
||||
elseif ( $level < $prevlevel && $toclevel > 1 ) {
|
||||
# Decrease TOC level, find level to jump to
|
||||
|
||||
for ($i = $toclevel; $i > 0; $i--) {
|
||||
if ( $levelCount[$i] == $level ) {
|
||||
# Found last matching level
|
||||
$toclevel = $i;
|
||||
break;
|
||||
}
|
||||
elseif ( $levelCount[$i] < $level ) {
|
||||
# Found first matching level below current level
|
||||
$toclevel = $i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( $i == 0 ) $toclevel = 1;
|
||||
if( $toclevel<$wgMaxTocLevel ) {
|
||||
if($prevtoclevel < $wgMaxTocLevel) {
|
||||
# Unindent only if the previous toc level was shown :p
|
||||
$toc .= $sk->tocUnindent( $prevtoclevel - $toclevel );
|
||||
$prevtoclevel = $toclevel;
|
||||
} else {
|
||||
$toc .= $sk->tocLineEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
# No change in level, end TOC line
|
||||
if( $toclevel<$wgMaxTocLevel ) {
|
||||
} else {
|
||||
$toc .= $sk->tocLineEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
# No change in level, end TOC line
|
||||
if( $toclevel<$wgMaxTocLevel ) {
|
||||
$toc .= $sk->tocLineEnd();
|
||||
}
|
||||
}
|
||||
|
||||
$levelCount[$toclevel] = $level;
|
||||
$levelCount[$toclevel] = $level;
|
||||
|
||||
# count number of headlines for each level
|
||||
@$sublevelCount[$toclevel]++;
|
||||
$dot = 0;
|
||||
for( $i = 1; $i <= $toclevel; $i++ ) {
|
||||
if( !empty( $sublevelCount[$i] ) ) {
|
||||
if( $dot ) {
|
||||
$numbering .= '.';
|
||||
}
|
||||
$numbering .= $wgContLang->formatNum( $sublevelCount[$i] );
|
||||
$dot = 1;
|
||||
# count number of headlines for each level
|
||||
@$sublevelCount[$toclevel]++;
|
||||
$dot = 0;
|
||||
for( $i = 1; $i <= $toclevel; $i++ ) {
|
||||
if( !empty( $sublevelCount[$i] ) ) {
|
||||
if( $dot ) {
|
||||
$numbering .= '.';
|
||||
}
|
||||
$numbering .= $wgContLang->formatNum( $sublevelCount[$i] );
|
||||
$dot = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3648,28 +3645,31 @@ class Parser
|
|||
if( $enoughToc && ( !isset($wgMaxTocLevel) || $toclevel<$wgMaxTocLevel ) ) {
|
||||
$toc .= $sk->tocLine($anchor, $tocline,
|
||||
$numbering, $toclevel, ($isTemplate ? false : $sectionIndex));
|
||||
|
||||
# Find the DOM node for this header
|
||||
while ( $node && !$isTemplate ) {
|
||||
if ( $node->getName() === 'h' ) {
|
||||
$bits = $node->splitHeading();
|
||||
if ( $bits['i'] == $sectionIndex )
|
||||
break;
|
||||
}
|
||||
$byteOffset += mb_strlen( $this->mStripState->unstripBoth(
|
||||
$frame->expand( $node, PPFrame::RECOVER_ORIG ) ) );
|
||||
$node = $node->getNextSibling();
|
||||
}
|
||||
$tocraw[] = array(
|
||||
'toclevel' => $toclevel,
|
||||
'level' => $level,
|
||||
'line' => $tocline,
|
||||
'number' => $numbering,
|
||||
'index' => $sectionIndex,
|
||||
'fromtitle' => $titleText,
|
||||
'byteoffset' => ( $isTemplate ? null : $byteOffset ),
|
||||
);
|
||||
}
|
||||
|
||||
# Add the section to the section tree
|
||||
# Find the DOM node for this header
|
||||
while ( $node && !$isTemplate ) {
|
||||
if ( $node->getName() === 'h' ) {
|
||||
$bits = $node->splitHeading();
|
||||
if ( $bits['i'] == $sectionIndex )
|
||||
break;
|
||||
}
|
||||
$byteOffset += mb_strlen( $this->mStripState->unstripBoth(
|
||||
$frame->expand( $node, PPFrame::RECOVER_ORIG ) ) );
|
||||
$node = $node->getNextSibling();
|
||||
}
|
||||
$tocraw[] = array(
|
||||
'toclevel' => $toclevel,
|
||||
'level' => $level,
|
||||
'line' => $tocline,
|
||||
'number' => $numbering,
|
||||
'index' => ($isTemplate ? 'T-' : '' ) . $sectionIndex,
|
||||
'fromtitle' => $titleText,
|
||||
'byteoffset' => ( $isTemplate ? null : $byteOffset ),
|
||||
'anchor' => $anchor,
|
||||
);
|
||||
|
||||
# give headline the correct <h#> tag
|
||||
if( $showEditLink && $sectionIndex !== false ) {
|
||||
if( $isTemplate ) {
|
||||
|
|
@ -3740,6 +3740,96 @@ class Parser
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge $tree2 into $tree1 by replacing the section with index
|
||||
* $section in $tree1 and its descendants with the sections in $tree2.
|
||||
* Note that in the returned section tree, only the 'index' and
|
||||
* 'byteoffset' fields are guaranteed to be correct.
|
||||
* @param $tree1 array Section tree from ParserOutput::getSectons()
|
||||
* @param $tree2 array Section tree
|
||||
* @param $section int Section index
|
||||
* @param $title Title Title both section trees come from
|
||||
* @param $len2 int Length of the original wikitext for $tree2
|
||||
* @return array Merged section tree
|
||||
*/
|
||||
public static function mergeSectionTrees( $tree1, $tree2, $section, $title, $len2 ) {
|
||||
global $wgContLang;
|
||||
$newTree = array();
|
||||
$targetLevel = false;
|
||||
$merged = false;
|
||||
$lastLevel = 1;
|
||||
$nextIndex = 1;
|
||||
$numbering = array( 0 );
|
||||
$titletext = $title->getPrefixedDBkey();
|
||||
foreach ( $tree1 as $s ) {
|
||||
if ( $targetLevel !== false ) {
|
||||
if ( $s['level'] <= $targetLevel )
|
||||
// We've skipped enough
|
||||
$targetLevel = false;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
if ( $s['index'] != $section ||
|
||||
$s['fromtitle'] != $titletext ) {
|
||||
self::incrementNumbering( $numbering,
|
||||
$s['toclevel'], $lastLevel );
|
||||
|
||||
// Rewrite index, byteoffset and number
|
||||
if ( $s['fromtitle'] == $titletext ) {
|
||||
$s['index'] = $nextIndex++;
|
||||
if ( $merged )
|
||||
$s['byteoffset'] += $len2;
|
||||
}
|
||||
$s['number'] = implode( '.', array_map(
|
||||
array( $wgContLang, 'formatnum' ),
|
||||
$numbering ) );
|
||||
$lastLevel = $s['toclevel'];
|
||||
$newTree[] = $s;
|
||||
} else {
|
||||
// We're at $section
|
||||
// Insert sections from $tree2 here
|
||||
foreach ( $tree2 as $s2 ) {
|
||||
// Rewrite the fields in $s2
|
||||
// before inserting it
|
||||
$s2['toclevel'] += $s['toclevel'] - 1;
|
||||
$s2['level'] += $s['level'] - 1;
|
||||
$s2['index'] = $nextIndex++;
|
||||
$s2['byteoffset'] += $s['byteoffset'];
|
||||
|
||||
self::incrementNumbering( $numbering,
|
||||
$s2['toclevel'], $lastLevel );
|
||||
$s2['number'] = implode( '.', array_map(
|
||||
array( $wgContLang, 'formatnum' ),
|
||||
$numbering ) );
|
||||
$lastLevel = $s2['toclevel'];
|
||||
$newTree[] = $s2;
|
||||
}
|
||||
// Skip all descendants of $section in $tree1
|
||||
$targetLevel = $s['level'];
|
||||
$merged = true;
|
||||
}
|
||||
}
|
||||
return $newTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment a section number. Helper function for mergeSectionTrees()
|
||||
* @param $number array Array representing a section number
|
||||
* @param $level int Current TOC level (depth)
|
||||
* @param $lastLevel int Level of previous TOC entry
|
||||
*/
|
||||
private static function incrementNumbering( &$number, $level, $lastLevel ) {
|
||||
if ( $level > $lastLevel )
|
||||
$number[$level - 1] = 1;
|
||||
else if ( $level < $lastLevel ) {
|
||||
foreach ( $number as $key => $unused )
|
||||
if ( $key >= $level )
|
||||
unset( $number[$key] );
|
||||
$number[$level - 1]++;
|
||||
} else
|
||||
$number[$level - 1]++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform wiki markup when saving a page by doing \r\n -> \n
|
||||
* conversion, substitting signatures, {{subst:}} templates, etc.
|
||||
|
|
|
|||
Loading…
Reference in a new issue