Skip to content

Instantly share code, notes, and snippets.

@turtlepod
Created July 28, 2012 09:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save turtlepod/3192682 to your computer and use it in GitHub Desktop.
Save turtlepod/3192682 to your computer and use it in GitHub Desktop.
Genbu Table Of Content Shortcode
<?php
/*
* Genbu Table Of Content Shortcode
* use [gtoc] to generate shortcode
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Based on (credit) :
* WP TOC by Brendon Boshell http://infinity-infinity.com/
*
* @package Genbu TOC
* @since 1.0.0
*/
add_action('after_setup_theme','genbu_toc_setup');
function genbu_toc_setup(){
/* Genbu Table of Content. */
add_shortcode( "gtoc", "genbu_toc_shortcode_toc" );
add_action( "the_content", "genbu_toc_the_content" );
//remove_filter('the_content', 'wptexturize');
}
// open a nested list
function genbu_toc_open_level($new, $cur, $first, $type) {
$levels = $new - $cur;
$out = "";
for($i = $cur; $i < $new; $i++) {
$level = $i - $first + 2;
if(($level % 2) == 0)
$out .= "<{$type} class='toc-even level-{$level}'>\n";
else
$out .= "<{$type} class='toc-odd level-{$level}'>\n";
}
return $out;
}
// close the list
function genbu_toc_close_level( $new, $cur, $first, $type ) {
$out = "";
for($i = $cur; $i > $new; $i--)
$out .= "</{$type}>\n";
return $out;
}
$wptoc_used_names = array();
function genbu_toc_get_unique_name($heading) {
global $wptoc_used_names;
$n = strip_tags($heading);
$n = str_replace('"', '', strip_tags($heading));
$n = str_replace(" ", "_", $heading);
$n = preg_replace("#[^A-Za-z0-9\-_\:\.]#", "", $n);
$n = preg_replace("#^[^A-Za-z]*?([A-Za-z])#", "$1", $n);
if (isset($wptoc_used_names[$n])) {
$wptoc_used_names[$n]++;
$n .= "_" . $wptoc_used_names[$n];
$wptoc_used_names[$n] = 0;
} else {
$wptoc_used_names[$n] = 0;
}
return $n;
}
function genbu_toc_unique_names_reset() {
global $wptoc_used_names;
$wptoc_used_names = array();
return true;
}
function genbu_toc_shortcode_toc($attribs) {
global $post;
if (is_singular() && !is_attachment()){
genbu_toc_unique_names_reset();
// REMOVE PLUGIN OPTION
$attribs = shortcode_atts(
array(
"depth" => 20,
"listtype" => 'ul',
"title" => __('Table Of Content:','tamatebako')
),
$attribs);
extract($attribs);
$depth = $depth ? $depth : 20;
$list_type = $listtype ? $listtype : "ul";
$title = $title ? $title : __('Table Of Content:','tamatebako');
// get the post
// don't consider stuff in <pre>s
$content = preg_replace("#<pre.*?>(.|\n|\r)*?<\/pre>#i", "", $post->post_content);
$lowest_heading = 1;
// calculate the lowest value heading (ie <hN> where N is a number)
// in the post
for($i = 1; $i <= 6; $i++)
if( preg_match("#<h" . $i . "#i", $content) ) {
$lowest_heading = $i;
break;
}
// maximum
$max_heading = $lowest_heading + $depth - 1;
// find page separation points
$next_pages = array();
preg_match_all("#<\!--nextpage-->#i", $content, $next_pages, PREG_OFFSET_CAPTURE);
$next_pages = $next_pages[0];
// get all headings in post
$headings = array();
preg_match_all("#<h([1-6])>(.*?)</h[1-6]>#i", $content, $headings, PREG_OFFSET_CAPTURE);
$cur_level = $lowest_heading;
$out = '<div id="table-of-content">';
if ($title)
$out .= '<div id="toc-title">' . $title . '</div>';
$out .= genbu_toc_open_level($lowest_heading, $lowest_heading-1, $lowest_heading, $list_type);
$first = true;
$tabs = 1;
// headings...
foreach($headings[2] as $i => $heading) {
$level = $headings[1][$i][0]; // <hN>
if($level > $max_heading) // heading too deep
continue;
if($level > $cur_level) { // this needs to be nested
$out .= str_repeat("\t", $tabs+1) . genbu_toc_open_level( $level, $cur_level, $lowest_heading, $list_type );
$first = true;
$tabs += 2;
}
if(!$first)
$out .= str_repeat("\t", $tabs) . "</li>\n";
$first = false;
if($level < $cur_level) { // jump back up from nest
$out .= str_repeat("\t", $tabs-1) . genbu_toc_close_level( $level, $cur_level, $lowest_heading, $list_type );
$tabs -= 2;
}
$name = genbu_toc_get_unique_name($heading[0]);
$page_num = 1;
$pos = $heading[1];
// find the current page
foreach($next_pages as $p) {
if($p[1] < $pos)
$page_num++;
}
// fix error if heading link overlap / not hieraricaly correct
if ($tabs+1 > 0) {
$tabs = $tabs;
}
else {
$tabs = 0;
}
// output the Contents item with link to the heading. Uses
// unique ID based on the $prefix variable.
if($page_num != 1)
$out .= str_repeat("\t", $tabs) . "<li>\n" . str_repeat("\t", $tabs+1) . "<a href=\"?p=" . $post->ID . "&page=" . $page_num . "#" . esc_attr($name). "\">" . $heading[0] . "</a>\n";
else
$out .= str_repeat("\t", $tabs) . "<li>\n" . str_repeat("\t", $tabs+1) . "<a href=\"#" . esc_attr($name). "\">" . $heading[0] . "</a>\n";
$cur_level = $level; // set the current level we are at
}
if(!$first)
$out .= str_repeat("\t", $tabs) . "</li>\n";
// close up the list
$out .= genbu_toc_close_level( 0, $cur_level, $lowest_heading, $list_type );
$out .= "</div>\n";
// return value to repalce the Shortcode
return $out;
}
}
function genbu_toc_heading_anchor($match) {
$name = genbu_toc_get_unique_name($match[2]);
return '<span id="' .esc_attr($name) . '">' . $match[0] . '</span>';
}
function genbu_toc_the_content($content) {
genbu_toc_unique_names_reset();
$out = preg_replace_callback("#<h([1-6])>(.*?)</h[1-6]>#i", "genbu_toc_heading_anchor", $content);
return $out;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment