It would be great if the Table of Content could used on a block, not (only) into content.

Comments

deviantintegral’s picture

Status: Active » Postponed (maintainer needs more info)

This would require significant work as right now everything is run through an input filter. I think the best way to do this would be to write a hook_block function which would lookup the current node and then call the hook_filter functions itself.

Submit a patch and I'll be glad to commit it.

--Andrew

silurius’s picture

Subscribing, even though I'm on drupal 6.3. Combined with something like CCK Blocks, this would be a great option to have.

it-was-a-shihtzu’s picture

I've done this by using the list-building code in a custom block, so it generates the toc from the current node on the fly (no 'tableofcontents' tags even used). It does mean loading every node twice, as the block needs its own copy to do the business.

In this case I have hard coded the options and removed the toc heading (using the block one instead), but you get the drift.

<?php

if (arg(0) == 'node' && is_numeric(arg(1))) {
    $node = node_load(arg(1));
	
  	$nodeout = node_view($node);  	

	 $toc = array();

        // $i = index of header level being processed
        // $matches[0][$i] -> Whole string matched
        // $matches[1][$i] -> First heading level
        // $matches[2][$i] -> Whole string of attributes
        // $matches[3][$i] -> id attibute, used for anchor
        // $matches[4][$i] -> Text of id attribute
        // $matches[5][$i] -> Text inside of h tag
        // $matches[6][$i] -> Close heading level, should be equal to open level
        $matches = array();

        //get all headers of current level, case insensitive
        $pattern = '/<h([2-4])( .*?(id="([^"]+)" ?.*?))?>(.*?)<\/h([2-4])>/is';
          
        $matches = array();
        preg_match_all($pattern, $nodeout, $matches, PREG_PATTERN_ORDER);
        
        $anchors = array();
        for ($i = 0; $i < sizeof($matches[0]); $i++) {

          // Strip HTML and non alphanumerics
          $level = $matches[1][$i];
          $heading = strip_tags($matches[5][$i]);
          $anchor = $matches[4][$i];
          array_push($toc, array(
            'level' => $level, 
            'heading' => $heading, 
            'anchor' => $anchor)
          );
        }
        // Build HTML for the Table of Contents
        $toc_html = "<div class=\"toc\">\n<div class=\"toc-list\">\n<ul>\n";

        $depth = 2;
        foreach ($toc as $index=>$title) {

          // process nested lists
          $curdepth = $title['level'];
          if ($curdepth <= 4) {
            // Close list items but not before no items have been added
            if ($curdepth == $depth && $index != 0) $toc_html .= "</li>\n";
            // Be sure to deal with skipping between non-adjacent h levels
            while ($curdepth != $depth) {
              if ($curdepth > $depth) {
                $toc_html .= "\n<ul>\n";
                $depth++;
              }
              else if ($curdepth < $depth) {
                $toc_html .= "</li>\n</ul>\n";
                $depth--;
                if ($curdepth == $depth) $toc_html .= "</li>\n";
              }
            }

            // insert the li element
            $toc_html .= "\t<li><a href=\"#".$title['anchor']."\">".$title['heading']."</a>";
          }
        }
        // Did we recurse back out? If not, close open lists.
        while ($depth > 2) {
          $toc_html .= "</li>\n</ul>\n";
          $depth = $depth -1;
        }
        $toc_html .= "</li>\n";
        
        $toc_html .= "</ul>\n</div>\n</div>";

	
	print $toc_html;  
}
?>
ged3000’s picture

Not a fix for this module, but another option would be to use the javascript at http://www.quirksmode.org/dom/toc.html to create and populate a table of contents on the fly. (Demo included on that page)

nicholas.alipaz’s picture

any further development on allowing toc's in a block? I would like to see it in d6

nicholas.alipaz’s picture

in d6, in a custom php block I have tried adding this:


$nid = explode('/', $_GET['q']);
if (isset($nid[0]) && $nid[0] == 'node' && isset($nid[1]) && is_numeric($nid[1])) {
  $nid = $nid[1];
  $node = node_load($nid);
  $node = node_view($node);
  $node = preg_replace('/\<div\sclass\="inner-content"\>/', '<div class="inner-content"><p><!-- tocstart --><!-- tableofcontents --><!-- tocend --></p>', $node);
  $node = headinganchors_filter('process', null, null, $node);
  $node = tableofcontents_filter('prepare', null, null, $node);
  $node = tableofcontents_filter('process', null, null, $node);
  $toc = preg_replace('/(.*?)\<\!--\stocstart\s--\>(.*?)\<\!--\stocend\s--\>(.*)/si', '\2', $node);
  echo $toc;
}

Perhaps I missing something, but I can't seem to get the filters to run properly and output the toc. Any help?

nicholas.alipaz’s picture

ok, Looks like I finally got it...

$nid = explode('/', $_GET['q']);
if (isset($nid[0]) && $nid[0] == 'node' && isset($nid[1]) && is_numeric($nid[1])) {
  $nid = $nid[1];
  $node = node_load($nid);
  $node = node_view($node);
  $node = preg_replace('/\<div\sclass\="inner-content"\>/', "<div class=\"inner-content\"><!-- tocstart -->\n<!-- tableofcontents -->\n<!-- tocend -->", $node);
  $node = headinganchors_filter('process', null, null, $node);
  $node = tableofcontents_filter('prepare', null, null, $node);
  $node = tableofcontents_filter('process', null, null, $node);
  $toc = preg_replace('/(.*?)\<!--\stocstart\s--\>(.*?)\<!--\stocend\s--\>(.*)/si', '\2', $node);
  echo $toc;
}

You need to be sure the Headings to Anchors filter still gets ran on the body of the node you are viewing. Then this code goes in a custom php block. It loads up the node and runs the headinganchors and tableofcontents filters on the node then grabs the produced table of contents and displays it in your block.

velden’s picture

@nicholas.alipaz -

That would be so nice if you can use the TOC in a block!
Unfortunately your code echo's an exact copy of my page itself, instead of just the TOC.
Maybe, i just did something wrong? Any suggestions?

nicholas.alipaz’s picture

It will only work if your page has a div surrounding your content area like <div class="inner-content">...yourcontent...</div>, if not then find out what the class is for the div and substitute it for the one my code is searching for. Different themes may have different source.

hepabolu’s picture

I couldn't get #7 to work with version 3 of the module, so I modified #3 (*) and got it working. This requires a [toc hidden:1] in the text.

(*) Changes:
- starting ' added to $pattern
- changed class of ToC list to 'tockblock' to allow separate styling

AlexisWilke’s picture

hepabolu,

Are you working with version 6.x? because this is a bug for v5.x ... and [toc hidden:1] would not work in 5.x anyway!

Thank you.
Alexis

AlexisWilke’s picture

In version 6.x, there is now a block module coming with the Table of Contents plug in.

Version 5 still does not have that feature.

Thank you.
Alexis Wilke