Open main menu

Extension:NavContent

MediaWiki extensions manual
OOjs UI icon advanced.svg
NavContent
Release status: beta
Implementation Parser function , Variable , Ajax
Description Navigation tool to toggle showing of content
Author(s) Tommy Ekola, Didrik Gauffin Wohlfarth
Latest version 1.0.3
MediaWiki tested with 1.21.1
License GPL v3
Download Here
Example [1]
Translate the NavContent extension if it is available at translatewiki.net
Check usage and version matrix.

For version tested with 1.11.0 see NavContent 0.9.5

NavContent is an extension that implements a type of navigational frame, i.e. a mechanism for users to expand (and collapse) certain page content by clicking on a link. This extension goes a bit beyond the simplest form of navigational frame and the main features of this implementation are

  • show and hide content,
  • dynamic loading of content,
  • toggle between content, and
  • step through content.


Contents

ExampleEdit

Show and hide contentEdit

Insert the following line of code in your wiki text

{{#NAVCONTENT: Answer|Answer to exercise 1.1}}

This will create a simple navigational bar.

 

If you click on the "Answer" link the content associated with this link will be revealed. In this case it will just be a link to a non-existent page "Answer to exercise 1.1".

 

One more click on the "Answer" link will hide the content again.

Dynamic loading of contentEdit

Continuing with the example above, you should now create a new page "Answer to exercise 1.1" in the wiki with the following content:

The answer is 42.

If you now click on the "Answer" link, on the page containing the navigation bar, you will this time first see a flash of a loading message

 

and then the content of your newly created page is inserted instead of the red-link you saw before.

 

Toggle between contentEdit

You should now expand the NavContent code to:

{{#NAVCONTENT: Answer|Answer to exercise 1.1|Solution|Solution to exercise 1.1}}

This time the navigation bar contains two links "Answer" and "Solution".

 

These links will individually show/hide the content associated with them. If, for example, you click on "Solution" a link to the non-existent page "Solution to exercise 1.1" will be folded out.

 

Now, clicking on "Answer" will replace the current content with the content associated with the "Answer" link, i.e. the "Answer" and "Solution" links toggle each other.

 

(You can, of course, have any number of links in the navigational bar and they all toggle each other.)

Step through contentEdit

To complete this example you should now create a page "Solution to exercise 1.1" in the wiki with the following content:

{{NAVCONTENT_START}}
If you add 1 to 40 you get 41,
{{NAVCONTENT_STEP}}
and if you add 1 to 41 you get the answer 42.
{{NAVCONTENT_STOP}}

On the page containing the #NAVCONTENT function the text from the page "Solution to exercise 1.1" is, as expected, injected as the content under the "Solution" link. However, if you click on "Solution" only the text up to the first {{NAVCONTENT_STEP}} is displayed and a second navigational bar is displayed at the bottom.

 

You can use this so-called progress bar to reveal or hide parts of the page with the links "Show more", "Show less", "Show all" and "Hide all". For example, clicking on "Show more" will show the second part of the solution.

 

(As you might have guessed: inserting more {{NAVCONTENT_STEP}} lines will subdivide the included material into more parts for the user to step through.)

Show full content on printable versionEdit

If you click on the "Printable version" link in the toolbox (located in the sidebar of the page), then the resulting printable page will contain all loaded material in an expanded view.

 


InstallationEdit

Save the codeEdit

Create the directory $IP/extensions/NavContent and save the code in the files NavContent.php, NavContent.i18n.magic.php, NavContent.i18n.php, NavContent.js, NavContent.css and NavContentPrint.css, respectively, in that directory. You should also download the image file Loading.gif and save it in the same directory. (Optionally, download a suitable image from www.ajaxload.info.) This is the image that is shown while content is being loaded.

Configure the extensionEdit

Add the following lines at the end of the file $IP/LocalSettings.php

include_once( "$IP/extensions/NavContent/NavContent.php" );

Further configuration:

  1. If you want to render math with MathJax on included content, then you need to add the following lines to the file $IP/LocalSettings.php before NavContent.php is included:
    $wgNavContentRenderMath = 'renderTex';
    
    (If you do this, make sure you have the Extension:Math installed and configured to render math via MathJax, see MathJax.)
  2. You can disable any button by setting its value to "False" in the $wgNavContentProgressBar array, before NavContent.php is included:
    $wgNavContentProgressBar = array(
    	'showall' => True,
    	'shownone' => True,
    	'showmore' => True,
    	'showless' => True,
    );
    

    (Default value is True).

  3. If you do not want all content to be expanded when the user is viewing a printable version of the page then you should write
    $wgNavContentDisablePrint = 1;
    

    before NavContent.php is included.

  4. You should consider changing the two style sheet files NavContent.css and NavContentPrint.css. At present they are merely useful for very simplistic needs.


The codeEdit

NavContent.phpEdit

<?php
/*   
*    NavContent - Navigation tool to toggle showing of content
*    Copyright © 2013 by Tommy Ekola (tek@kth.se), Didrik Gauffin Wohlfarth (didrikgw@kth.se)
*
*    This program is free software: you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/ 


if (! defined( 'MEDIAWIKI' ) ) {
        echo( "This is an extension to the MediaWiki package and cannot be run standalone.\n" );
        die( -1 );
}
 

// Extension information
 
$wgExtensionCredits['other'][] = array(
        'name'        => 'NavContent',
        'description' => 'Navigation bar that toggles showing of content',
        'version'     => '1.0.3',
        'author'      => array('Tommy Ekola', 'Didrik Gauffin Wohlfarth'),
        'url'         => 'http://www.mediawiki.org/wiki/Extension:NavContent');
 
 
// Configuration

// Configure modules for script and stylesheets
$wgResourceModules['ext.NavContent.script'] = array(
    'scripts' => '/NavContent.js',
    'localBasePath' => __DIR__,
    'dependencies' => 'jquery.client',
);
$wgResourceModules['ext.NavContent.style'] = array(
    'styles' => array(
                '/NavContent.css' => array( 'media' => 'screen' ),
                '/NavContentPrint.css' => array( 'media' => 'print' ),
                ),
    'localBasePath' => __DIR__,
    'position' => 'top',
);



// Set Hooks
$wgHooks['ParserFirstCallInit'][] = 'wfNavContent_Setup';
$wgHooks['MakeGlobalVariablesScript'][] = 'PassNavContentConfigVariables';


// Load messages and MagicWords
$wgExtensionMessagesFiles[ 'NavContent' ] = __DIR__ . '/NavContent.i18n.php';
$wgExtensionMessagesFiles[ 'NavContentMagic' ] = __DIR__ . '/NavContent.i18n.magic.php';


// Pass NavContent config vars to Javascript
function PassNavContentConfigVariables( &$vars, $out ) {
	global $wgNavContentRenderMath, $wgNavContentDisablePrint;
	
	$printable = $out->isPrintable();
	$printable = $printable && !$wgNavContentDisablePrint;
	
	if (($wgNavContentRenderMath)) {
            $vars['wgNavContentRenderMath'] = $wgNavContentRenderMath;
        }
	
	$vars['wgPrintable'] = $printable;
	
	return true;
}


// Define {{#NAVCONTENT: ...}}
function wfNavContent_Setup( &$parser ) {
        global $wgOut, $wgNavContentRenderMath;
	
	// Set Hook
	$parser->setFunctionHook( 'navcontent', 'wfNavContent_Render' );
	
	// Add modules
	$wgOut->addModuleScripts( 'ext.NavContent.script' );
        
        $wgOut->addModuleStyles( 'ext.NavContent.style' );
        
        if (isset($wgNavContentRenderMath)) {
            $wgOut->addModules( 'ext.math.mathjax.enabler' );
        }
        
        return true;

}


// Execution
function wfNavContent_Render( &$parser ) {
	global $wgNavContentProgressBar;
	
        $arguments = array_slice( func_get_args(), 1 );
        $n = count( $arguments );
 
        $output = array();
        $output['noparse'] = true;
        $output['isHTML'] = true;
 
        // Check arguments
        if ( fmod( $n, 2 ) != 0 ) {
                $output[0] = "<div class=\"mw-warning\">Uneven number of arguments to #NAVCONTENT</div>";
                return $output;
        }
        foreach( $arguments as $i => $pageref ) {
                if( fmod($i,2) == 1 ) {
                        if(! Title::newFromText( $pageref ) ) {
                                $output[0] = "<div class=\"mw-warning\">Argument \""
                                        . htmlspecialchars($pageref)
                                        . "\" in #NAVCONTENT is referring to a non-valid page</div>";
                                return $output;
                        }
                }
        }
 
        // Add navigation bar 
        $output[0] = "<div class=\"NavContentFrame\">\n"
                . "<div class=\"NavContentBar\" style=\"display: none;\">\n";
        for( $i=0; $i < $n; $i += 2) {
                $output[0] .= "<a class=\"NavContentBarLink\" style=\"cursor: pointer;\">"
                        . htmlspecialchars($arguments[$i])
                        . "</a>\n";
        }
        $output[0] .= "</div>\n";
 
        for( $i=0; $i<$n; $i+=2 ) {
                $output[0] .= "<div class=\"NavContentPart\">\n";
 
                // Add content
                $title = Title::newFromText( $arguments[$i+1] );
                $output[0] .= "<div class=\"NavContentHeader\">\n"
                        . htmlspecialchars($arguments[$i]) . "</div>\n"
                        . "<div class=\"NavContentContent load-on-demand\">\n"
                        . "<div class=\"NavContentContentPart\">\n"
                        . "<a ";
                if(! $title->exists() ) 
                        $output[0] .= "class=\"new\" "
                                . "href=\"" . htmlspecialchars( $title->getFullURL( 'action=edit' )) . "\">";
                                
                else
                        $output[0] .= "href=\"" . htmlspecialchars( $title->getFullURL()) . "\">";
                $output[0] .= htmlspecialchars($arguments[$i+1]) . "</a>"
                        . "<div class=\"NavContentLoading\" style=\"display:none;\">"
                        . "<img align=\"middle\" style=\"display: inline;\" "
                        . "src=\"/w/extensions/NavContent/Loading.gif\"/>"
                        . "&nbsp;&nbsp;" . wfMessage('navcontentloading')->text()
                        . "</div>\n"
                        . "</div>\n"
                        . "</div>\n";
 
                // Add progress bar
                $output[0] .= "<div class=\"NavContentProgressBar\" style=\"display:none;\">\n";
                if( !isset($wgNavContentProgressBar['showless']) || $wgNavContentProgressBar['showless'] ) {
                        $output[0] .= "<span class=\"NavContentShowLess\">"
                                . wfMessage('navcontentshowless')->text() . "</span>\n";
                        }
                if( !isset($wgNavContentProgressBar['showmore']) || $wgNavContentProgressBar['showmore'] ) {
                        $output[0] .= "<span class=\"NavContentShowMore\">"
                                . wfMessage('navcontentshowmore')->text() . "</span>\n";
                        }
                if( !isset($wgNavContentProgressBar['shownone']) || $wgNavContentProgressBar['shownone'] ) {
                        $output[0] .= "<span class=\"NavContentShowNone\">"
                                . wfMessage('navcontentshownone')->text() . "</span>\n";
                        }
                if( !isset($wgNavContentProgressBar['showall']) || $wgNavContentProgressBar['showall'] ) {
                        $output[0] .= "<span class=\"NavContentShowAll\">"
                                . wfMessage('navcontentshowall')->text() . "</span>\n";
                        }
                $output[0] .= "</div>\n"
                        . "</div>\n";
        }
        $output[0] .= "</div>";
 
        return $output;
}


// Insert links to referenced pages on the edit page
 
$wgHooks['EditPage::showEditForm:initial'][] = 'wfNavContentRefPages';
 
function wfNavContentRefPages( &$editpage ) {
  global $wgUser, $wgParser;
 
        if(! $editpage->mTitle->exists() ) return true;
 
        // Parse the edit page (and turn #navcontent into _#navcontent)
        $wgParser->setFunctionHook( 'navcontent', 'wfNavContent_FakeRender' );
        $text = $editpage->textbox1;
        $popts = new ParserOptions;
        $popts->setTidy(true);
        $parsedOutput = $wgParser->parse( $text, $editpage->mTitle, $popts );
        $text = $parsedOutput->getText();
        $wgParser->setFunctionHook( 'navcontent', 'wfNavContent_Render' );
 
        // Extract pages referenced by _#navcontent
        $t = explode( '{{', $text );
        $navcontentlinks = array();
        foreach( $t as $key => $x ) {
                $y = explode( '}}', $x );
                if ( count( $y ) >= 2 ) {
                        $z = $y[0];
                        $z = explode( ':', $z );
                        if ( in_array( strtolower($z[0]), array('_#navcontent') ) ) {
                                array_shift( $z );
                                $z = implode( ':', $z );
                                $z = explode( '|', $z );
                                foreach( $z as $i => $page ) {
                                        if( fmod($i,2)==1 ) {
                                          $navcontentlinks[] = Title::newFromText( trim($page) );
                                        }
                                }
                        } 
                }
        }
        if( count($navcontentlinks) == 0 ) return true;
 
        $sk = $wgUser->getSkin();
 
        // Do a batch existence check
        $batch = new LinkBatch;
        foreach( $navcontentlinks as $titleObj ) {
                $batch->addObj( $titleObj );
        }
        $batch->execute();
 
        // Add links to referenced pages
        $outText = "\n<div class=\"templatesUsed\">\n<div class=\"mw-templatesUsedExplanation\"><p>";
        if( $editpage->preview ) {
                $outText .= wfMessage( 'navcontentpagesusedpreview' )->text();
        } elseif( $editpage->section != '' ) {
                $outText .= wfMessage( 'navcontentpagesusedsection' )->text();
        } else {
                $outText .= wfMessage( 'navcontentpagesused' )->text();
        }
        $outText .= "</p></div>\n<ul>\n";
 
        foreach( $navcontentlinks as $titleObj ) {
                if( $titleObj ) {
                        $r = $titleObj->getRestrictions( 'edit' );
                        if( in_array( 'sysop', $r ) ) {
                                $protected = wfMessage( 'navcontent-protected' )->text();
                        } elseif ( in_array( 'autoconfirmed', $r ) ) {
                                $protected = wfMessage( 'navcontent-semiprotected' )->text();
                        } else {
                                $protected = '';
                        }
                        $outText .= "<li>" . $sk->makeLinkObj( $titleObj ) . " " . $protected . "</li>\n";
                }
        }
        
        $outText .= "</ul>\n</div>";
 
        $editpage->editFormTextAfterTools .= $outText;
        return true;
}
 
function wfNavContent_FakeRender( &$parser ) {
 
        $arguments = array_slice( func_get_args(), 1 );
        $n = count( $arguments );
 
        $output = array();
        $output['noparse'] = true;
        $output['isHTML'] = true;
 
        $output[0] = "{{_#navcontent:";
        foreach( $arguments as $arg ) {
                $output[0] .= $arg . "|";
        }
        $output[0] = substr($output[0],0,-1);
        $output[0] .= "}}";
 
        return $output;
}


// Define {{NAVCONTENT_START}}, {{NAVCONTENT_STEP}} and {{NAVCONTENT_STOP}}

$wgHooks['ParserGetVariableValueSwitch'][] = 'wfNavContentVarAssign';
 
function wfNavContentVarAssign(&$parser, &$cache, &$magicWordId, &$ret) {
        switch( $magicWordId ) {
        case 'NAVCONTENT_START':
                $ret='<div class="NavContentContentPart">';
                return true;
        case 'NAVCONTENT_STEP':
                $ret='</div><div class="NavContentContentPart">';
                return true;
        case 'NAVCONTENT_STOP':
                $ret='</div>';
                return true;
        }
        return true;
}
 
$wgHooks['MagicWordwgVariableIDs'][] = 'wfNavContentDeclareVarIds';
 
function wfNavContentDeclareVarIds(&$aCustomVariableIds) {
        $aCustomVariableIds[] = 'NAVCONTENT_START';
        $aCustomVariableIds[] = 'NAVCONTENT_STEP';
        $aCustomVariableIds[] = 'NAVCONTENT_STOP';
        return true;
}

NavContent.i18n.magic.phpEdit

<?php

/*
*    Copyright © 2013 by Tommy Ekola (tek@kth.se), Didrik Gauffin Wohlfarth (didrikgw@kth.se)
*    Full notice in NavContent.php
*/

$magicWords['en'] = array(
	'navcontent' => array( 0, 'navcontent' ),
	'NAVCONTENT_START' => array( 0, 'navcontent_start' ),
	'NAVCONTENT_STEP' => array( 0, 'navcontent_step' ),
	'NAVCONTENT_STOP' => array( 0, 'navcontent_stop' ),
);

NavContent.i18n.phpEdit

<?php
 
/*
*    Copyright © 2013 by Tommy Ekola (tek@kth.se), Didrik Gauffin Wohlfarth (didrikgw@kth.se)
*    Full notice in NavContent.php
*/
 
$messages = array();
$messages[ 'de' ] = array(
	'navcontentloading' => 'Laden...',
	'navcontentshowless' => 'Zeige weniger',
	'navcontentshowmore' => 'Zeige mehr',
	'navcontentshownone' => 'Alles ausblenden',
	'navcontentshowall' => 'Alles anzeigen',
	'navcontentpagesusedpreview' => 'Seiten, auf die sich #NAVCONTENT in dieser Vorschau bezieht:',
	'navcontentpagesusedsection' => 'Seiten, auf die sich #NAVCONTENT in diesem Abschnitt bezieht:',
	'navcontentpagesused' => 'Seiten, auf die sich #NAVCONTENT in dieser Seite bezieht:',
	'navcontent-protected' => '(schreibgeschützt)',
	'navcontent-semiprotected' => '(schreibgeschützt für nicht angemeldete und neue Benutzer)',
);
$messages[ 'en' ] = array(
	'navcontentloading' => 'Loading...',
	'navcontentshowless' => 'Show less',
	'navcontentshowmore' => 'Show more',
	'navcontentshownone' => 'Hide all',
	'navcontentshowall' => 'Show all',
	'navcontentpagesusedpreview' => 'Pages that #NAVCONTENT is referring to in this preview:',
	'navcontentpagesusedsection' => 'Pages that #NAVCONTENT is referring to in this section:',
	'navcontentpagesused' => 'Pages that #NAVCONTENT is referring to on this page:',
	'navcontent-protected' => '(protected)',
	'navcontent-semiprotected' => '(semiprotected)',
);
$ messages ['es'] = array (
	'navcontentloading' => 'Cargando...', 
	'navcontentshowless' => 'Mostrar menos', 
	'navcontentshowmore' => 'Mostrar más', 
	'navcontentshownone' => 'Ocultar todo', 
	'navcontentshowall' => 'Mostrar todo', 
	'navcontentpagesusedpreview' => 'Páginas a las que se refiere #NAVCONTENT en esta vista previa:',
	'navcontentpagesusedsection' => 'Páginas a las que se refiere #NAVCONTENT en esta sección:',
	'navcontentpagesused' => 'Páginas a las que se refiere #NAVCONTENT en esta página:',
	'navcontent-protected' => '(protegida - de sólo lectura)',
	'navcontent-semiprotected' => '(semiprotegida - sólo lectura para usuarios no registrados y nuevos)',
);
$messages[ 'sv' ] = array(
	'navcontentloading' => 'Hämtar...',
	'navcontentshowless' => 'Visa mindre',
	'navcontentshowmore' => 'Visa mer',
	'navcontentshownone' => 'Dölj allt',
	'navcontentshowall' => 'Visa allt',
	'navcontentpagesusedpreview' => 'Sidor som #NAVCONTENT hänvisar till i förhandsgranskningen:',
	'navcontentpagesusedsection' => 'Sidor som #NAVCONTENT hänvisar till i detta avsnitt:',
	'navcontentpagesused' => 'Sidor som #NAVCONTENT hänvisar till på denna sida:',
	'navcontent-protected' => '(skyddad)',
	'navcontent-semiprotected' => '(delvis skyddad)',
);
$messages[ 'fr' ] = array(
	'navcontentloading' => 'Chargement...',
	'navcontentshowless' => 'Afficher moins',
	'navcontentshowmore' => 'Afficher plus',
	'navcontentshownone' => 'Tout cacher',
	'navcontentshowall' => 'Tout montrer',
	'navcontentpagesusedpreview' => 'Pages auquel #NAVCONTENT fait référence dans cet aperçu:',
	'navcontentpagesusedsection' => 'Pages auquel #NAVCONTENT fait référence dans cette section:',
	'navcontentpagesused' => 'Pages auquel #NAVCONTENT fait référence sur cette page:',
	'navcontent-protected' => '(protégé)',
	'navcontent-semiprotected' => '(semi-protégé)',
);

NavContent.jsEdit

/*
*    Copyright © 2013 by Tommy Ekola (tek@kth.se), Didrik Gauffin Wohlfarth (didrikgw@kth.se)
*    Full notice in NavContent.php
*/

// Execution
if (wgPrintable) {
    $( document ).ready( NavContentLoadContent() );
}
else {
    $( document ).ready( NavContentCreate() );
}

// Hook setup, execute external function
function runOnloadSubpagesHook(el) {
    if(!(document.getElementById && document.getElementsByTagName)) {
        return;
    }
    if (!wgPrintable) {
        $(el)[wgNavContentRenderMath]();
    }
}
 
 
// Navigation bar
 
function NavContentCreate(el) {
 
    el = el || document;
 
    // Hide all parts
    $("div.NavContentFrame div.NavContentPart",el).hide();
 
    // Hide the header inside each part
    $("div.NavContentFrame div.NavContentPart div.NavContentHeader",el).hide();
 
    // Add some space and a vertical bar between the bar links
    $("div.NavContentFrame div.NavContentBar",el)
        .each(function(){
                $("a.NavContentBarLink",this).not(":last").after("&nbsp;| ");
            })
        .show();
 
    // Add navigation links to each progress bar
    $("div.NavContentFrame div.NavContentPart div.NavContentProgressBar",el)
        .each(function(){
                $("span",this)
                    .each(function(){
                            $(this).after("<a></a>");
                        });
            });
 
    $("div.NavContentFrame div.NavContentPart div.NavContentProgressBar a",el)
        .each(function(){
                $(this)
                    .addClass($(this).prev("span").attr("class"))
                    .css({cursor: "pointer"})
                    .text($(this).prev("span").text());
            })
 
    // Hide all non-clickable links
    $("div.NavContentFrame div.NavContentPart div.NavContentProgressBar span",el)
        .hide();
 
    // Add some space and a vertical bar between the progress bar links
    $("div.NavContentFrame div.NavContentPart div.NavContentProgressBar",el)
        .each(function(){
                $("a",this).not(":last").after("&nbsp;| ");
            });
 
    // Replace link with message on load-on-demand div's
    $("div.NavContentFrame div.NavContentPart div.NavContentContent.load-on-demand",el)
        .find("div.NavContentContentPart a:not(.new)")
        .hide()
        .siblings().show();
 
    // Add actions to each link in bar
    $("div.NavContentFrame div.NavContentBar",el)
        .each(function(i){
                $("a.NavContentBarLink",this)
                    .each(function(j){
                            $(this).click(
                                function(){
                                    // Make all other links normal face
                                    $(this).parent()
                                        .find("a.NavContentBarLink")
                                        .not(":eq(" + j + ")")
                                        .css("fontWeight","normal");
                                    // Toggle this link (normal/bold)
                                    if($(this).css("fontWeight")=="bold" ||
                                       $(this).css("fontWeight")==700)
                                        $(this).css("fontWeight","normal");
                                    else
                                        $(this).css("fontWeight","bold");
                                    // Make all other parts hidden
                                    $(this).parents("div.NavContentFrame")
                                        .find("div.NavContentPart")
                                        .not(":eq(" + j + ")")
                                        .hide();
                                    // Load contents if needed
                                    var contents = $(this).parents("div.NavContentFrame")
                                        .find("div.NavContentPart").eq(j)
                                        .find("div.NavContentContent");
                                    if (contents.hasClass("load-on-demand")) {
                                        if(!contents.find("a").hasClass("new")) {
                                            addr = $("a",contents).attr("href");
                                            host = addr.split(/\/index.php/g)[0];
                                            addr = addr.split(/\/index.php/g)[1];
                                            if (/[\?\&]title\=/.test(addr)) {
                                                addr = addr.match(/[\?\&]title\=[^\?\&]+/);
                                                addr = addr[0].match(/([\?\&]title\=)([^\?\&]+)/);
                                                addr = addr[2];
                                            }
                                            else {
                                                addr = addr.slice(1);
                                            }
                                            $(contents).load(host + "/index.php?title="
                                 + addr + " #mw-content-text",
                                                {},
                                                function(){                                     
                                                    $(this).removeClass("load-on-demand");
                                                    $("div.NavContentContentPart:first",this).slideDown("normal");
                                                    $("div.NavContentContentPart:gt(0)",this).hide();
                                                    if($("div.NavContentContentPart",this).size()>1) {
                                                        $(this).parent().find("div.NavContentProgressBar").show();
                                                    }
                                                    if (typeof wgNavContentRenderMath != 'undefined' && wgNavContentRenderMath) {
                                                        runOnloadSubpagesHook(this);
                                                    }
                                                });
                                        }
                                    }
                                    // Toggle the visibility of this part
                                    if($(this).css("fontWeight")=="bold" ||
                                       $(this).css("fontWeight")==700) {
                                        $(this).parents("div.NavContentFrame")
                                            .find("div.NavContentPart").eq(j)
                                            .slideDown("normal");
                                    }
                                    else {
                                        $(this).parents("div.NavContentFrame")
                                            .find("div.NavContentPart").eq(j)
                                            .slideUp("normal");
                                    }        
                                });
                        });
            });
 
    // Hide all but the first content in each part
    $("div.NavContentFrame div.NavContentPart div.NavContentContent",el)
        .each(function(){
                $("div.NavContentContentPart:first",this).show();
            });
    $("div.NavContentFrame div.NavContentPart div.NavContentContent",el)
        .each(function(){
                $("div.NavContentContentPart:gt(0)",this).hide();
            });
 
    // If there is zero or one content in a part then the progress bar is hidden
    $("div.NavContentFrame div.NavContentPart",el)
        .each(function(){
                if($("div.NavContentContentPart",this).size()<2) {
                    $("div.NavContentProgressBar",this).hide();
                }
                else {
                    $("div.NavContentProgressBar",this).show();
                }
            });
 
    // Actions to perform when a progress bar link is clicked
    $("div.NavContentFrame",el)
        .each(function(i){
                $("div.NavContentPart div.NavContentProgressBar",this)
                    .each(function(j){
                            $("a.NavContentShowLess",this).click(
                                function(){
                                    // Make this link invisible if needed
                                    if($(this).parents("div.NavContentPart")
                                          .find("div.NavContentContentPart:visible")
                                          .size() < 2) {
                                        $(this).hide()
                                            .parent()
                                            .find("span.NavContentShowLess")
                                            .show()
                                            .end()
                                            .find("a.NavContentShowNone")
                                            .hide()
                                            .end()
                                            .find("span.NavContentShowNone")
                                            .show();
                                    }
                                    // Hide the last visible content
                                    $(this).parents("div.NavContentPart")
                                        .find("div.NavContentContentPart:visible:last")
                                        .slideUp("normal");
                                    // Make other links visible
                                    $(this).parent()
                                        .find("span.NavContentShowMore").hide()
                                        .end()
                                        .find("a.NavContentShowMore").show()
                                        .end()
                                        .find("span.NavContentShowAll").hide()
                                        .end()
                                        .find("a.NavContentShowAll").show();
                                });
                            $("a.NavContentShowMore",this).click(
                                function(){
                                    // Make other links invisible if needed
                                    if($(this).parents("div.NavContentPart")
                                          .find("div.NavContentContentPart:hidden")
                                          .size() < 2) {
                                        $(this).hide()
                                            .parent()
                                            .find("span.NavContentShowMore").show()
                                            .end()
                                            .find("a.NavContentShowAll").hide()
                                            .end()
                                            .find("span.NavContentShowAll").show();
                                    }
                                    // Show the first invisible content
                                    $(this).parents("div.NavContentPart")
                                        .find("div.NavContentContent").show()
                                        .end()
                                        .find("div.NavContentContentPart:hidden:first").slideDown("normal");
                                    // Make less/none links visible
                                    $(this).parent()
                                        .find("span.NavContentShowLess").hide()
                                        .end()
                                        .find("a.NavContentShowLess").show()
                                        .end()
                                        .find("span.NavContentShowNone").hide()
                                        .end()
                                        .find("a.NavContentShowNone").show();
                                });
                            $("a.NavContentShowAll",this).click(
                                function(){
                                    // Show all invisible content
                                    $(this).parents("div.NavContentPart")
                                        .find("div.NavContentContent").show()
                                        .find("div.NavContentContentPart:hidden:first")
                                        .slideDown("normal", function(){
                                                $(this).parents("div.NavContentContent")
                                                    .find("div.NavContentContentPart:hidden")
                                                    .show();
                                            });
                                    // Make less/none links visible
                                    $(this).parent()
                                        .find("span.NavContentShowLess").hide()
                                        .end()
                                        .find("a.NavContentShowLess").show()
                                        .end()
                                        .find("span.NavContentShowNone").hide()
                                        .end()
                                        .find("a.NavContentShowNone").show();
                                    // Make more/all links invisible
                                    $(this).hide().parent()
                                        .find("span.NavContentShowAll").show()
                                        .end()
                                        .find("a.NavContentShowMore").hide()
                                        .end()
                                        .find("span.NavContentShowMore").show();
                                });
                            $("a.NavContentShowNone",this).click(
                                function(){
                                    // Hide all visible content
                                    $(this).parents("div.NavContentPart")
                                        .find("div.NavContentContent").show()
                                        .find("div.NavContentContentPart:visible:last")
                                        .slideUp("normal", function(){
                                                $(this).parents("div.NavContentContent")
                                                    .find("div.NavContentContentPart:visible")
                                                    .hide();
                                            });                             
                                    // Make more/all links visible
                                    $(this).parent()
                                        .find("span.NavContentShowMore").hide()
                                        .end()
                                        .find("a.NavContentShowMore").show()
                                        .end()
                                        .find("span.NavContentShowAll").hide()
                                        .end()
                                        .find("a.NavContentShowAll").show();
                                    // Make less/none links invisible
                                    $(this).hide().parent()
                                        .find("span.NavContentShowNone").show()
                                        .end()
                                        .find("a.NavContentShowLess").hide()
                                        .end()
                                        .find("span.NavContentShowLess").show();
                                });
                        });
            });
}

 
function NavContentLoadContent(el) {
 
    el = el || document;
 
    // Replace link with message on load-on-demand div's
    $("div.NavContentFrame div.NavContentPart div.NavContentContent.load-on-demand",el)
        .find("div.NavContentContentPart a:not(.new)")
        .hide()
        .siblings().show();
 
    // Load content
    $("div.load-on-demand",el).each(function(){
            if(!$("a",this).hasClass("new")) {
                addr = $("a",this).attr("href");
                host = addr.split(/\/index.php/g)[0];
                addr = addr.split(/\/index.php/g)[1];
                if (/[\?\&]title\=/.test(addr)) {
                    addr = addr.match(/[\?\&]title\=[^\?\&]+/);
                    addr = addr[0].match(/([\?\&]title\=)([^\?\&]+)/);
                    addr = addr[2];
                }
                else {
                    addr = addr.slice(1);
                }
                $(this).load(host + "/index.php?title="
                                 + addr + " #mw-content-text",
                    {},
                    function(){
                        $(this).removeClass("load-on-demand");
                        
                        if (wgNavContentRenderMath) {
                            runOnloadSubpagesHook(this);
                        }                        
                    });
            }
    });
}

NavContent.cssEdit

div.NavContentFrame {
        margin:1em 0em 1em 0em;
        padding:0.5em 1.5em .2em 1.5em;
        border:1px solid #CFCFCF;
        width:580px;
  }
 
div.NavContentBar {
        font-size: 70%;
}
 
a.NavContentBarLink, a.NavContentShowLess, a.NavContentShowMore, a.NavContentShowNone, a.NavContentShowAll {
        Cursor: pointer;
}
 
div.NavContentHeader + div.NavContentContent {
        margin-top: 2em;
}
 
div.NavContentProgressBar {
        margin-top: 2em;
        font-size: 70%;
}
 
div.NavContentLoading {
        width: 100%;
        margin: 1em 0em 1em 0em;
        text-align: center;
}

NavContentPrint.cssEdit

div.NavContentFrame {
        margin: 0;
        padding: 0;
        border: 0;
        width: auto;
}

div.NavContentPart {
	margin:1.5em 0em 1em 0em;
	padding:0.5em 1.5em .5em 1.5em;
	border:1px solid black;
	width:580px;
}

div.NavContentHeader {
        position: relative;
        float: left;
        margin-top: -1em;
        margin-bottom: 1em;
        padding-left: 1em;
        padding-right: 1em;
        background-color: white;
}

div.NavContentContent, div.NavContentContentPart {
        clear: left;
}

div.NavContentProgressBar {
        display: none;
}

Loading.gifEdit

Right-click and save this image:  

HistoryEdit

Version 0.9
Initial version.
Version 0.9.1
Pages referenced by #NAVCONTENT are now allowed to contain a slash. (Bug reported by Daniel E. Forester).
Version 0.9.2
German translation of messages. (Thanks to Ruedi Seiler).
Version 0.9.3
French translation of messages.
Version 0.9.4
Fix Opera bug.
Version 0.9.5
Small bug fix.
Version 1.0.0
Migrated for use with MediaWiki 1.21 and ResourceLoader.
Version 1.0.1
Some further migration and added external rendering of math on included content.
Version 1.0.2
Added disabling buttons feature.
Version 1.0.3
Spanish translation of messages. Fix German translation of messages.