Location: PHPKode > scripts > Simple TOC > bainternet-simple-toc/simple-toc.php
<?php
/*
Plugin Name: Bainternet Simple TOC
Plugin URI: http://en.bainternet.info/2011/simple-toc-table-of-contents-plugin
Description: Automatically create a wiki like TOC (table of contents) in your posts or pages using shortcode based on your headings.
Version: 0.8.0
Author: Bainternet
Author URI: http://en.bainternet.info
*/
/*  Copyright 2012 Ohad Raz aKa BaInternet  (email : hide@address.com)

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License, version 2, as 
    published by the Free Software Foundation.

    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.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/


if (!class_exists('simple_toc')) {
	class simple_toc{

		function __construct() {
			add_action( 'admin_init', array( $this, 'action_admin_init' ) );
			add_filter("the_content", array( $this, "bainternet_generate_toc" ));
			add_filter("the_content", array( $this, "bainternet_generate_toc_encoded" ));
			add_filter("the_content", array( $this, "toc_by_h_tags" ));
			//plugin links row
            add_filter( 'plugin_row_meta', array($this,'_my_plugin_links'), 10, 2 );
		   	if (is_admin()){
		    	global $pagenow;
		      	if ($pagenow=='post-new.php' || $pagenow=='post.php'){
			 		add_action('admin_footer',array($this,'tinymce_popup'));
		      	}
		   	}
			
		}
		
		//when using tinymce button you need < > are encoded
		public function bainternet_generate_toc_encoded($content) {
			global $toc_u,$post;
			//$this->toc_by_h_tags($content);
			$pos = strpos($content, "[---TOC---]");
			if ($pos === false  || $toc_u) {
				return $content;
			}
			$pos = strpos($content, "[---TOC Header:");
			if ($pos >= 0) {
				$toc_header = '';
				$start = strpos($content, "[---TOC Header:");
				$end = strpos($content, "---]",$start);
				$h = substr($content,$start,($end + 4 - $start));
				$he = str_replace('[---TOC Header:','',$h);
				$he = str_replace('---]','',$he);
				$toc = '<div class="toc"><div class="toc-head">'.apply_filters('Simple_TOC_Heading',$he,$post).'</div><div class="toc_list">';
				$content = str_replace($h,'',$content);
			}else{
				$toc = '<div class="toc"><div class="toc_list">';
			}
			$toc .= apply_filters('Simple_TOC_before_items','',$post).'<ul>';
			$toc .= apply_filters('Simple_TOC_before_first_item','',$post);
			$count = 0;
			while (strpos($content, "[---TOC Heading:") > 0){
				$start = $end = $heading = $h = $anchor = '';
				$start = strpos($content, "[---TOC Heading:");
				$end = strpos($content, "---]",$start);
				$h = substr($content,$start,($end + 4 - $start));
				$heading = str_replace ("[---TOC Heading:",'',$h);
				$heading = str_replace ("---]",'',$heading);
				$heading_slug = str_replace (" ",'-',$heading);
				//fix none unicode anchors?
				$heading_slug = apply_filters('Simple_TOC_Heading_Slugs',sanitize_title($heading_slug),$post);
				$anchor = '<a name="toc-'.$heading_slug.'" style="text-decoration: none;">&nbsp;&nbsp;</a>'; 
				$content = str_replace($h, $anchor,$content);
				$toc .= '<li class="toc_item"><a href="#toc-'.$heading_slug.'">'.$heading.'</a></li>';
				$count = $count + 1;
			}
			$toc .= apply_filters('Simple_TOC_After_Last_item','',$post);
			$toc .= '</ul></div></div>';
			$content = str_replace("[---TOC---]", $toc,$content);
			$toc_u = true;
			return $content;
		}
		//backwords capabilities
		public function bainternet_generate_toc($content) {
			global $toc_u;
			$pos = strpos($content, "<---TOC--->");
			if ($pos === false  || $toc_u) {
				return $content;
			}
			$toc = '<div class="toc"><div class="toc_list"><ul>';
					
			$count = 0;
			while (strpos($content, "<---TOC Heading:") > 0){
				$start = $end = $heading = $h = $anchor = '';
				$start = strpos($content, "<---TOC Heading:");
				$end = strpos($content, "--->",$start);
				$h = substr($content,$start,($end + 4 - $start));
				$heading = str_replace ("<---TOC Heading:",'',$h);
				$heading = str_replace ("--->",'',$heading);
				$heading_slug = str_replace (" ",'-',$heading);
				$anchor = '<a name="toc-'.$heading_slug.'" style="text-decoration: none;">&nbsp;&nbsp;</a>'; 
				$content = str_replace($h, $anchor,$content);
				$toc .= '<li class="toc_item"><a href="#toc-'.$heading_slug.'">'.$heading.'</a></li>';
				$count = $count + 1;
			}
			$toc .= '</ul></div></div>';
			$content = str_replace("<---TOC--->", $toc,$content);
			$toc_u = true;
			return $content;
		}
		
		function action_admin_init() {
			// only hook up these filters if we're in the admin panel, and the current user has permission
			// to edit posts and pages
			if ( current_user_can( 'edit_posts' ) && current_user_can( 'edit_pages' ) ) {
				add_filter( 'mce_buttons', array( $this, 'filter_mce_button' ) );
				add_filter( 'tiny_mce_before_init', array( $this, 'filter_mce_plugin' ) );
			}
		}
		
		function filter_mce_button( $buttons ) {
			// add a separation before our button, here our button's id is "TOC_button"
			array_push( $buttons, '|', 'TOC' );
			return $buttons;
		}
		
		function filter_mce_plugin( $initArray ) {
			// this plugin file will work the magic of our button
			$initArray['setup'] = <<<JS
[function(ed) {
    ed.addButton('TOC', {
        title : 'Simple TOC',
        image : '../wp-content/plugins/bainternet-simple-toc/toc_icon.png',
        onclick : function() {
           var width = jQuery(window).width(), H = jQuery(window).height(), W = ( 720 < width ) ? 720 : width;
	   W = W - 80;
	   H = H - 84;
	   tb_show( 'Simple TOC Shortcode', '#TB_inline?width=' + W + '&height=' + H + '&inlineId=toc-form' );
        }
    });
}][0]
JS;
    return $initArray;

		}
		
		public function toc_by_h_tags($content){
			global $post;
			$pos = strpos($content, "[---ATOC---]");
			if ($pos === false) {
				return $content;
			}
			$pos = strpos($content, "[---TOC Header:");
			if ($pos != false) {
				$start = strpos($content, "[---TOC Header:");
				$end = strpos($content, "---]",$start);
				$h = substr($content,$start,($end + 4 - $start));
				$he = str_replace('[---TOC Header:','',$h);
				$he = str_replace('---]','',$he);
				$toc = '<div class="toc"><div class="toc-head">'.apply_filters('Simple_TOC_Heading',$he,$post).'</div><div class="toc_list">';
				$content = str_replace($h,'',$content);
			}else{
				$toc = '<div class="toc"><div class="toc_list">';
			}
			$toc .= apply_filters('Simple_TOC_before_items','',$post).'<ul>';
			$toc .= apply_filters('Simple_TOC_before_first_item','',$post);
			//tag is?
			$start = $end = '';
			$start = strpos($content, "[---TAG:");
			$end = strpos($content, "---]",$start);
			$tag_c = substr($content,$start,($end + 4 - $start));
			$tag = str_replace('[---TAG:','',$tag_c);
			$tag = str_replace('---]','',$tag);
			$content = str_replace($tag_c,'',$content);
			
			
			$count = 0;
			$doc = new DOMDocument();
			@$doc->loadHTML('<?xml encoding="'.get_bloginfo('charset').'">'.$content);
			$headings = $doc->getElementsByTagName($tag);
			foreach ($headings as $heading){
				// Create the new element 
				$anchor = $doc->createElement('a');
				$anchor->setAttribute('name','toc-'.$count);
				$anchor->setAttribute('style','text-decoration: none;');
				$anchor->innerHTML  = '&nbsp;&nbsp;';
				$heading->parentNode->insertBefore($anchor,$heading);
				$h = $heading_slug = $anchor = '';
				$h = $heading->nodeValue;
				$heading_slug = $count;
				$toc .= '<li class="toc_item"><a href="#toc-'.$heading_slug.'">'.$h.'</a></li>';
				$count++;
			}
			$content = $doc->saveHTML();
			$toc .= apply_filters('Simple_TOC_After_Last_item','',$post);
			$toc .= '</ul></div></div>';
			
			$content = str_replace("[---ATOC---]", $toc,$content);
			return $content;
		}
		
	   public function tinymce_popup(){
	      ?>
	    <script type="text/javascript">
	    jQuery(document).ready(function() {
	      var form = jQuery('<div id="toc-form"><h2>Specify the Shortcode you want to insert.</h2><br /><ul><li><strong>Auto TOC heading</strong> -<small> insert once in the place you want your TOC to show, and specify the tag for heading (eg: h2,h3)</small></li><li><strong>TOC place holder </strong> -<small> insert once in the place you want your TOC to show</small></li><li><strong>TOC Header </strong>- <small>insert once or none at all, anywhere you want, this will show as the TOC heading</small></li><li><strong>TOC Heading </strong>- <small>insert as many headings as you want include there heading, this will be the text at the TOC link, insert where the heading is.</small></li><table id="TOC-table" class="form-table">\
			<tr>\
				<th><label for="TOC-sc_type">What to insert?</label></th>\
				<td><select name="type" id="TOC-sc_type">\
					<option value="4">Auto TOC heading</option>\
					<option value="1">TOC place holder</option>\
					<option value="2">TOC Header</option>\
					<option value="3">TOC Heading</option>\
				</select><br />\
				</td>\
			</tr>\
			<tr id="type_2" style="display: none;">\
					<th><label for="TOC-include">Table of contents header</label></th>\
					<td><input type="text" name="header" id="TOC-header" value="" /><br />\
					<small>Shown on The head of the TOC, add Only Once!!</small>\
					</td>\
			</tr>\
			<tr id="type_3" style="display: none;">\
					<th><label for="TOC-include">TOC ITEM heading</label></th>\
					<td><input type="text" name="heading" id="TOC-heading" value="" /><br />\
					<small>This will be the text of the TOC link ,Usually palce one above each heading in you content</small>\
					</td>\
			</tr>\
			<tr id="type_4">\
					<th><label for="TOC-include">Auto generate heading by tag</label></th>\
					<td><input type="text" name="tag" id="TOC-tag" value="" /><br />\
					<small>This smart shortcode generates headings for you , you just need to specify the heading tag, depends on your content ( but usually h2)</small>\
					</td>\
			</tr>\
		</table>\
		<p class="submit">\
			<input type="button" id="TOC-submit" class="button-primary" value="Insert" name="submit" />\
		</p>\
		</div>');
		
		var table = form.find('table');
		form.appendTo('body').hide();
		// handels the select event
		table.find('#TOC-sc_type').change(function(){
			if (table.find('#TOC-sc_type').val() == 1){
				table.find('#type_2').hide('fast');
				table.find('#type_3').hide('fast');
				table.find('#type_4').hide('fast');
			}
			if (table.find('#TOC-sc_type').val() == 2){
				table.find('#type_3').hide('fast');
				table.find('#type_4').hide('fast');
				table.find('#type_2').show('slow');
			}
			if (table.find('#TOC-sc_type').val() == 3){
				table.find('#type_2').hide('fast');
				table.find('#type_4').hide('fast');
				table.find('#type_3').show('slow');
			}
			if (table.find('#TOC-sc_type').val() == 4){
				table.find('#type_2').hide('fast');
				table.find('#type_3').hide('fast');
				table.find('#type_4').show('slow');
			}
		});
		
		// handles the click event of the submit button
		form.find('#TOC-submit').click(function(){
			// defines the options and their default values
			// again, this is not the most elegant way to do this
			// but well, this gets the job done nonetheless
			
			var shortcode = '[---TOC';
			
			if (table.find('#TOC-sc_type').val() == 2){
				shortcode += ' Header:' + table.find('#TOC-header').val();
			}
			if (table.find('#TOC-sc_type').val() == 3){
				shortcode += ' Heading:' + table.find('#TOC-heading').val();
			}			
			shortcode += '---]';
			if (table.find('#TOC-sc_type').val() == 4){
				shortcode = '[---ATOC---]  <br/> [---TAG:' + table.find('#TOC-tag').val();
				shortcode += '---]';
			}
			table.find('#TOC-heading').val('');
			table.find('#TOC-header').val('');
			table.find('#TOC-tag').val('');
			// inserts the shortcode into the active editor
			tinyMCE.activeEditor.execCommand('mceInsertContent', 0, shortcode);
			
			// closes Thickbox
			tb_remove();
		});
		});
	       </script>
		<?php
	   }
	  	
	  	public function _my_plugin_links($links, $file) { 
	        $plugin = plugin_basename(__FILE__);  
	        if ($file == $plugin) // only for this plugin 
	                return array_merge( $links, 
	            array( '<a href="http://en.bainternet.info/category/plugins">' . __('Other Plugins by this author' ) . '</a>' ), 
	            array( '<a href="http://wordpress.org/support/plugin/bainternet-simple-toc">' . __('Plugin Support') . '</a>' ), 
	            array( '<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=K4MMGF5X3TM5L" target="_blank">' . __('Donate') . '</a>' ) 
	        ); 
	        return $links;
		}

	} //end class
} //end if

$simple_toc = new simple_toc();
Return current item: Simple TOC