Wiki source for MindWikiTOC

Show raw source

[cloned from]

=====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:

- **format="string"** - determines the type of list for the toc. each level can have its own format, the format identifier then are delimited by spaces. if the document has more heading levels than formats are specified, the last format will be used for all following levels. default is a bulleted list. for example format="num bullet alpha" will use numbers for the first //used// level (that may not nessecarily be <h1>!), bullets for the second level and capitals for the third, and - if present - for the fourth level. following formats are available:
- indent - no bullets, no numbering
- bullet - an <ul> list
- num - <ol> a numbered list
- alpha - <ol type='A'> "numbering" with capital letters
- roman - <ol type='I'> roman numbers (capital style)
- loweralpha - <ol type='a'> alpha list with lowercase characters
- lowerroman - <ol type='i'> the same for roman numbers (i, ii, iii, iv)
- **levels="integer"** - a number from 1 to 4 defines the deepest level to be included (default is 4=all). with this parameter you can restrict the toc to the topmost level(s) excluding the more detailed sections.
- **startlevel="integer"** - the counterpart of "levels". this parameter skips the first one or two (or even three) levels and lists **the first** section that matches the starting level. toc generation will end when the level bumps beyond the starting level and ignores //all// following headlines, regardless wheather they indent back into scope or not. when the levels boundary is less than starting level or if no headlines match the given boundaries, it will result in an empty toc. the startlevel can also be set to "-1" which will return //all// headlines from beginning to end, regardless where the toc is placed.

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.

// 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;

$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:

%%(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.

// 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:

if ($toc_box && $max = count($this->toc)) {
foreach ($this->toc_box as $index => $toc) {
if ($toc["format"] == "mindmap") {
$result = $this->Action("mindmap ".$this->Link($toc["tag"], ""))."\n"; // params
$text = str_replace("{{toc ".$index."}}", $result, $text);
if ($toc["tag"] && $toc["tag"] != $this->tag) {
// not yet implemented
} else $outline = $this->toc[$this->tag];

// get part of the toc to be shown and prepare formatting
for ($i = $start = $toc["start"], $end = 1; ++$i <= $max; $end++) {
if ($outline[$i][0] < $toc["startlevel"]) { $start++; continue; }
if($outline[$i][0] < $outline[$start][0]) break;
$level[$outline[$i][0]] = true;
$slice = array_slice($outline, $start, $end);

// assign formatting specs to the found levels
$token = strtok($toc["format"], " ");
for ($i =1; $i <= 4; $i++) {
if ($level[$i]) {
switch ($token) {
case "indent": $opener = "<div class='indent'>"; $closer = "</div>"; $item = "<br />"; break;
case "bullet": $opener = "<ul>"; $closer = "</ul>"; $item = "<li>"; break;
case "num": $opener = "<ol>"; $closer = "</ol>"; $item = "<li>"; break;
case "alpha": $opener = "<ol type='A'>"; $closer = "</ol>"; $item = "<li>"; break;
case "roman": $opener = "<ol type='I'>"; $closer = "</ol>"; $item = "<li>"; break;
case "loweralpha": $opener = "<ol type='a'>"; $closer = "</ol>"; $item = "<li>"; break;
case "lowerroman": $opener = "<ol type='i'>"; $closer = "</ol>"; $item = "<li>"; break;
$level[$i] = $array($opener, $closer, $item);
$token = strtok(" ");

// and build the toc
$act_level = 0; $toc_stack = array();
$result = "<div class='tocheader'>".$toc["title"]."</div>";
foreach ($outline as $line) {
if ($line[0] <= $toc["levels"] ) {
$item = $level[$line[0]][3];
if ($line[0] > $act_level) {
for ($i = $act_level; $i <= $line[0]; $i++) {
if ($level[$i]) {
$result .= $level[$i][0]."\n";
$toc_stack[] = $level[$i][1];
$act_level = $line[0]
} else if ($line[0] < $act_level) {
for ($i = $act_level; $i >= $line[0]; $i--) if ($level[$i]) $result .= array_pop($toc_stack)."\n";
$act_level = $line[0];
$result .= $item.$line[1].($item == "<li>" ? "</li>" : "")."\n";
$text = str_replace("{{toc ".$index."}}", $result, $text);

Valid XHTML :: Valid CSS: :: Powered by WikkaWiki