[cloned from http://mindwiki.de/wikka_toc/]

mindWiki TOC


status: proposed, works fine with mindWiki

i'm not sure if all the features provided by the toc action are useful but i wanted a flexible routine that works even with document structures which are for some reason not formed as usually expected, i.e. it will work with samples like the following:

level 1

level 3

level 2


the final version may drop one feature or the other to cut down size of code and processing time. but the action should at least support references to other pages (i.e. a toc can be placed in a page of its own, maybe together with more toc actions listing several wikipages at once, one for each chapter of a large documentation). this is not implemented yet.

the table of contents will list all headlines that follow the line of the table itself as long as they are on the same or a deeper level as the first headline that matched. it is possible to place more than one toc in one page and thus give several sections its own tocs. each toc follows its own logic ant thus may list a headline twice. it's up to the author to set meaningful boundaries as listed below to avoid interferences when more than one toc is used related to the same wikipage.

the action provides following parameters:


headlines will always be rendered with an anchor (<a name=...>). this may be useful to link from external sites as well as using it with a table of contents

furthermore we need to match start and end tag at once to get the contents for the outline array. this should be addressed in other parts of the formatter. with this mode it's obsolete to check wheather headline tags need to be closed. formatting of headlines is possible because the formatter is called recoursively. as yet the routine does not test for cock-eyed formatting like nesting of headlines.

formatters/wakka.php
<?
    // headers
    else if (preg_match("/^(={2,5})(.*)?\\1$/", $thing, $matches)) {
        $level = 6 - strlen($matches[1]);
        $headline = chop($mind->Format($matches[2]));

        if ($level > $toc_level) {
            $mind->toc_stack[$level] = 0;
            $toc_level = $level;
        } else if ($level < $toc_level) $toc_level = $level;
        $mind->toc_stack[$level]++;

        $label = "toc";
        for ($i = 1; $i <= $level; $i++) $label .= "-".(integer)$mind->toc_stack[$i];
        $mind->toc[$mind->tag][] = array($level, "<a href='#".$label."'>".strip_tags($headline)."</a>");
        return "<a name='".$label."'><h".$level.">".$headline."</h".$level.">";
    }
?>


the main workload will be done after the page is (almost) completely rendered and with it all headlines are collected. it will get its own formatter. place the call below the major preg_replace_callback call at the bottom of the script:

formatters/wakka.php
<? if (strstr($text, "{{toc ")) $text = $this->Format($text, "toc"); ?>


the toc action only adjusts some parameters and stores them in an array for later processing. finally it prints out a token which can be replaced later on.

actions/toc.php
<?php
// determing first element of toc
if (!$tag) $tag = $this->tag;
if (!$levels) $levels = 99;
$start = $startlevel == -1 ? 0 : count($this->toc[$tag]);

// fill format string to serve any toc level
if ($format) {
    $format_array = explode(" ", $format);
    $padding = array_pop($format_array);
    $format = join(" ", array_pad ($format_array, 4, $padding));
} else $format = "bullet bullet bullet bullet";

if (!$title) $title = "Table of Contents";

// push parameters to array for later processing
$this->toc_box[] = array ("tag" => $tag, "title" => $title, "format" => $format, "levels" => $levels, "startlevel" => $startlevel, "start" => $start);

// and leave token for replacement in the text
print "{{toc ".count($this->toc_box)."}}";
?>


and here's the formatter that renders the toc itself:

formatters/toc.php
  1. <?php
  2. if ($toc_box && $max = count($this->toc)) {
  3.     foreach ($this->toc_box as $index => $toc) {
  4.         if ($toc["format"] == "mindmap") {
  5.             $result = $this->Action("mindmap ".$this->Link($toc["tag"], "outline.mm"))."\n"; // params
  6.             $text = str_replace("{{toc ".$index."}}", $result, $text);
  7.             continue;
  8.         }
  9.         if ($toc["tag"] && $toc["tag"] != $this->tag) {
  10.             // not yet implemented
  11.              continue;
  12.         } else $outline = $this->toc[$this->tag];
  13.  
  14.         // get part of the toc to be shown and prepare formatting
  15.         for ($i = $start = $toc["start"], $end = 1; ++$i <= $max; $end++) {
  16.             if ($outline[$i][0] < $toc["startlevel"]) { $start++; continue; }
  17.             if($outline[$i][0] < $outline[$start][0]) break;
  18.             $level[$outline[$i][0]] = true;
  19.         }
  20.         $slice = array_slice($outline, $start, $end);
  21.  
  22.         // assign formatting specs to the found levels
  23.         $token = strtok($toc["format"], " ");
  24.         for ($i =1; $i <= 4; $i++) {
  25.             if ($level[$i]) {
  26.                 switch ($token) {
  27.                     case "indent": $opener = "<div class='indent'>"; $closer = "</div>"; $item = "<br />"; break;
  28.                     case "bullet": $opener = "<ul>"; $closer = "</ul>"; $item = "<li>"; break;
  29.                     case "num": $opener = "<ol>"; $closer = "</ol>"; $item = "<li>"; break;
  30.                     case "alpha": $opener = "<ol type='A'>"; $closer = "</ol>"; $item = "<li>"; break;
  31.                     case "roman": $opener = "<ol type='I'>"; $closer = "</ol>"; $item = "<li>"; break;
  32.                     case "loweralpha":  $opener = "<ol type='a'>"; $closer = "</ol>"; $item = "<li>"; break;
  33.                     case "lowerroman":  $opener = "<ol type='i'>"; $closer = "</ol>"; $item = "<li>"; break;
  34.                 }
  35.                 $level[$i] = $array($opener, $closer, $item);
  36.                 $token = strtok(" ");
  37.             }
  38.         }
  39.  
  40.         // and build the toc
  41.         $act_level = 0; $toc_stack = array();
  42.         $result = "<div class='tocheader'>".$toc["title"]."</div>";
  43.         foreach ($outline as $line) {
  44.             if ($line[0] <= $toc["levels"] ) {
  45.                 $item = $level[$line[0]][3];
  46.                 if ($line[0] > $act_level) {
  47.                     for ($i = $act_level; $i <= $line[0]; $i++) {
  48.                         if ($level[$i]) {
  49.                             $result .= $level[$i][0]."\n";
  50.                             $toc_stack[] = $level[$i][1];
  51.                         }
  52.                     }
  53.                     $act_level = $line[0]
  54.                 } else if ($line[0] < $act_level) {
  55.                     for ($i = $act_level; $i >= $line[0]; $i--) if ($level[$i]) $result .= array_pop($toc_stack)."\n";
  56.                     $act_level = $line[0];
  57.                 }
  58.                 $result .= $item.$line[1].($item == "<li>" ? "</li>" : "")."\n";
  59.             }
  60.         }
  61.         $text = str_replace("{{toc ".$index."}}", $result, $text);
  62.     }
  63. }
  64. ?>



CategoryDevelopmentActions
Comments
Comment by TonZijlstra
2005-09-22 19:43:37
I copied the code into my wikka install. It generated two error messages:

Call to a member function on a non-object in /home/ifccc/public_html/formatters/wakka.php

and

Parse error: parse error, unexpected '}' in /home/ifccc/public_html/formatters/toc.php on line 54

no time yet to look into it. Will try and do that later.
Comment by NilsLindenberg
2005-09-22 22:23:15
For the second error: try a ; at the end of line 53:
$act_level = $line[0];

As for the first one: perhaps replacing $mind with $wakka will help (in the first code for formatters/wakka.php)?
Comment by TonZijlstra
2005-09-23 08:06:01
Ok, done both. No more errors.

The current code creates strange behaviour though.

It now renders Level 1 headers as hyperlinks. If you add the toc-action to a page, it no longer renders a page-section, and the page is blank except for header and footer. Intriguing! :)

Will see if I have time to look at the code this weekend in more detail.
Comment by NilsLindenberg
2005-09-23 09:55:27
change the second line in formatters/toc:
if ($this->toc_box ...

that leeds to new errors, but at least the if-clause is processed now.
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki