[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
There are 4 comments on this page. [Show comments]
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki