1856 lines
		
	
	
		
			53 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			1856 lines
		
	
	
		
			53 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | ||
| 
 | ||
| use function Easy_Plugins\Table_Of_Contents\Cord\br2;
 | ||
| 
 | ||
| class ezTOC_Post {
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * @since 2.0
 | ||
| 	 * @var int
 | ||
| 	 */
 | ||
| 	private $queriedObjectID;
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * @since 2.0
 | ||
| 	 * @var WP_Post
 | ||
| 	 */
 | ||
| 	private $post;
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * @since 2.0
 | ||
| 	 * @var false|string
 | ||
| 	 */
 | ||
| 	private $permalink;
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * The post content broken into pages by user inserting `<!--nextpage-->` into the post content.
 | ||
| 	 * @see ezTOC_Post::extractPages()
 | ||
| 	 * @since 2.0
 | ||
| 	 * @var array
 | ||
| 	 */
 | ||
| 	private $pages = array();
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * The user defined heading levels to be included in the TOC.
 | ||
| 	 * @see ezTOC_Post::getHeadingLevels()
 | ||
| 	 * @since 2.0
 | ||
| 	 * @var array
 | ||
| 	 */
 | ||
| 	private $headingLevels = array();
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Array of nodes that are excluded by class/id selector.
 | ||
| 	 * @since 2.0
 | ||
| 	 * @var string[]
 | ||
| 	 */
 | ||
| 	private $excludedNodes = array();
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Keeps a track of used anchors for collision detecting.
 | ||
| 	 * @see ezTOC_Post::generateHeadingIDFromTitle()
 | ||
| 	 * @since 2.0
 | ||
| 	 * @var array
 | ||
| 	 */
 | ||
| 	private $collision_collector = array();
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * @var bool
 | ||
| 	 */
 | ||
| 	private $hasTOCItems = false;
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * ezTOC_Post constructor.
 | ||
| 	 *
 | ||
| 	 * @since 2.0
 | ||
| 	 *
 | ||
| 	 * @param WP_Post $post
 | ||
| 	 * @param bool    $apply_content_filter Whether or not to apply the `the_content` filter on the post content.
 | ||
| 	 */
 | ||
| 	public function __construct( WP_Post $post, $apply_content_filter = true ) {
 | ||
| 
 | ||
| 		$this->post            = $post;
 | ||
| 		$this->permalink       = get_permalink( $post );
 | ||
| 		$this->queriedObjectID = get_queried_object_id();
 | ||
| 
 | ||
|         $apply_content_filter  = $this->apply_filter_status( $apply_content_filter );
 | ||
| 
 | ||
|         if ( $apply_content_filter ) {
 | ||
| 
 | ||
|             $this->applyContentFilter()->process();
 | ||
|         } else {
 | ||
| 
 | ||
|             $this->process();
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * apply_filter_status function
 | ||
| 	 *
 | ||
| 	 * @since 2.0.51
 | ||
| 	 * @access private
 | ||
| 	 * @param bool $apply_content_filter
 | ||
| 	 * @return bool
 | ||
| 	 */
 | ||
| 	private function apply_filter_status( $apply_content_filter )
 | ||
|     {
 | ||
| 
 | ||
| 		/**
 | ||
| 		 * ez_toc_apply_filter_status Apply filter
 | ||
| 		 * for any plugin which conflict 
 | ||
| 		 * in easy toc plugin
 | ||
| 		 * @since 2.0.51
 | ||
| 		 */
 | ||
|         $plugins = apply_filters(
 | ||
|             'ez_toc_apply_filter_status',
 | ||
|             array(
 | ||
|                 'booster-extension/booster-extension.php',
 | ||
|                 'divi-bodycommerce/divi-bodyshop-woocommerce.php',
 | ||
|                 'social-pug/index.php',
 | ||
| 				'fusion-builder/fusion-builder.php',
 | ||
| 				'modern-footnotes/modern-footnotes.php',
 | ||
| 				'yet-another-stars-rating-premium/yet-another-stars-rating.php'
 | ||
|             )
 | ||
|         );
 | ||
| 
 | ||
|         foreach ( $plugins as $value ) {
 | ||
|             if ( in_array( $value, apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
 | ||
|                 $apply_content_filter = false;
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
| 		$apply_content_filter = apply_filters('ez_toc_apply_filter_status_manually', $apply_content_filter);
 | ||
| 
 | ||
|         return $apply_content_filter;
 | ||
|     }
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * @access public
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @param $id
 | ||
| 	 *
 | ||
| 	 * @return ezTOC_Post|null
 | ||
| 	 */
 | ||
| 	public static function get( $id ) {
 | ||
| 
 | ||
| 		$post = get_post( $id );
 | ||
| 
 | ||
| 		if ( ! $post instanceof WP_Post ) {
 | ||
| 
 | ||
| 			return null;
 | ||
| 		}
 | ||
|                 
 | ||
| 		return new static( $post );
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Process post content for headings.
 | ||
| 	 *
 | ||
| 	 * This must be run after object init or after @see ezTOC_Post::applyContentFilter().
 | ||
| 	 *
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @return static
 | ||
| 	 */
 | ||
| 	private function process() {
 | ||
| 
 | ||
| 		$this->processPages();
 | ||
| 
 | ||
| 		return $this;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Apply `the_content` filter to the post content.
 | ||
| 	 *
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @return static
 | ||
| 	 */
 | ||
| 	private function applyContentFilter() {
 | ||
| 
 | ||
| 		/*
 | ||
| 		 * Parses dynamic blocks out of post_content and re-renders them for gutenberg blocks.
 | ||
| 		 */		
 | ||
| 		if(function_exists('do_blocks')){
 | ||
| 			$this->post->post_content = do_blocks($this->post->post_content);
 | ||
| 		}else{
 | ||
| 			$this->post->post_content = $this->post->post_content;
 | ||
| 		}
 | ||
| 		
 | ||
| 		if( defined('EASY_TOC_AMP_VERSION') && function_exists('ampforwp_is_amp_endpoint') && ampforwp_is_amp_endpoint() ){
 | ||
| 			$ampforwp_pagebuilder_enable = get_post_meta(get_the_ID(),'ampforwp_page_builder_enable', true);
 | ||
| 			if($ampforwp_pagebuilder_enable=='yes' && function_exists('ampforwp_eztoc_PageBuilder_content')){
 | ||
| 				$this->post->post_content = ampforwp_eztoc_PageBuilder_content();
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		add_filter( 'strip_shortcodes_tagnames', array( __CLASS__, 'stripShortcodes' ), 10, 2 );
 | ||
| 
 | ||
| 		/*
 | ||
| 		 * Ensure the ezTOC content filter is not applied when running `the_content` filter.
 | ||
| 		 */
 | ||
| 		remove_filter( 'the_content', array( 'ezTOC', 'the_content' ), 100 );
 | ||
| 
 | ||
| 		$this->post->post_content = apply_filters( 'the_content', strip_shortcodes( $this->post->post_content ) );
 | ||
| 
 | ||
| 		add_filter( 'the_content', array( 'ezTOC', 'the_content' ), 100 );  // increased  priority to fix other plugin filter overwriting our changes
 | ||
| 
 | ||
| 		remove_filter( 'strip_shortcodes_tagnames', array( __CLASS__, 'stripShortcodes' ) );
 | ||
| 
 | ||
| 		return $this;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Callback for the `strip_shortcodes_tagnames` filter.
 | ||
| 	 *
 | ||
| 	 * Strip the shortcodes so their content is no processed for headings.
 | ||
| 	 *
 | ||
| 	 * @see ezTOC_Post::applyContentFilter()
 | ||
| 	 *
 | ||
| 	 * @since 2.0
 | ||
| 	 *
 | ||
| 	 * @param array  $tags_to_remove Array of shortcode tags to remove.
 | ||
| 	 * @param string $content        Content shortcodes are being removed from.
 | ||
| 	 *
 | ||
| 	 * @return array
 | ||
| 	 */
 | ||
| 	public static function stripShortcodes( $tags_to_remove, $content ) {
 | ||
| 
 | ||
| 		/*
 | ||
| 		 * Ensure the ezTOC shortcodes are not processed when applying `the_content` filter
 | ||
| 		 * otherwise an infinite loop may occur.
 | ||
| 		 */
 | ||
| 		$tags_to_remove = apply_filters(
 | ||
| 			'ez_toc_strip_shortcodes_tagnames',
 | ||
| 			array(
 | ||
| 				'ez-toc',
 | ||
| 				apply_filters( 'ez_toc_shortcode', 'toc' ),
 | ||
| 			),
 | ||
| 			$content
 | ||
| 		);
 | ||
| 
 | ||
| 		return $tags_to_remove;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * This is a work around for theme's and plugins
 | ||
| 	 * which break the WordPress global $wp_query var by unsetting it
 | ||
| 	 * or overwriting it which breaks the method call
 | ||
| 	 * that `get_query_var()` uses to return the query variable.
 | ||
| 	 *
 | ||
| 	 * @access protected
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @return int
 | ||
| 	 */
 | ||
| 	protected function getCurrentPage() {
 | ||
| 
 | ||
| 		global $wp_query;
 | ||
| 
 | ||
| 		// Check to see if the global `$wp_query` var is an instance of WP_Query and that the get() method is callable.
 | ||
| 		// If it is then when can simply use the get_query_var() function.
 | ||
| 		if ( $wp_query instanceof WP_Query && is_callable( array( $wp_query, 'get' ) ) ) {
 | ||
| 
 | ||
| 			$page =  get_query_var( 'page', 1 );
 | ||
| 
 | ||
| 			return 1 > $page ? 1 : $page;
 | ||
| 
 | ||
| 			// If a theme or plugin broke the global `$wp_query` var, check to see if the $var was parsed and saved in $GLOBALS['wp_query']->query_vars.
 | ||
| 		} elseif ( isset( $GLOBALS['wp_query']->query_vars[ 'page' ] ) ) {
 | ||
| 
 | ||
| 			return $GLOBALS['wp_query']->query_vars[ 'page' ];
 | ||
| 
 | ||
| 			// We should not reach this, but if we do, lets check the original parsed query vars in $GLOBALS['wp_the_query']->query_vars.
 | ||
| 		} elseif ( isset( $GLOBALS['wp_the_query']->query_vars[ 'page' ] ) ) {
 | ||
| 
 | ||
| 			return $GLOBALS['wp_the_query']->query_vars[ 'page' ];
 | ||
| 
 | ||
| 			// Ok, if all else fails, check the $_REQUEST super global.
 | ||
| 		} elseif ( isset( $_REQUEST[ 'page' ] ) ) {
 | ||
| 
 | ||
| 			return $_REQUEST[ 'page' ];
 | ||
| 		}
 | ||
| 
 | ||
| 		// Finally, return the $default if it was supplied.
 | ||
| 		return 1;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Get the number of page the post has.
 | ||
| 	 *
 | ||
| 	 * @access protected
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @return int
 | ||
| 	 */
 | ||
| 	protected function getNumberOfPages() {
 | ||
| 
 | ||
| 		return count( $this->pages );
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Whether or not the post has multiple pages.
 | ||
| 	 *
 | ||
| 	 * @access protected
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @return bool
 | ||
| 	 */
 | ||
| 	protected function isMultipage() {
 | ||
| 
 | ||
| 		return 1 < $this->getNumberOfPages();
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Parse the post content and headings.
 | ||
| 	 *
 | ||
| 	 * @access private
 | ||
| 	 * @since  2.0
 | ||
| 	 */
 | ||
| 	private function processPages() {
 | ||
| 
 | ||
| 		$content = apply_filters( 'ez_toc_modify_process_page_content', $this->post->post_content );
 | ||
| 		
 | ||
| 		// Fix for wordpress category pages showing wrong toc if they have description
 | ||
| 		if(is_category()){
 | ||
| 			$cat_from_query=get_query_var( 'cat', null ); 
 | ||
| 			if($cat_from_query){
 | ||
| 				$category = get_category($cat_from_query);
 | ||
| 				if(is_object($category) && property_exists($category,'description') && !empty($category->description)){
 | ||
| 					$content = $category->description;
 | ||
| 				}
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if(is_tax() || is_tag()){
 | ||
| 			global $wp_query;
 | ||
| 			$tax = $wp_query->get_queried_object();
 | ||
| 			if(is_object($tax)){
 | ||
| 				$content = apply_filters('ez_toc_modify_taxonomy_content',$tax->description,$tax->term_id);
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if(function_exists('is_product_category') && is_product_category()){
 | ||
| 			$term_object = get_queried_object();			
 | ||
| 			if(!empty($term_object->description)){
 | ||
| 				$content     = $term_object->description;
 | ||
| 			}						
 | ||
| 		}		
 | ||
| 
 | ||
| 		if ( in_array( 'js_composer_salient/js_composer.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
 | ||
| 			$eztoc_post_id=get_the_ID();
 | ||
| 			$eztoc_post_meta = get_option( 'ez-toc-post-meta-content',false);
 | ||
| 			if(!empty($eztoc_post_meta) && !empty($eztoc_post_id) && isset($eztoc_post_meta[$eztoc_post_id])){
 | ||
| 				if ( empty( $content ) ) {
 | ||
| 					$content = $eztoc_post_meta[$eztoc_post_id];
 | ||
| 				} else {
 | ||
| 					$content .= $eztoc_post_meta[$eztoc_post_id];
 | ||
| 				}
 | ||
| 		}
 | ||
| 		} else if ( ( in_array( 'divi-machine/divi-machine.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) || 'Fortunato Pro' == apply_filters( 'current_theme', get_option( 'current_theme' ) ) ) && false != get_option( 'ez-toc-post-content-core-level' ) ) {
 | ||
|                     $content = get_option( 'ez-toc-post-content-core-level' );
 | ||
| 		} else {
 | ||
|                        
 | ||
|                 }
 | ||
| 
 | ||
| 		$pages = array();
 | ||
| 
 | ||
| 		$split = preg_split( '/<!--nextpage-->/msuU', $content );
 | ||
| 
 | ||
| 		$page = $first_page = 1;
 | ||
| 		$totalHeadings = [];
 | ||
| 		if ( is_array( $split ) ) {
 | ||
| 
 | ||
| 
 | ||
| 			foreach ( $split as $content ) {
 | ||
| 
 | ||
| 				$this->extractExcludedNodes( $page, $content );
 | ||
| 
 | ||
| 				$totalHeadings[] = array(
 | ||
| 					'headings' => $this->extractHeadings( $content, $page ),
 | ||
| 					'content'  => $content,
 | ||
| 				);
 | ||
| 
 | ||
| 				$page++;
 | ||
| 			}
 | ||
| 
 | ||
| 		}
 | ||
| 		$pages[$first_page] = $totalHeadings;
 | ||
| 
 | ||
| 		$this->pages = $pages;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Get the post's parse content and headings.
 | ||
| 	 *
 | ||
| 	 * @access public
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @return array
 | ||
| 	 */
 | ||
| 	public function getPages() {
 | ||
| 
 | ||
| 		return $this->pages;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Extract nodes that heading are to be excluded.
 | ||
| 	 *
 | ||
| 	 * @since 2.0
 | ||
| 	 *
 | ||
| 	 * @param int    $page
 | ||
| 	 * @param string $content
 | ||
| 	 */
 | ||
| 	private function extractExcludedNodes( $page, $content ) {
 | ||
| 
 | ||
| 		if ( ! class_exists( 'TagFilter' ) ) {
 | ||
| 
 | ||
|                         if(phpversion() <= 5.6)
 | ||
|                             require_once( EZ_TOC_PATH . '/includes/vendor/ultimate-web-scraper/tag_filter56.php' );
 | ||
|                         else
 | ||
|                             require_once( EZ_TOC_PATH . '/includes/vendor/ultimate-web-scraper/tag_filter.php' );
 | ||
| 		}
 | ||
| 
 | ||
| 		$tagFilterOptions = TagFilter::GetHTMLOptions();
 | ||
| 
 | ||
| 		// Set custom TagFilter options.
 | ||
| 		$tagFilterOptions['charset'] = get_option( 'blog_charset' );
 | ||
| 
 | ||
| 		$html = TagFilter::Explode( $content, $tagFilterOptions );
 | ||
| 
 | ||
| 		/**
 | ||
| 		 * @since 2.0
 | ||
| 		 *
 | ||
| 		 * @param $selectors array  Array of classes/id selector to exclude from TOC.
 | ||
| 		 * @param $content   string Post content.
 | ||
| 		 */
 | ||
| 		$selectors = apply_filters( 'ez_toc_exclude_by_selector', array( '.ez-toc-exclude-headings' ), $content );
 | ||
| 
 | ||
| 		$nodes = $html->Find( implode( ',', $selectors ) );
 | ||
| 		if(isset($nodes['ids'])){
 | ||
| 			foreach ( $nodes['ids'] as $id ) {
 | ||
| 
 | ||
| 				array_push( $this->excludedNodes, $html->Implode( $id, $tagFilterOptions ) );
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		/**
 | ||
| 		 * TagFilter::Implode() writes br tags as `<br>` while WP normalizes to `<br />`.
 | ||
| 		 * Normalize `$eligibleContent` to match WP.
 | ||
| 		 *
 | ||
| 		 * @see wpautop()
 | ||
| 		 */
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Extract the posts content for headings.
 | ||
| 	 *
 | ||
| 	 * @access private
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @param string $content
 | ||
| 	 *
 | ||
| 	 * @return array
 | ||
| 	 */
 | ||
| 	private function extractHeadings( $content, $page = 1 ) {
 | ||
| 
 | ||
| 		$matches = array();
 | ||
| 
 | ||
| 		if ( in_array( 'elementor/elementor.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) || in_array( 'divi-machine/divi-machine.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) || 'Fortunato Pro' == apply_filters( 'current_theme', get_option( 'current_theme' ) ) ) {
 | ||
|                     $content = apply_filters( 'ez_toc_extract_headings_content', $content );           
 | ||
|                 } else {
 | ||
|                     $content = apply_filters( 'ez_toc_extract_headings_content', wptexturize( $content ) );
 | ||
|                 }
 | ||
| 
 | ||
|                 /**
 | ||
|                 * Lasso Product Compatibility
 | ||
|                 * @since 2.0.46
 | ||
|                 */
 | ||
|                 $regEx = apply_filters( 'ez_toc_regex_filteration', '/(<h([1-6]{1})[^>]*>)(.*)<\/h\2>/msuU' );
 | ||
|                 
 | ||
| 		// get all headings
 | ||
| 		// the html spec allows for a maximum of 6 heading depths
 | ||
| 		if ( preg_match_all( $regEx, $content, $matches, PREG_SET_ORDER ) ) {
 | ||
| 
 | ||
| 			$minimum = absint( ezTOC_Option::get( 'start' ) );
 | ||
| 
 | ||
| 			$this->removeHeadingsFromExcludedNodes( $matches );
 | ||
| 			$this->removeHeadings( $matches );
 | ||
| 			$this->excludeHeadings( $matches );
 | ||
| 			$this->removeEmptyHeadings( $matches );
 | ||
| 
 | ||
| 			if ( count( $matches ) >= $minimum ) {
 | ||
| 
 | ||
| 				$this->alternateHeadings( $matches );
 | ||
| 				$this->headingIDs( $matches );
 | ||
| 				$this->addPage( $matches, $page );
 | ||
| 				$this->hasTOCItems = true;
 | ||
| 
 | ||
| 			} else {
 | ||
| 
 | ||
| 				return array();
 | ||
| 			}
 | ||
| 
 | ||
| 		}
 | ||
| 
 | ||
| 		return array_values( $matches ); // Rest the array index.
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * addPage function
 | ||
| 	 *
 | ||
| 	 * @access private
 | ||
| 	 * @since 2.0.50
 | ||
| 	 * @param array|null|false $matches
 | ||
| 	 * @param int $page
 | ||
| 	 * @return void
 | ||
| 	 */
 | ||
| 	private function addPage( &$matches, $page )
 | ||
| 	{
 | ||
| 		foreach ( $matches as $i => $match ) {
 | ||
| 			$matches[ $i ][ 'page' ] = $page;
 | ||
| 		}
 | ||
| 		return $matches;
 | ||
| 	}
 | ||
| 	/**
 | ||
| 	 * Whether or not the string is in one of the excluded nodes.
 | ||
| 	 *
 | ||
| 	 * @since 2.0
 | ||
| 	 *
 | ||
| 	 * @param string $string
 | ||
| 	 *
 | ||
| 	 * @return bool
 | ||
| 	 */
 | ||
| 	private function inExcludedNode( $string ) {
 | ||
| 
 | ||
| 		foreach ( $this->excludedNodes as $node ) {
 | ||
| 
 | ||
| 			if ( empty( $node ) || empty( $string ) ) {
 | ||
| 
 | ||
| 				return false;
 | ||
| 			}
 | ||
| 
 | ||
| 			if ( false !== strpos( $node, $string ) ) {
 | ||
| 
 | ||
| 				return true;
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		return false;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Remove headings that are in excluded nodes.
 | ||
| 	 *
 | ||
| 	 * @since 2.0
 | ||
| 	 *
 | ||
| 	 * @param array $matches
 | ||
| 	 *
 | ||
| 	 * @return array
 | ||
| 	 */
 | ||
| 	private function removeHeadingsFromExcludedNodes( &$matches ) {
 | ||
| 
 | ||
| 		foreach ( $matches as $i => $match ) {
 | ||
| 
 | ||
| 			if ( $this->inExcludedNode( "{$match[3]}</h$match[2]>" ) ) {
 | ||
| 
 | ||
| 				unset( $matches[ $i ] );
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		return $matches;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Get the heading levels to be included in the TOC.
 | ||
| 	 *
 | ||
| 	 * @access private
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @return array
 | ||
| 	 */
 | ||
| 	private function getHeadingLevels() {
 | ||
| 
 | ||
| 		$levels = get_post_meta( $this->post->ID, '_ez-toc-heading-levels', true );
 | ||
| 
 | ||
| 		if ( ! is_array( $levels ) ) {
 | ||
| 
 | ||
| 			$levels = array();
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( empty( $levels ) ) {
 | ||
| 
 | ||
| 			$levels = ezTOC_Option::get( 'heading_levels', array() );
 | ||
| 		}
 | ||
| 
 | ||
| 		$this->headingLevels = $levels;
 | ||
| 
 | ||
| 		return $this->headingLevels;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Remove the heading levels as defined by user settings from the TOC heading matches.
 | ||
| 	 *
 | ||
| 	 * @see ezTOC_Post::extractHeadings()
 | ||
| 	 *
 | ||
| 	 * @access private
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @param array $matches The heading from the post content extracted with preg_match_all().
 | ||
| 	 *
 | ||
| 	 * @return array
 | ||
| 	 */
 | ||
| 	private function removeHeadings( &$matches ) {
 | ||
| 
 | ||
| 		$levels = $this->getHeadingLevels();
 | ||
| 
 | ||
| 		if ( count( $levels ) != 6 ) {
 | ||
| 
 | ||
| 			$new_matches = array();
 | ||
| 
 | ||
| 			foreach ( $matches as $i => $match ) {
 | ||
| 
 | ||
| 				if ( in_array( $matches[ $i ][2], $levels ) ) {
 | ||
| 
 | ||
| 					$new_matches[ $i ] = $matches[ $i ];
 | ||
| 				}
 | ||
| 			}
 | ||
| 
 | ||
| 			$matches = $new_matches;
 | ||
| 		}
 | ||
| 
 | ||
| 		return $matches;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Exclude the heading, by title, as defined by the user settings from the TOC matches.
 | ||
| 	 *
 | ||
| 	 * @see ezTOC_Post::extractHeadings()
 | ||
| 	 *
 | ||
| 	 * @access private
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @param array  $matches The headings from the post content extracted with preg_match_all().
 | ||
| 	 *
 | ||
| 	 * @return array
 | ||
| 	 */
 | ||
| 	private function excludeHeadings( &$matches ) {
 | ||
| 
 | ||
| 		$exclude = get_post_meta( $this->post->ID, '_ez-toc-exclude', true );
 | ||
| 
 | ||
| 		if ( empty( $exclude ) ) {
 | ||
| 
 | ||
| 			$exclude = ezTOC_Option::get( 'exclude' );
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $exclude ) {
 | ||
| 
 | ||
| 			$excluded_headings = explode( '|', $exclude );
 | ||
| 			$excluded_count    = count( $excluded_headings );
 | ||
| 
 | ||
| 			if ( $excluded_count > 0 ) {
 | ||
| 
 | ||
| 				for ( $j = 0; $j < $excluded_count; $j++ ) {
 | ||
| 
 | ||
| 					$excluded_headings[ $j ] = preg_quote( $excluded_headings[ $j ] );
 | ||
| 
 | ||
| 					// escape some regular expression characters
 | ||
| 					// others: http://www.php.net/manual/en/regexp.reference.meta.php
 | ||
| 					$excluded_headings[ $j ] = str_replace(
 | ||
| 						array( '\*', '/', '%' ),
 | ||
| 						array( '.*', '\/', '\%' ),
 | ||
| 						trim( $excluded_headings[ $j ] )
 | ||
| 					);
 | ||
| 				}
 | ||
| 
 | ||
| 				$new_matches = array();
 | ||
| 
 | ||
| 				foreach ( $matches as $i => $match ) {
 | ||
| 
 | ||
| 					$found = false;
 | ||
| 
 | ||
| 					$against = html_entity_decode(
 | ||
|                                                 ( in_array( 'divi-machine/divi-machine.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) || 'Fortunato Pro' == apply_filters( 'current_theme', get_option( 'current_theme' ) ) ) ? strip_tags( str_replace( array( "\r", "\n" ), ' ', $matches[ $i ][0] ) ) : wptexturize(strip_tags( str_replace( array( "\r", "\n" ), ' ', $matches[ $i ][0] ) ) ),
 | ||
| 						ENT_NOQUOTES,
 | ||
| 						get_option( 'blog_charset' )
 | ||
| 					);
 | ||
| 
 | ||
| 					for ( $j = 0; $j < $excluded_count; $j++ ) {
 | ||
| 
 | ||
| 						// Since WP manipulates the post content it is required that the excluded header and
 | ||
| 						// the actual header be manipulated similarly so a match can be made.
 | ||
| 						$pattern = html_entity_decode(
 | ||
| 							( in_array( 'divi-machine/divi-machine.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) || 'Fortunato Pro' == apply_filters( 'current_theme', get_option( 'current_theme' ) ) ) ? $excluded_headings[ $j ] : wptexturize($excluded_headings[ $j ]),
 | ||
| 							ENT_NOQUOTES,
 | ||
| 							get_option( 'blog_charset' )
 | ||
| 						);
 | ||
| 
 | ||
| 						if ( @preg_match( '/^' . $pattern . '$/imU', $against ) ) {
 | ||
| 
 | ||
| 							$found = true;
 | ||
| 							break;
 | ||
| 						}
 | ||
| 					}
 | ||
| 
 | ||
| 					if ( ! $found ) {
 | ||
| 
 | ||
| 						$new_matches[ $i ] = $matches[ $i ];
 | ||
| 					}
 | ||
| 				}
 | ||
| 
 | ||
| 					$matches = $new_matches;
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		return $matches;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Return the alternate headings added by the user, saved in the post meta.
 | ||
| 	 *
 | ||
| 	 * The result is an associative array where the `key` is the original post heading
 | ||
| 	 * and the `value` is the alternate heading.
 | ||
| 	 *
 | ||
| 	 * @access private
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @return array
 | ||
| 	 */
 | ||
| 	private function getAlternateHeadings() {
 | ||
| 
 | ||
| 		$alternates = array();
 | ||
| 		$value      = get_post_meta( $this->post->ID, '_ez-toc-alttext', true );
 | ||
| 
 | ||
| 		if ( $value ) {
 | ||
| 
 | ||
| 			$headings = preg_split( '/\r\n|[\r\n]/', $value );
 | ||
| 			$count    = count( $headings );
 | ||
| 
 | ||
| 			if ( $headings ) {
 | ||
| 
 | ||
| 				for ( $k = 0; $k < $count; $k++ ) {
 | ||
| 
 | ||
| 					$heading = explode( '|', $headings[ $k ] );
 | ||
| 
 | ||
| 					/**
 | ||
| 					 * @link https://wordpress.org/support/topic/undefined-offset-1-home-blog-public-wp-content-plugins-easy-table-of-contents/
 | ||
| 					 */
 | ||
| 					if ( ! is_array( $heading) ||
 | ||
| 					     ! array_key_exists( 0, $heading ) ||
 | ||
| 					     ! array_key_exists( 1, $heading )
 | ||
| 					) {
 | ||
| 						continue;
 | ||
| 					}
 | ||
| 
 | ||
| 					if ( 0 < strlen( $heading[0] ) && 0 < strlen( $heading[1] ) ) {
 | ||
| 
 | ||
| 						$alternates[ $heading[0] ] = $heading[1];
 | ||
| 					}
 | ||
| 				}
 | ||
| 
 | ||
| 			}
 | ||
| 
 | ||
| 		}
 | ||
| 
 | ||
| 		return $alternates;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Add the alternate headings to the array.
 | ||
| 	 *
 | ||
| 	 * @see ezTOC_Post::extractHeadings()
 | ||
| 	 *
 | ||
| 	 * @access private
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @param array $matches The heading from the post content extracted with preg_match_all().
 | ||
| 	 *
 | ||
| 	 * @return array
 | ||
| 	 */
 | ||
| 	private function alternateHeadings( &$matches ) {
 | ||
| 
 | ||
| 		$alt_headings = $this->getAlternateHeadings();
 | ||
| 
 | ||
| 		if ( 0 < count( $alt_headings ) ) {
 | ||
| 
 | ||
| 			foreach ( $matches as $i => $match ) {
 | ||
| 
 | ||
| 				foreach ( $alt_headings as $original_heading => $alt_heading ) {
 | ||
| 
 | ||
| 					// Cleanup and texturize so alt heading can match heading in post content.
 | ||
|                                         if ( in_array( 'divi-machine/divi-machine.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) || 'Fortunato Pro' == apply_filters( 'current_theme', get_option( 'current_theme' ) ) ) {
 | ||
|                                             $original_heading = trim( $original_heading );
 | ||
|                                         }else {
 | ||
|                                             $original_heading = wptexturize( trim( $original_heading ) );
 | ||
|                                         }
 | ||
| 					// Deal with special characters such as non-breakable space.
 | ||
| 					$original_heading = str_replace(
 | ||
| 						array( "\xc2\xa0" ),
 | ||
| 						array( ' ' ),
 | ||
| 						$original_heading
 | ||
| 					);
 | ||
| 
 | ||
| 					// Escape for regular expression.
 | ||
| 					$original_heading = preg_quote( $original_heading );
 | ||
| 
 | ||
| 					// Escape for regular expression some other characters: http://www.php.net/manual/en/regexp.reference.meta.php
 | ||
| 					$original_heading = str_replace(
 | ||
| 						array( '\*', '/', '%' ),
 | ||
| 						array( '.*', '\/', '\%' ),
 | ||
| 						$original_heading
 | ||
| 					);
 | ||
| 
 | ||
| 					// Cleanup subject so alt heading can match heading in post content.
 | ||
| 					$subject = strip_tags( $matches[ $i ][0] );
 | ||
| 
 | ||
| 					// Deal with special characters such as non-breakable space.
 | ||
| 					$subject = str_replace(
 | ||
| 						array( "\xc2\xa0" ),
 | ||
| 						array( ' ' ),
 | ||
| 						$subject
 | ||
| 					);
 | ||
| 
 | ||
| 					if ( @preg_match( '/^' . $original_heading . '$/imU', $subject ) ) {
 | ||
| 
 | ||
| 						$matches[ $i ]['alternate'] = $alt_heading;
 | ||
| 					}
 | ||
| 				}
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		return $matches;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Add the heading `id` to the array.
 | ||
| 	 *
 | ||
| 	 * @see ezTOC_Post::extractHeadings()
 | ||
| 	 *
 | ||
| 	 * @access private
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @param array $matches The heading from the post content extracted with preg_match_all().
 | ||
| 	 *
 | ||
| 	 * @return mixed
 | ||
| 	 */
 | ||
| 	private function headingIDs( &$matches ) {
 | ||
| 
 | ||
| 		foreach ( $matches as $i => $match ) {
 | ||
| 
 | ||
| 			$matches[ $i ]['id'] = $this->generateHeadingIDFromTitle( $matches[ $i ][0] );
 | ||
| 		}
 | ||
| 
 | ||
| 		return $matches;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Create unique heading ID from heading string.
 | ||
| 	 *
 | ||
| 	 * @access private
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @param string $heading
 | ||
| 	 *
 | ||
| 	 * @return bool|string
 | ||
| 	 */
 | ||
| 	private function generateHeadingIDFromTitle( $heading ) {
 | ||
| 
 | ||
| 		$return = false;
 | ||
| 
 | ||
| 		if ( $heading ) {
 | ||
| 			$heading = apply_filters( 'ez_toc_url_anchor_target_before', $heading );
 | ||
| 			// WP entity encodes the post content.
 | ||
| 			$return = html_entity_decode( $heading, ENT_QUOTES, get_option( 'blog_charset' ) );
 | ||
| 			$return = br2( $return, ' ' );
 | ||
| 			$return = trim( strip_tags( $return ) );
 | ||
| 
 | ||
| 			// Convert accented characters to ASCII.
 | ||
| 			$return = remove_accents( $return );
 | ||
| 
 | ||
| 			// replace newlines with spaces (eg when headings are split over multiple lines)
 | ||
| 			$return = str_replace( array( "\r", "\n", "\n\r", "\r\n" ), ' ', $return );
 | ||
| 
 | ||
| 			// Remove `&` and ` ` NOTE: in order to strip "hidden" ` `,
 | ||
| 			// title needs to be converted to HTML entities.
 | ||
| 			// @link https://stackoverflow.com/a/21801444/5351316
 | ||
| 			$return = htmlentities2( $return );
 | ||
| 			$return = str_replace( array( '&', ' '), ' ', $return );
 | ||
| 			$return = str_replace( array( '­' ),'', $return );					// removed silent hypen 
 | ||
| 			$return = html_entity_decode( $return, ENT_QUOTES, get_option( 'blog_charset' ) );
 | ||
| 
 | ||
| 			// remove non alphanumeric chars
 | ||
| 			$return = preg_replace( '/[\x00-\x1F\x7F]*/u', '', $return );
 | ||
| 
 | ||
| 			//for procesing shortcode in headings
 | ||
| 			$return = apply_filters('ez_toc_table_heading_title_anchor',$return);
 | ||
| 			// Reserved Characters.
 | ||
| 			// * ' ( ) ; : @ & = + $ , / ? # [ ]
 | ||
| 			$return = str_replace(
 | ||
| 				array( '*', '\'', '(', ')', ';', '@', '&', '=', '+', '$', ',', '/', '?', '#', '[', ']' ),
 | ||
| 				'',
 | ||
| 				$return
 | ||
| 			);
 | ||
| 
 | ||
| 			// Unsafe Characters.
 | ||
| 			// % { } | \ ^ ~ [ ] `
 | ||
| 			$return = str_replace(
 | ||
| 				array( '%', '{', '}', '|', '\\', '^', '~', '[', ']', '`' ),
 | ||
| 				'',
 | ||
| 				$return
 | ||
| 			);
 | ||
| 
 | ||
| 			// Special Characters.
 | ||
| 			// $ - _ . + ! * ' ( ) ,
 | ||
| 			// Special case for Apostrophes (’) which is causing TOC link to break in Block themes and CM Tooltip Glossary plugin #556
 | ||
| 			$return = str_replace(
 | ||
| 				array( '$', '.', '+', '!', '*', '\'', '(', ')', ',', '’' ),
 | ||
| 				'',
 | ||
| 				$return
 | ||
| 			);
 | ||
| 
 | ||
| 			// Dashes
 | ||
| 			// Special Characters.
 | ||
| 			// - (minus) - (dash) – (en dash) — (em dash)
 | ||
| 			$return = str_replace(
 | ||
| 				array( '-', '-', '–', '—' ),
 | ||
| 				'-',
 | ||
| 				$return
 | ||
| 			);
 | ||
| 
 | ||
| 			// Curley quotes.
 | ||
| 			// ‘ (curly single open quote) ’ (curly single close quote) “ (curly double open quote) †(curly double close quote)
 | ||
| 			$return = str_replace(
 | ||
| 				array( '‘', '’', '“', 'â€' ),
 | ||
| 				'',
 | ||
| 				$return
 | ||
| 			);
 | ||
| 
 | ||
| 			// AMP/Caching plugins seems to break URL with the following characters, so lets replace them.
 | ||
| 			$return = str_replace( array( ':' ), '_', $return );
 | ||
| 
 | ||
| 			// Convert space characters to an `_` (underscore).
 | ||
| 			$return = preg_replace( '/\s+/', '_', $return );
 | ||
| 
 | ||
| 			// Replace multiple `-` (hyphen) with a single `-` (hyphen).
 | ||
| 			$return = preg_replace( '/-+/', '-', $return );
 | ||
| 
 | ||
| 			// Replace multiple `_` (underscore) with a single `_` (underscore).
 | ||
| 			$return = preg_replace( '/_+/', '_', $return );
 | ||
| 
 | ||
| 			// Remove trailing `-` (hyphen) and `_` (underscore).
 | ||
| 			$return = rtrim( $return, '-_' );
 | ||
| 
 | ||
| 			/*
 | ||
| 			 * Encode URI based on ECMA-262.
 | ||
| 			 *
 | ||
| 			 * Only required to support the jQuery smoothScroll library.
 | ||
| 			 *
 | ||
| 			 * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI#Description
 | ||
| 			 * @link https://stackoverflow.com/a/19858404/5351316
 | ||
| 			 */
 | ||
| 			$return = preg_replace_callback(
 | ||
| 				"{[^0-9a-z_.!~*'();,/?:@&=+$#-]}i",
 | ||
| 				function( $m ) {
 | ||
| 
 | ||
| 					return sprintf( '%%%02X', ord( $m[0] ) );
 | ||
| 				},
 | ||
| 				$return
 | ||
| 			);
 | ||
| 
 | ||
| 			// lowercase everything?
 | ||
| 			if ( ezTOC_Option::get( 'lowercase' ) ) {
 | ||
| 
 | ||
| 				$return = strtolower( $return );
 | ||
| 			}
 | ||
| 
 | ||
| 			// if blank, then prepend with the fragment prefix
 | ||
| 			// blank anchors normally appear on sites that don't use the latin charset
 | ||
| 			//@since  2.0.59
 | ||
| 			if ( !$return || true == ezTOC_Option::get( 'all_fragment_prefix' ) ) {
 | ||
| 				$return = ( ezTOC_Option::get( 'fragment_prefix' ) ) ? ezTOC_Option::get( 'fragment_prefix' ) : '_';
 | ||
| 			}
 | ||
| 
 | ||
| 			// hyphenate?
 | ||
| 			if ( ezTOC_Option::get( 'hyphenate' ) ) {
 | ||
| 
 | ||
| 				$return = str_replace( '_', '-', $return );
 | ||
| 				$return = preg_replace( '/-+/', '-', $return );
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( array_key_exists( $return, $this->collision_collector ) ) {
 | ||
| 
 | ||
| 			$this->collision_collector[ $return ]++;
 | ||
| 			$return .= '-' . $this->collision_collector[ $return ];
 | ||
| 
 | ||
| 		} else {
 | ||
| 
 | ||
| 			$this->collision_collector[ $return ] = 1;
 | ||
| 		}
 | ||
| 
 | ||
| 		return apply_filters( 'ez_toc_url_anchor_target', $return, $heading );
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Remove any empty headings from the TOC.
 | ||
| 	 *
 | ||
| 	 * @access private
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @param array $matches The heading from the post content extracted with preg_match_all().
 | ||
| 	 *
 | ||
| 	 * @return array
 | ||
| 	 */
 | ||
| 	private function removeEmptyHeadings( &$matches ) {
 | ||
| 
 | ||
| 		$new_matches = array();
 | ||
| 		foreach ( $matches as $i => $match ) {
 | ||
| 
 | ||
| 			if ( trim( strip_tags( $matches[ $i ][0] ) ) != false ) {
 | ||
| 
 | ||
| 				$new_matches[ $i ] = $matches[ $i ];
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 
 | ||
| 			$matches = $new_matches;
 | ||
| 
 | ||
| 		return $matches;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Whether or not the post has TOC items.
 | ||
| 	 *
 | ||
| 	 * @see ezTOC_Post::extractHeadings()
 | ||
| 	 *
 | ||
| 	 * @access public
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @return bool
 | ||
| 	 */
 | ||
| 	public function hasTOCItems() {
 | ||
| 
 | ||
| 		return $this->hasTOCItems;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Get the headings of the current page of the post.
 | ||
| 	 *
 | ||
| 	 * @access public
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @param int|null $page
 | ||
| 	 *
 | ||
| 	 * @return array
 | ||
| 	 */
 | ||
| 	public function getHeadings( $page = null ) {
 | ||
| 
 | ||
| 		$headings = array();
 | ||
| 
 | ||
| 		if ( is_null( $page ) ) {
 | ||
| 
 | ||
| 			$page = $this->getCurrentPage();
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( !empty( $this->pages ) || isset( $this->pages[ $page ] ) ) {
 | ||
| 
 | ||
| 			$matches = $this->getHeadingsfromPageContents( $page );
 | ||
| 
 | ||
| 			foreach ( $matches as $i => $match ) {
 | ||
| 
 | ||
|                 $headings[] = str_replace(
 | ||
|                     array(
 | ||
|                         $matches[ $i ][1],                // start of heading
 | ||
|                         '</h' . $matches[ $i ][2] . '>'   // end of heading
 | ||
|                     ),
 | ||
|                     array(
 | ||
|                         '>',
 | ||
|                         '</h' . $matches[ $i ][2] . '>'
 | ||
|                     ),
 | ||
|                    apply_filters('ez_toc_content_heading_title',$matches[ $i ][0])
 | ||
|                 );
 | ||
| 
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		return $headings;
 | ||
| 	}
 | ||
| 	/**
 | ||
| 	 * Get the heading title id.
 | ||
| 	 *
 | ||
| 	 * @access public
 | ||
| 	 * @since  2.0.58
 | ||
| 	 *
 | ||
| 	 * @param int|null $page
 | ||
| 	 *
 | ||
| 	 * @return array
 | ||
| 	 */
 | ||
| 	public function getTocTitleId( $page = null ) {
 | ||
| 		$nav_data = array();
 | ||
| 		if ( is_null( $page ) ) {
 | ||
| 			$page = $this->getCurrentPage();
 | ||
| 		}
 | ||
| 		if ( !empty( $this->pages ) || isset( $this->pages[ $page ] ) ) {
 | ||
| 			$matches = $this->getHeadingsfromPageContents( $page );
 | ||
| 			foreach ( $matches as $i => $match ) {
 | ||
| 				$nav_data[$i]['title'] = strip_tags( $matches[ $i ][0] );
 | ||
| 				$nav_data[ $i ]['id'] = strtolower(str_replace( '_', '-', $matches[ $i ]['id'] ));
 | ||
| 			}
 | ||
| 		}
 | ||
| 		return $nav_data;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Get the heading with in page anchors of the current page of the post.
 | ||
| 	 *
 | ||
| 	 * @access public
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @param int|null $page
 | ||
| 	 *
 | ||
| 	 * @return array
 | ||
| 	 */
 | ||
| 	public function getHeadingsWithAnchors( $page = null ) {
 | ||
| 
 | ||
| 		$headings = array();
 | ||
| 
 | ||
| 		if ( is_null( $page ) ) {
 | ||
| 
 | ||
| 			$page = $this->getCurrentPage();
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( !empty( $this->pages ) || isset( $this->pages[ $page ] ) ) {
 | ||
| 
 | ||
| 			$matches = $this->getHeadingsfromPageContents( $page );
 | ||
| 			foreach ( $matches as $i => $match ) {
 | ||
| 
 | ||
| 				$anchor     = $matches[ $i ]['id'];
 | ||
| 
 | ||
| 				$headings[] = str_replace(
 | ||
| 					array(
 | ||
| 						$matches[ $i ][1],                // start of heading
 | ||
| 						'</h' . $matches[ $i ][2] . '>'   // end of heading
 | ||
| 					),
 | ||
| 					array(
 | ||
| 						'><span class="ez-toc-section" id="' . $anchor . '"></span>',
 | ||
| 						'<span class="ez-toc-section-end"></span></h' . $matches[ $i ][2] . '>'
 | ||
| 					),
 | ||
| 					apply_filters('ez_toc_content_heading_title_anchor',$matches[ $i ][0])
 | ||
| 				);
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		return $headings;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * getHeadingsfromPageContents function
 | ||
| 	 *
 | ||
| 	 * @access private
 | ||
| 	 * @since 2.0.50
 | ||
| 	 * @param int $page
 | ||
| 	 * @return array|null
 | ||
| 	 */
 | ||
| 	private function getHeadingsfromPageContents( $page = 1 )
 | ||
| 	{
 | ||
| 		$headings = [];
 | ||
| 		$first_page = 1;
 | ||
| 		foreach( $this->pages[ $first_page ] as $attributes ) 
 | ||
| 		{
 | ||
| 			if( isset($attributes['headings'][0]['page'])  && $page == $attributes['headings'][0]['page'] ) 
 | ||
| 			{
 | ||
| 				foreach( $attributes['headings'] as $heading ) 
 | ||
| 				{
 | ||
| 					array_push( $headings, $heading );
 | ||
| 				}
 | ||
| 			}
 | ||
| 		}
 | ||
| 		
 | ||
| 		return $headings;
 | ||
| 	} 
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * createTOCParent function
 | ||
| 	 *
 | ||
| 	 * @param string $prefix
 | ||
| 	 * @return void|mixed|string|null
 | ||
| 	 */
 | ||
| 	private function createTOCParent( $prefix = "ez-toc", $toc_more = array() )
 | ||
| 	{
 | ||
| 		$html = ''; 
 | ||
| 		$first_page = 1;
 | ||
| 		$headings = array();
 | ||
| 		foreach ( $this->pages[ $first_page ] as $attribute )
 | ||
| 		{
 | ||
| 			$headings = array_merge( $headings, $attribute[ 'headings' ] );
 | ||
| 		}
 | ||
| 
 | ||
| 		if( !empty( $headings ) )
 | ||
| 		{
 | ||
| 			$html .= $this->createTOC( $first_page, $headings, $prefix, $toc_more );
 | ||
| 		}
 | ||
| 
 | ||
| 		return $html;
 | ||
| 	}
 | ||
| 	/**
 | ||
| 	 * Get the post TOC list.
 | ||
| 	 *
 | ||
| 	 * @access public
 | ||
| 	 * @param string $prefix
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @return string
 | ||
| 	 */
 | ||
| 	public function getTOCList($prefix = "ez-toc", $options = []) {
 | ||
| 
 | ||
| 		$html = '';
 | ||
| 
 | ||
| 		$toc_more = isset($options['view_more']) ? array( 'view_more' => $options['view_more'] )  : array();
 | ||
| 
 | ||
| 		if(isset($options['hierarchy'])){
 | ||
| 			$toc_more['hierarchy'] = true;
 | ||
| 		}elseif(isset($options['no_hierarchy'])){
 | ||
| 			$toc_more['no_hierarchy'] = true;
 | ||
| 		}
 | ||
| 
 | ||
| 		if(isset($options['collapse_hd'])){
 | ||
| 			$toc_more['collapse_hd'] = true;
 | ||
| 		}elseif(isset($options['no_collapse_hd'])){
 | ||
| 			$toc_more['no_collapse_hd'] = true;
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $this->hasTOCItems ) {
 | ||
| 			
 | ||
| 			$html = $this->createTOCParent($prefix, $toc_more);
 | ||
| 			$visiblityClass = '';
 | ||
| 			if( ezTOC_Option::get( 'visibility_hide_by_default' ) && 'js' == ezTOC_Option::get( 'toc_loading' ) &&  ezTOC_Option::get( 'visibility' ))
 | ||
| 			{
 | ||
| 				$visiblityClass = "eztoc-toggle-hide-by-default";
 | ||
| 			}
 | ||
| 			if( get_post_meta( $this->post->ID, '_ez-toc-visibility_hide_by_default', true ) && 'js' == ezTOC_Option::get( 'toc_loading' ) && ezTOC_Option::get( 'visibility' ))
 | ||
| 			{
 | ||
| 				$visiblityClass = "eztoc-toggle-hide-by-default";
 | ||
| 			}
 | ||
| 			if(is_array($options) && key_exists( 'visibility_hide_by_default', $options ) && $options['visibility_hide_by_default'] == true && 'js' == ezTOC_Option::get( 'toc_loading' ) && ezTOC_Option::get( 'visibility' )){
 | ||
| 				$visiblityClass = "eztoc-toggle-hide-by-default";
 | ||
| 			}elseif(is_array($options) && key_exists( 'visibility_show_by_default', $options ) && $options['visibility_show_by_default'] == true && 'js' == ezTOC_Option::get( 'toc_loading' ) && ezTOC_Option::get( 'visibility' )){
 | ||
| 				$visiblityClass = "";
 | ||
| 			}			
 | ||
| 			$html  = "<ul class='{$prefix}-list {$prefix}-list-level-1 $visiblityClass' >" . $html . "</ul>";
 | ||
| 		}
 | ||
| 
 | ||
| 		return $html;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Get the post Sticky Toggle TOC content block.
 | ||
| 	 *
 | ||
| 	 * @access public
 | ||
| 	 * @return string
 | ||
| 	 * @since  2.0.32
 | ||
| 	 *
 | ||
| 	 */
 | ||
| 	public function getStickyToggleTOC() {
 | ||
| 		$classSticky = array( 'ez-toc-sticky-v' . str_replace( '.', '_', ezTOC::VERSION ) );
 | ||
| 		$htmlSticky  = '';
 | ||
| 		if ( $this->hasTOCItems() ) {
 | ||
| 			$classSticky[] = 'counter-flat';
 | ||
| 			if( ezTOC_Option::get( 'heading-text-direction', 'ltr' ) == 'ltr' ) {
 | ||
|                 $classSticky[] = 'ez-toc-sticky-toggle-counter';
 | ||
|             }
 | ||
|             if( ezTOC_Option::get( 'heading-text-direction', 'ltr' ) == 'rtl' ) {
 | ||
|                 $classSticky[] = 'ez-toc-sticky-toggle-counter-rtl';
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 			$classSticky = array_filter( $classSticky );
 | ||
| 			$classSticky = array_map( 'trim', $classSticky );
 | ||
| 			$classSticky = array_map( 'sanitize_html_class', $classSticky );
 | ||
| 
 | ||
| 
 | ||
|             $ezTocStickyToggleDirection = 'ez-toc-sticky-toggle-direction';
 | ||
| 
 | ||
| 			if ( ezTOC_Option::get( 'show_heading_text' ) ) {
 | ||
| 				$toc_title = apply_filters('ez_toc_sticky_title', ezTOC_Option::get( 'heading_text' ));
 | ||
| 				$toc_title_tag = ezTOC_Option::get( 'heading_text_tag' );
 | ||
| 				$toc_title_tag = $toc_title_tag?$toc_title_tag:'p';
 | ||
| 				if ( strpos( $toc_title, '%PAGE_TITLE%' ) !== false ) {
 | ||
| 					$toc_title = str_replace( '%PAGE_TITLE%', get_the_title(), $toc_title );
 | ||
| 				}
 | ||
| 				if ( strpos( $toc_title, '%PAGE_NAME%' ) !== false ) {
 | ||
| 					$toc_title = str_replace( '%PAGE_NAME%', get_the_title(), $toc_title );
 | ||
| 				}
 | ||
| 					$htmlSticky .= '<div class="ez-toc-sticky-title-container">' . PHP_EOL;
 | ||
| 
 | ||
| 				$htmlSticky .= '<'.esc_attr($toc_title_tag).' class="ez-toc-sticky-title">' . esc_html__( htmlentities( $toc_title, ENT_COMPAT, 'UTF-8' ), 'easy-table-of-contents' ) . '</'.esc_attr($toc_title_tag).'>' . PHP_EOL;
 | ||
| 					$htmlSticky .= '<a class="ez-toc-close-icon" href="#" onclick="ezTOC_hideBar(event)" aria-label="×"><span aria-hidden="true">×</span></a>' . PHP_EOL;
 | ||
| 					$htmlSticky .= '</div>' . PHP_EOL;
 | ||
| 			} else {
 | ||
| 				$htmlSticky .= '<div class="ez-toc-sticky-title-container">' . PHP_EOL;
 | ||
| 				$htmlSticky .= '<a class="ez-toc-close-icon" href="#" onclick="ezTOC_hideBar(event)" aria-label="Close"><span aria-hidden="true">×</span></a>' . PHP_EOL;
 | ||
| 				$htmlSticky .= '</div>' . PHP_EOL;
 | ||
| 			}
 | ||
| 			$htmlSticky  .= '<div id="ez-toc-sticky-container" class="ez-toc-sticky-container ' . implode( ' ', $classSticky ) . '">' . PHP_EOL;
 | ||
| 			ob_start();
 | ||
| 			do_action( 'ez_toc_sticky_toggle_before' );
 | ||
| 			$htmlSticky .= ob_get_clean();
 | ||
| 			$htmlSticky .= "<nav class='$ezTocStickyToggleDirection'>" . $this->getTOCList( "ez-toc-sticky" ) . "</nav>";
 | ||
| 			ob_start();
 | ||
| 			do_action( 'ez_toc_sticky_toggle_after' );
 | ||
| 			$htmlSticky .= ob_get_clean();
 | ||
| 			$htmlSticky .= '</div>' . PHP_EOL;						
 | ||
| 		}
 | ||
| 		return $htmlSticky;
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Get the post TOC content block.
 | ||
| 	 *
 | ||
| 	 * @access public
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @return string
 | ||
| 	 */
 | ||
| 	public function getTOC($options = []) {
 | ||
| 
 | ||
| 		$class = array( 'ez-toc-v' . str_replace( '.', '_', ezTOC::VERSION ) );
 | ||
| 		$html  = '';
 | ||
| 
 | ||
| 		if ( $this->hasTOCItems() ) {
 | ||
| 			$wrapping_class_add = "";
 | ||
| 			if(ezTOC_Option::get( 'toc_wrapping' )){
 | ||
| 				$wrapping_class_add='-text';
 | ||
| 			}
 | ||
| 
 | ||
| 			$toc_align = get_post_meta( get_the_ID(), '_ez-toc-alignment', true );
 | ||
| 
 | ||
| 			if ( !$toc_align || empty( $toc_align ) || $toc_align == 'none' ) {
 | ||
| 				$toc_align = ezTOC_Option::get( 'wrapping' );
 | ||
| 			}
 | ||
| 
 | ||
| 			// wrapping css classes
 | ||
| 			switch ( $toc_align ) {
 | ||
| 
 | ||
| 				case 'left':
 | ||
| 					$class[] = 'ez-toc-wrap-left'.esc_attr($wrapping_class_add);
 | ||
| 					break;
 | ||
| 
 | ||
| 				case 'right':
 | ||
| 					$class[] = 'ez-toc-wrap-right'.esc_attr($wrapping_class_add);
 | ||
| 					break;
 | ||
| 				case 'center':
 | ||
| 					$class[] = 'ez-toc-wrap-center';
 | ||
| 					break;						
 | ||
| 				case 'none':					
 | ||
| 				default:
 | ||
| 					// do nothing
 | ||
| 			}
 | ||
| 
 | ||
| 	        $show_counter = (isset($options['no_counter']) && $options['no_counter'] == true ) ? false : true;
 | ||
| 
 | ||
| 	        $post_hide_counter = get_post_meta( get_the_ID(), '_ez-toc-hide_counter', true );
 | ||
| 
 | ||
| 	        if($post_hide_counter){
 | ||
| 	        	$show_counter = false;
 | ||
| 	        }
 | ||
| 
 | ||
| 	        if( $show_counter ){
 | ||
| 	        	$hierarchical = ezTOC_Option::get( 'show_hierarchy' );
 | ||
| 	        	if(isset($options['hierarchy'])){
 | ||
| 	        		$hierarchical = true;
 | ||
| 	        	}elseif(isset($options['no_hierarchy'])){
 | ||
| 	        		$hierarchical = false;
 | ||
| 	        	}
 | ||
| 
 | ||
| 	            if ( $hierarchical ) {
 | ||
| 	            	$class[] = 'counter-hierarchy';
 | ||
| 	            } else {
 | ||
| 	            	$class[] = 'counter-flat';
 | ||
| 	            }
 | ||
| 	            if( ezTOC_Option::get( 'heading-text-direction', 'ltr' ) == 'ltr' ) {
 | ||
| 	                $class[] = 'ez-toc-counter';
 | ||
| 	            }
 | ||
| 	            if( ezTOC_Option::get( 'heading-text-direction', 'ltr' ) == 'rtl' ) {
 | ||
| 	                $class[] = 'ez-toc-counter-rtl';
 | ||
| 	            }
 | ||
| 	        }
 | ||
| 
 | ||
| 			// colour themes
 | ||
| 			switch ( ezTOC_Option::get( 'theme' ) ) {
 | ||
| 
 | ||
| 				case 'light-blue':
 | ||
| 					$class[] = 'ez-toc-light-blue';
 | ||
| 					break;
 | ||
| 
 | ||
| 				case 'white':
 | ||
| 					$class[] = 'ez-toc-white';
 | ||
| 					break;
 | ||
| 
 | ||
| 				case 'black':
 | ||
| 					$class[] = 'ez-toc-black';
 | ||
| 					break;
 | ||
| 
 | ||
| 				case 'transparent':
 | ||
| 					$class[] = 'ez-toc-transparent';
 | ||
| 					break;
 | ||
| 
 | ||
| 				case 'grey':
 | ||
| 					$class[] = 'ez-toc-grey';
 | ||
| 					break;
 | ||
| 
 | ||
| 				case 'custom':
 | ||
| 					$class[] = 'ez-toc-custom';
 | ||
| 					break;
 | ||
| 			}
 | ||
| 
 | ||
| 			$custom_classes = ezTOC_Option::get( 'css_container_class', '' );			
 | ||
| 
 | ||
|             $class[] = 'ez-toc-container-direction';
 | ||
| 			
 | ||
| 			if ( 0 < strlen( $custom_classes ) ) {
 | ||
| 
 | ||
| 				$custom_classes = explode( ' ', $custom_classes );
 | ||
| 				$custom_classes = apply_filters( 'ez_toc_container_class', $custom_classes, $this );
 | ||
| 
 | ||
| 				if ( is_array( $custom_classes ) ) {
 | ||
| 
 | ||
| 					$class = array_merge( $class, $custom_classes );
 | ||
| 				}
 | ||
| 			}
 | ||
| 
 | ||
| 			$class = array_filter( $class );
 | ||
| 			$class = array_map( 'trim', $class );
 | ||
| 			$class = array_map( 'sanitize_html_class', $class );
 | ||
| 
 | ||
| 			$html .= '<div id="ez-toc-container" class="' . implode( ' ', $class ) . '">' . PHP_EOL;
 | ||
|                         
 | ||
|             if( ezTOC_Option::get( 'toc_loading' ) == 'js' ){
 | ||
| 				$html .= $this->get_js_based_toc_heading($options);
 | ||
| 			}else{
 | ||
| 				$html .= $this->get_css_based_toc_heading($options);
 | ||
| 			}            
 | ||
| 
 | ||
| 			ob_start();
 | ||
| 			do_action( 'ez_toc_before' );
 | ||
| 			$html .= ob_get_clean();
 | ||
| 
 | ||
| 			$html .= '<nav>' . $this->getTOCList('ez-toc', $options) . '</nav>';
 | ||
| 
 | ||
| 			ob_start();
 | ||
| 			do_action( 'ez_toc_after' );
 | ||
| 			$html .= ob_get_clean();
 | ||
| 
 | ||
| 			$html .= '</div>' . PHP_EOL;
 | ||
| 			
 | ||
| 		}
 | ||
| 
 | ||
| 		return $html;
 | ||
| 	}
 | ||
| 
 | ||
| 	private function get_js_based_toc_heading($options){
 | ||
| 
 | ||
| 		$html = '';						
 | ||
| 		$html .= '<div class="ez-toc-title-container">' . PHP_EOL;
 | ||
| 		$header_label = '';
 | ||
| 		$show_header_text = ezTOC_Option::get( 'show_heading_text' );
 | ||
| 		if(isset($options['label'])){
 | ||
| 			$show_header_text = true;
 | ||
| 		}elseif(isset($options['no_label'])){
 | ||
| 			$show_header_text = false;
 | ||
| 		}
 | ||
| 		$read_time = array();
 | ||
| 		if(isset($options['read_time'])){
 | ||
| 			$read_time['read_time'] = $options['read_time'];
 | ||
| 		}
 | ||
| 	if ( $show_header_text ) {
 | ||
| 
 | ||
| 		$toc_title = get_post_meta( get_the_ID(), '_ez-toc-header-label', true );
 | ||
| 
 | ||
| 		if ( !$toc_title || empty( $toc_title ) ) {
 | ||
| 			$toc_title = ezTOC_Option::get( 'heading_text' );
 | ||
| 		}
 | ||
| 
 | ||
| 		$toc_title_tag = ezTOC_Option::get( 'heading_text_tag' );
 | ||
| 		$toc_title_tag = $toc_title_tag?$toc_title_tag:'p';
 | ||
| 
 | ||
| 		if ( strpos( $toc_title, '%PAGE_TITLE%' ) !== false ) {
 | ||
| 
 | ||
| 			$toc_title = str_replace( '%PAGE_TITLE%', get_the_title(), $toc_title );
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( strpos( $toc_title, '%PAGE_NAME%' ) !== false ) {
 | ||
| 
 | ||
| 			$toc_title = str_replace( '%PAGE_NAME%', get_the_title(), $toc_title );
 | ||
| 		}
 | ||
| 		if(isset($options['header_label'])){
 | ||
| 			$toc_title = $options['header_label'];
 | ||
| 		}
 | ||
| 		$headerTextToggleClass = '';
 | ||
| 		$headerTextToggleStyle = '';
 | ||
| 		
 | ||
| 		if ( ezTOC_Option::get( 'visibility_on_header_text' ) ) {
 | ||
| 			$headerTextToggleClass = 'ez-toc-toggle';
 | ||
| 			$headerTextToggleStyle = 'style="cursor: pointer"';
 | ||
| 		}
 | ||
| 		$header_label = '<'.esc_attr($toc_title_tag).' class="ez-toc-title ' . $headerTextToggleClass .'" ' . $headerTextToggleStyle . '>' . esc_html__( htmlentities( $toc_title, ENT_COMPAT, 'UTF-8' ), 'easy-table-of-contents' ). '</'.esc_attr($toc_title_tag).'>' . PHP_EOL;
 | ||
| 		$html .= $header_label;
 | ||
| 													
 | ||
| 	} 
 | ||
| 	$html .= '<span class="ez-toc-title-toggle">';
 | ||
| 
 | ||
| 	$label_below_html = '';
 | ||
| 	$show_toggle_view = ezTOC_Option::get( 'visibility' );
 | ||
| 	if(isset($options['toggle']) && $options['toggle'] == true){
 | ||
| 		$show_toggle_view = true;
 | ||
| 	}elseif(isset($options['no_toggle']) && $options['no_toggle'] == true){
 | ||
| 		$show_toggle_view = false;
 | ||
| 	}
 | ||
| 	if ( $show_toggle_view ) {
 | ||
| 								
 | ||
| 		$icon = ezTOC::getTOCToggleIcon();
 | ||
| 		if( function_exists( 'ez_toc_pro_activation_link' ) ) {
 | ||
| 				$icon = apply_filters('ez_toc_modify_icon',$icon);
 | ||
| 				$label_below_html = apply_filters('ez_toc_label_below_html',$label_below_html, $read_time);
 | ||
| 		}							   
 | ||
| 		$html .= '<a href="#" class="ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle" aria-label="Toggle Table of Content"><span class="ez-toc-js-icon-con">'.$icon.'</span></a>';
 | ||
| 		 
 | ||
| 	}
 | ||
| 			$html .= '</span>';
 | ||
| 			$html .= '</div>' . PHP_EOL;
 | ||
| 			$html .= $label_below_html;
 | ||
| 				
 | ||
| 		return $html;
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	//css based heaing function
 | ||
| 	private function get_css_based_toc_heading($options){
 | ||
| 
 | ||
| 		$html = '';	
 | ||
| 		$header_label = '';
 | ||
| 		$show_header_text = true;
 | ||
| 		if(isset($options['no_label']) && $options['no_label'] == true){
 | ||
| 			$show_header_text = false;
 | ||
| 		}
 | ||
| 	if ( $show_header_text && ezTOC_Option::get( 'show_heading_text' ) ) {
 | ||
| 
 | ||
| 		$toc_title = ezTOC_Option::get( 'heading_text' );
 | ||
| 		$toc_title_tag = ezTOC_Option::get( 'heading_text_tag' );
 | ||
| 		$toc_title_tag = $toc_title_tag?$toc_title_tag:'p';
 | ||
| 		if ( strpos( $toc_title, '%PAGE_TITLE%' ) !== false ) {
 | ||
| 
 | ||
| 			$toc_title = str_replace( '%PAGE_TITLE%', get_the_title(), $toc_title );
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( strpos( $toc_title, '%PAGE_NAME%' ) !== false ) {
 | ||
| 
 | ||
| 			$toc_title = str_replace( '%PAGE_NAME%', get_the_title(), $toc_title );
 | ||
| 		}
 | ||
| 					
 | ||
| 		if(isset($options['header_label'])){
 | ||
| 			$toc_title = $options['header_label'];
 | ||
| 		}
 | ||
| 
 | ||
| 		$header_label = '<'.esc_attr($toc_title_tag).' class="ez-toc-title">' . esc_html__( htmlentities( $toc_title, ENT_COMPAT, 'UTF-8' ), 'easy-table-of-contents' ). '</'.esc_attr($toc_title_tag).'>' . PHP_EOL;
 | ||
| 		if (!ezTOC_Option::get( 'visibility' ) ) {
 | ||
| 			$html .='<div class="ez-toc-title-container">'.$header_label.'</div>';
 | ||
| 		}															
 | ||
| 	} 
 | ||
| 	
 | ||
| 
 | ||
| 	$show_toggle_view = true;
 | ||
| 	if(isset($options['no_toggle']) && $options['no_toggle'] == true){
 | ||
| 		$show_toggle_view = false;
 | ||
| 	}
 | ||
| 
 | ||
| 	if ( $show_toggle_view && ezTOC_Option::get( 'visibility' ) ) {
 | ||
| 			$cssIconID = uniqid();
 | ||
| 			
 | ||
| 			$inputCheckboxExludeStyle = "";
 | ||
| 			if ( ezTOC_Option::get( 'exclude_css' ) ) {
 | ||
| 				$inputCheckboxExludeStyle = "style='display:none'";
 | ||
| 			}
 | ||
| 			$toggle_view='';
 | ||
| 			if(ezTOC_Option::get('visibility_hide_by_default')==true){
 | ||
| 					$toggle_view= "checked";
 | ||
| 			}
 | ||
| 			if( true == get_post_meta( $this->post->ID, '_ez-toc-visibility_hide_by_default', true ) ){
 | ||
| 					$toggle_view= "checked";
 | ||
| 			}
 | ||
| 			if( $options !== null && !empty( $options ) && is_array( $options ) && key_exists( 'visibility_hide_by_default', $options ) && true == $options['visibility_hide_by_default'] ) {
 | ||
| 					$toggle_view= "checked";
 | ||
| 			}
 | ||
| 			$toc_icon = ezTOC::getTOCToggleIcon();
 | ||
| 		    $label_below_html = '';
 | ||
| 		    $read_time = array();
 | ||
| 		    if(isset($options['read_time']) && $options['read_time'] != ''){
 | ||
| 		    	$read_time['read_time'] = $options['read_time'];
 | ||
| 		    }
 | ||
| 			if( function_exists( 'ez_toc_pro_activation_link' ) ) {
 | ||
| 				$toc_icon = apply_filters('ez_toc_modify_icon',$toc_icon);
 | ||
| 				$label_below_html = apply_filters('ez_toc_label_below_html',$label_below_html, $read_time);
 | ||
| 		     }				
 | ||
| 			if ( ezTOC_Option::get( 'visibility_on_header_text' ) ) {		
 | ||
| 				$html .= '<label for="ez-toc-cssicon-toggle-item-' . $cssIconID . '" class="ez-toc-cssicon-toggle-label">' .$header_label. $toc_icon . '</label>'.$label_below_html.'<input type="checkbox" ' . $inputCheckboxExludeStyle . ' id="ez-toc-cssicon-toggle-item-' . $cssIconID . '" '.$toggle_view.' />';
 | ||
| 			}else{
 | ||
| 				if(function_exists('ez_toc_pro_inline_css_func')){
 | ||
| 					$html .= '<div class="ez-toc-cssicon-toggle-label">'.$header_label.'<label for="ez-toc-cssicon-toggle-item-' . $cssIconID . '">' . $toc_icon . '</label></div>'.$label_below_html.'<input type="checkbox" ' . $inputCheckboxExludeStyle . ' id="ez-toc-cssicon-toggle-item-' . $cssIconID . '" '.$toggle_view.' aria-label="Toggle" />';
 | ||
| 				}else{
 | ||
| 					$html .= $header_label.'<label for="ez-toc-cssicon-toggle-item-' . $cssIconID . '" class="ez-toc-cssicon-toggle-label">' . $toc_icon . '</label>'.$label_below_html.'<input type="checkbox" ' . $inputCheckboxExludeStyle . ' id="ez-toc-cssicon-toggle-item-' . $cssIconID . '" '.$toggle_view.' aria-label="Toggle" />';
 | ||
| 				}
 | ||
| 				
 | ||
| 				
 | ||
| 			}
 | ||
| 					
 | ||
| 		}
 | ||
| 		return $html;
 | ||
| 	}
 | ||
|         
 | ||
| 	/**
 | ||
| 	 * Displays the post's TOC.
 | ||
| 	 *
 | ||
| 	 * @access public
 | ||
| 	 * @since  2.0
 | ||
| 	 */
 | ||
| 	public function toc() {
 | ||
| 
 | ||
| 		echo $this->getTOC();
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * Generate the TOC list items for a given page within a post.
 | ||
| 	 *
 | ||
| 	 * @access private
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @param int   $page    The page of the post to create the TOC items for.
 | ||
| 	 * @param array $matches The heading from the post content extracted with preg_match_all().
 | ||
| 	 *
 | ||
| 	 * @return string The HTML list of TOC items.
 | ||
| 	 */
 | ||
| 	private function createTOC( $page, $matches, $prefix = "ez-toc", $toc_more = array() ) {
 | ||
| 
 | ||
| 		// Whether or not the TOC should be built flat or hierarchical.
 | ||
| 		$hierarchical = ezTOC_Option::get( 'show_hierarchy' );
 | ||
| 
 | ||
| 		if(isset($toc_more['hierarchy'])){
 | ||
| 			$hierarchical = true;
 | ||
| 		}elseif(isset($toc_more['no_hierarchy'])){
 | ||
| 			$hierarchical = false;
 | ||
| 		}
 | ||
| 
 | ||
| 		$html = $toc_type = $collapse_status = '';
 | ||
| 
 | ||
| 		if(isset($toc_more['collapse_hd'])){
 | ||
| 			$collapse_status = true;
 | ||
| 		}elseif(isset($toc_more['no_collapse_hd'])){
 | ||
| 			$collapse_status = false;
 | ||
| 		}
 | ||
| 
 | ||
| 		$count_matches = is_array($matches) ? count($matches) : '';
 | ||
| 
 | ||
| 		$toc_type = ezTOC_Option::get( 'toc_loading' );
 | ||
| 
 | ||
| 		if ( $hierarchical ) {
 | ||
| 
 | ||
| 			//To not show view more in Hierarchy
 | ||
| 			unset($toc_more['view_more']);
 | ||
| 
 | ||
| 			$current_depth      = 100;    // headings can't be larger than h6 but 100 as a default to be sure
 | ||
| 			$numbered_items     = array();
 | ||
| 			$numbered_items_min = null;
 | ||
| 
 | ||
| 			// find the minimum heading to establish our baseline
 | ||
| 			foreach ( $matches as $i => $match ) {
 | ||
| 				if ( $current_depth > $matches[ $i ][2] ) {
 | ||
| 					$current_depth = (int) $matches[ $i ][2];
 | ||
| 				}
 | ||
| 			}
 | ||
| 
 | ||
| 			$numbered_items[ $current_depth ] = 0;
 | ||
| 			$numbered_items_min               = $current_depth;
 | ||
| 
 | ||
| 			foreach ( $matches as $i => $match ) {
 | ||
| 
 | ||
| 				$level = $matches[ $i ][2];
 | ||
| 				$count = $i + 1;
 | ||
| 
 | ||
| 				if ( $current_depth == (int) $matches[ $i ][2] ) {
 | ||
| 
 | ||
| 					$html .= "<li class='{$prefix}-page-" . $page . " {$prefix}-heading-level-" . $current_depth . "'>";
 | ||
| 				}
 | ||
| 
 | ||
| 				// start lists
 | ||
| 				if ( $current_depth != (int) $matches[ $i ][2] ) {
 | ||
| 
 | ||
| 					for ( $current_depth; $current_depth < (int) $matches[ $i ][2]; $current_depth++ ) {
 | ||
| 
 | ||
| 						$numbered_items[ $current_depth + 1 ] = 0;
 | ||
| 						//Hide Level 4 Headings
 | ||
| 						$sub_active = '';
 | ||
| 						if($level > 3){
 | ||
| 							$sub_active = apply_filters('ez_toc_hierarchy_js_add_attr', $sub_active, $collapse_status);
 | ||
| 						}
 | ||
| 						$html .= "<ul class='{$prefix}-list-level-" . $level . "' ".$sub_active."><li class='{$prefix}-heading-level-" . $level . "'>";
 | ||
| 					}
 | ||
| 				}
 | ||
| 
 | ||
| 				$title = isset( $matches[ $i ]['alternate'] ) ? $matches[ $i ]['alternate'] : $matches[ $i ][0];
 | ||
| 				//check for line break
 | ||
| 				if(!ezTOC_Option::get( 'prsrv_line_brk' )){
 | ||
| 					$title = br2( $title, ' ' );
 | ||
| 				}
 | ||
| 				$title = strip_tags( apply_filters( 'ez_toc_title', $title ), apply_filters( 'ez_toc_title_allowable_tags', '' ) );
 | ||
| 
 | ||
| 				$html .= $this->createTOCItemAnchor( $matches[ $i ]['page'], $matches[ $i ]['id'], $title, $count );
 | ||
| 
 | ||
| 				// end lists
 | ||
| 				if ( $i != count( $matches ) - 1 ) {
 | ||
| 
 | ||
| 					if ( $current_depth > (int) $matches[ $i + 1 ][2] ) {
 | ||
| 
 | ||
| 						for ( $current_depth; $current_depth > (int) $matches[ $i + 1 ][2]; $current_depth-- ) {
 | ||
| 
 | ||
| 							$html .= '</li></ul>';
 | ||
| 							$numbered_items[ $current_depth ] = 0;
 | ||
| 						}
 | ||
| 					}
 | ||
| 
 | ||
| 					if ( $current_depth == (int) @$matches[ $i + 1 ][2] ) {
 | ||
| 
 | ||
| 						$html .= '</li>';
 | ||
| 					}
 | ||
| 
 | ||
| 				} else {
 | ||
| 
 | ||
| 					// this is the last item, make sure we close off all tags
 | ||
| 					for ( $current_depth; $current_depth >= $numbered_items_min; $current_depth-- ) {
 | ||
| 
 | ||
| 						$html .= '</li>';
 | ||
| 
 | ||
| 						if ( $current_depth != $numbered_items_min ) {
 | ||
| 							$html .= '</ul>';
 | ||
| 						}
 | ||
| 					}
 | ||
| 				}
 | ||
| 			}
 | ||
| 
 | ||
| 		} else {
 | ||
| 			if(isset($toc_more['view_more']) && $toc_more['view_more']>0){
 | ||
| 				//No. of Headings
 | ||
| 				$no_of_headings = $toc_more['view_more'];
 | ||
| 				if(is_array($matches)){
 | ||
| 					foreach ( $matches as $i => $match ) {
 | ||
| 						$count = $i + 1;
 | ||
| 						$title = isset( $matches[ $i ]['alternate'] ) ? $matches[ $i ]['alternate'] : $matches[ $i ][0];
 | ||
| 						$title = strip_tags( apply_filters( 'ez_toc_title', $title ), apply_filters( 'ez_toc_title_allowable_tags', '' ) );
 | ||
| 						if($count <= $no_of_headings){
 | ||
| 							$html .= "<li class='{$prefix}-page-" . $page . "'>";
 | ||
| 							$html .= $this->createTOCItemAnchor( $matches[ $i ]['page'], $matches[ $i ]['id'], $title, $count );
 | ||
| 							$html .= '</li>';
 | ||
| 						}else{
 | ||
| 							$detect = '';
 | ||
| 							$is_more_last = false;
 | ||
| 							if('css' == $toc_type && $i == $no_of_headings && function_exists('ez_toc_non_amp') && ez_toc_non_amp()){
 | ||
| 								$html .= '</ul><input type="checkbox" id="ez-toc-more-toggle-css"/><ul class="ez-toc-more-wrp" style="--start: '.$i.'">';
 | ||
| 							}
 | ||
| 							if($i == count($matches)-1){
 | ||
| 								$detect = 'm-last';
 | ||
| 								$is_more_last = true;
 | ||
| 							}
 | ||
| 							$html .= "<li class='{$prefix}-page-" . $page . " ez-toc-more-link " . $detect . "'>";
 | ||
| 							$html .= $this->createTOCItemAnchor( $matches[ $i ]['page'], $matches[ $i ]['id'], $title, $count );
 | ||
| 							$html .= '</li>';
 | ||
| 							if($is_more_last && 'css' == $toc_type && function_exists('ez_toc_non_amp') && ez_toc_non_amp()){
 | ||
| 								$html .= '</ul>';
 | ||
| 							}
 | ||
| 						}
 | ||
| 					}
 | ||
| 				}
 | ||
| 			}else{
 | ||
| 				if(is_array($matches)){
 | ||
| 					foreach ( $matches as $i => $match ) {
 | ||
| 						$count = $i + 1;
 | ||
| 						$title = isset( $matches[ $i ]['alternate'] ) ? $matches[ $i ]['alternate'] : $matches[ $i ][0];
 | ||
| 						$title = strip_tags( apply_filters( 'ez_toc_title', $title ), apply_filters( 'ez_toc_title_allowable_tags', '' ) );
 | ||
| 						$html .= "<li class='{$prefix}-page-" . $page . "'>";
 | ||
| 						$html .= $this->createTOCItemAnchor( $matches[ $i ]['page'], $matches[ $i ]['id'], $title, $count );
 | ||
| 						$html .= '</li>';
 | ||
| 					}
 | ||
| 				}
 | ||
| 			}
 | ||
| 		}
 | ||
| 
 | ||
| 		$html = apply_filters('ez_toc_pro_html_modifier', $html, $toc_more, $count_matches, $toc_type);
 | ||
| 
 | ||
| 		return do_shortcode($html);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * @access private
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @param int    $page
 | ||
| 	 * @param string $id
 | ||
| 	 * @param string $title
 | ||
| 	 * @param int    $count
 | ||
| 	 *
 | ||
| 	 * @return string
 | ||
| 	 */
 | ||
| 	private function createTOCItemAnchor( $page, $id, $title, $count ) {
 | ||
| 		if (ezTOC_Option::get( 'remove_special_chars_from_title' )) {
 | ||
| 			$title = str_replace(':', '', $title);
 | ||
| 		}
 | ||
| 		
 | ||
| 		$anch_name = 'href';
 | ||
| 		if(ezTOC_Option::get( 'toc_loading' ) == 'js' && ezTOC_Option::get( 'smooth_scroll' ) && ezTOC_Option::get( 'avoid_anch_jump' )){
 | ||
| 			$anch_name = 'href="#" data-href';
 | ||
| 		}
 | ||
| 
 | ||
| 		return sprintf(
 | ||
| 			'<a class="ez-toc-link ez-toc-heading-' . $count . '" '.$anch_name.'="%1$s" title="%2$s">%3$s</a>',
 | ||
| 			esc_url( $this->createTOCItemURL( $id, $page ) ),
 | ||
| 			esc_attr( strip_tags( $title ) ),
 | ||
| 			$title
 | ||
| 		);
 | ||
| 	}
 | ||
| 
 | ||
| 	/**
 | ||
| 	 * @access private
 | ||
| 	 * @since  2.0
 | ||
| 	 *
 | ||
| 	 * @param string $id
 | ||
| 	 * @param int    $page
 | ||
| 	 *
 | ||
| 	 * @return string
 | ||
| 	 */
 | ||
| 	private function createTOCItemURL( $id, $page ) {
 | ||
| 
 | ||
| 		$current_post = $this->post->ID === $this->queriedObjectID;
 | ||
| 		$current_page = $this->getCurrentPage();
 | ||
| 
 | ||
| 		$anch_url = $this->permalink;
 | ||
| 
 | ||
| 		//Ajax Load more 
 | ||
| 		//@since 2.0.61
 | ||
| 		if(ezTOC_Option::get( 'ajax_load_more' ) && isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'){
 | ||
| 			$anch_url = $_SERVER['HTTP_REFERER'];
 | ||
| 		}
 | ||
| 
 | ||
| 		if ( $page === $current_page && $current_post ) {
 | ||
| 
 | ||
| 			return (ezTOC_Option::get( 'add_request_uri' ) ? $_SERVER['REQUEST_URI'] : '') . '#' . $id;
 | ||
| 
 | ||
| 		} elseif ( 1 === $page ) {
 | ||
| 			// Fix for wrong links on TOC on Wordpress category page
 | ||
| 			if(is_category() || is_tax() || is_tag() || (function_exists('is_product_category') && is_product_category())){
 | ||
| 				return  '#' . $id;
 | ||
| 			}
 | ||
| 			return trailingslashit( $anch_url ) . '#' . $id;
 | ||
| 
 | ||
| 		}
 | ||
| 
 | ||
| 		return trailingslashit( $anch_url ) . $page . '/#' . $id;
 | ||
| 	}
 | ||
| } |