PHP-based table of contents generator
Posted: Fri Mar 16, 2007 6:03 pm
This bit of code generates a table of contents for a DOMDocument you pass it by checking out the headings, and then inserting a list in a marker element called <div id="toc" /> It's not really procedural, but I removed the abstraction for readability's sake.
Any comments?
Any comments?
Code: Select all
<?php
function generate_toc(DOMDocument $dom) {
// setup xpath, this can be factored out
$xpath = new DOMXPath($dom);
$xpath->registerNamespace('html', "http://www.w3.org/1999/xhtml");
// test for ToC container, if not present don't bother
$container = $xpath->query("//html:div[@id='toc']")->item(0);
if (!$container) return;
// grab all headings h2 and down from the document
$headings = array('h2', 'h3', 'h4', 'h5', 'h6');
foreach ($headings as $k => $v) $headings[$k] = "self::html:$v";
$query_headings = implode(' or ', $headings);
$query = "//*[$query_headings]"; // looks like "//*[self::html:h2 or ...]"
$headings = $xpath->query($query);
// setup the table of contents element
$toc = $dom->createElement('ul');
$container->appendChild($dom->createElement('h2', 'Table of Contents'));
$container->appendChild($toc);
// iterate through headings and build the table of contents
$current_level = 2;
$parents = array(false, $toc);
$indexes = array(0);
$i = 0;
foreach ($headings as $node) {
$level = (int) $node->tagName[1];
$name = $node->textContent; // no support for formatting
while ($level > $current_level) {
if (!$parents[$current_level-1]->lastChild) {
$parents[$current_level-1]->appendChild(
$dom->createElement('li')
);
}
$sublist = $dom->createElement('ul');
$parents[$current_level - 1]->lastChild->appendChild($sublist);
$parents[$current_level] = $sublist;
$current_level++;
$indexes[$current_level - 2] = 0;
}
while ($level < $current_level) {
unset($indexes[$current_level - 2]);
$current_level--;
}
$indexes[$current_level - 2]++;
$line = $dom->createElement('li');
$label = $dom->createElement('span', implode('.', $indexes) . '.');
$label->setAttribute('class', 'toc-label');
$line->appendChild($label);
$link = $dom->createElement('a', $name);
$line->appendChild($link);
$parents[$current_level-1]->appendChild($line);
// setup the anchors
$header_id = $node->getAttribute('id');
if (!$header_id) {
$header_id = 'toclink' . $i;
$node->setAttribute('id', $header_id);
}
$link->setAttribute('href', '#' . $header_id);
}
}
?>