>}
*/
function zeitfresser_build_toc_payload( $post_id ) {
static $cache = array();
$post_id = (int) $post_id;
if ( isset( $cache[ $post_id ] ) ) {
return $cache[ $post_id ];
}
$payload = array(
'content' => apply_filters( 'the_content', get_post_field( 'post_content', $post_id ) ),
'items' => array(),
);
if ( ! $post_id || ! is_singular( 'post' ) || ! zeitfresser_show_article_toc() ) {
$cache[ $post_id ] = $payload;
return $payload;
}
$content = trim( (string) $payload['content'] );
if ( '' === $content ) {
$cache[ $post_id ] = $payload;
return $payload;
}
if ( ! class_exists( 'DOMDocument' ) ) {
$cache[ $post_id ] = $payload;
return $payload;
}
libxml_use_internal_errors( true );
$dom = new DOMDocument();
$loaded = $dom->loadHTML(
'
' . $content . '
',
LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
);
if ( ! $loaded ) {
libxml_clear_errors();
$cache[ $post_id ] = $payload;
return $payload;
}
$container = $dom->getElementById( 'zeitfresser-toc-root' );
if ( ! $container ) {
libxml_clear_errors();
$cache[ $post_id ] = $payload;
return $payload;
}
$index = 1;
$toc_items = array();
$xpath = new DOMXPath( $dom );
$headings = $xpath->query( './/h2 | .//h3 | .//h4', $container );
if ( $headings instanceof DOMNodeList ) {
foreach ( $headings as $heading ) {
$text = trim( wp_strip_all_tags( $heading->textContent ) );
if ( '' === $text ) {
continue;
}
$tag_name = strtolower( $heading->nodeName );
$id = $heading->getAttribute( 'id' );
if ( '' === $id ) {
$base_id = sanitize_title( $text );
$id = $base_id ? $base_id : 'section-' . $index;
while ( $dom->getElementById( $id ) ) {
$id = $base_id . '-' . $index;
$index++;
}
$heading->setAttribute( 'id', $id );
}
$toc_items[] = array(
'id' => $id,
'text' => $text,
'level' => (int) substr( $tag_name, 1 ),
);
$index++;
}
}
libxml_clear_errors();
if ( count( $toc_items ) < zeitfresser_get_article_toc_min_headlines() ) {
$cache[ $post_id ] = array(
'content' => zeitfresser_extract_toc_inner_html( $container ),
'items' => array(),
);
return $cache[ $post_id ];
}
$cache[ $post_id ] = array(
'content' => zeitfresser_extract_toc_inner_html( $container ),
'items' => $toc_items,
);
return $cache[ $post_id ];
}
/**
* Extract container inner HTML.
*
* @param DOMNode $node Source node.
* @return string
*/
function zeitfresser_extract_toc_inner_html( $node ) {
$html = '';
if ( ! $node || ! $node->hasChildNodes() ) {
return $html;
}
foreach ( $node->childNodes as $child_node ) {
$html .= $node->ownerDocument->saveHTML( $child_node );
}
return $html;
}
/**
* Return whether the current singular post has a TOC.
*
* @param int|null $post_id Optional post ID.
* @return bool
*/
function zeitfresser_has_floating_toc( $post_id = null ) {
$post_id = $post_id ? (int) $post_id : get_the_ID();
if ( ! $post_id ) {
return false;
}
$payload = zeitfresser_build_toc_payload( $post_id );
return ! empty( $payload['items'] );
}
/**
* Render floating TOC markup.
*
* @param int|null $post_id Optional post ID.
* @return void
*/
function zeitfresser_render_floating_toc( $post_id = null ) {
$post_id = $post_id ? (int) $post_id : get_the_ID();
if ( ! $post_id ) {
return;
}
$payload = zeitfresser_build_toc_payload( $post_id );
if ( empty( $payload['items'] ) ) {
return;
}
?>