📁 File Manager Pro
v10.0.3 | PHP: 8.1.34
Server: LiteSpeed
2026-06-29 00:00:01
📂
/ (Root)
/
home
/
apkbfjox
/
joygame.com.pk
/
wp-content
/
plugins
/
kadence-blocks
/
includes
📍 /home/apkbfjox/joygame.com.pk/wp-content/plugins/kadence-blocks/includes
🔄 Refresh
✏️
Editing: class-kadence-blocks-table-of-contents.php
Writable
<?php /** * Setup the Table of Contents Block. * * This Block was largely based on the Core Table of Contents block. * * @since 1.9.0 * @package Kadence Blocks */ // Exit if accessed directly. if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Class to Build the Table of Contents. * * @category class */ class Kadence_Blocks_Table_Of_Contents { /** * Headers that needs ids. * * @var null */ public static $headings = array(); /** * Anchors that are used, prevent duplicates. * * @var null */ public static $anchors = array(); /** * Post content. * * @var string */ public static $output_content = ''; /** * Instance of this class * * @var null */ private static $instance = null; /** * headings that are used, prevent duplicates. * * @var null */ public static $the_headings = null; /** * The block names to ignore. * * @var null */ public static $ignore_list = null; /** * The block names to ignore. * * @var null */ public static $inner_ignore_list = null; /** * The heading classes to ignore. * * @var null */ public static $ignore_classes = null; /** * Instance Control */ public static function get_instance() { if ( is_null( self::$instance ) ) { self::$instance = new self(); } return self::$instance; } /** * Constructor */ public function __construct() { add_action( 'wp_footer', array( $this, 'headings_enqueue' ), 1 ); } /** * Helper function to remove the children of a node. * * @access private * * @param object $node The node to remove children from. */ public function table_of_contents_delete_node_children( $node ) { /* phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase */ // Disabled because of PHP DOMDoument and DOMXPath APIs using camelCase. // Whenever the 1st child node is removed, the 2nd one becomes the 1st. while ( isset( $node->firstChild ) ) { $this->table_of_contents_delete_node_children( $node->firstChild ); $node->removeChild( $node->firstChild ); } /* phpcs:enable */ } /** * Helper function to remove a node and all of its children. * * @access private * * @param object $node The node to remove along with its children. */ public function table_of_contents_delete_node_and_children( $node ) { /* phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase */ // Disabled because of PHP DOMDoument and DOMXPath APIs using camelCase. $this->table_of_contents_delete_node_children( $node ); $node->parentNode->removeChild( $node ); /* phpcs:enable */ } /** * Gets the content, anchor, level, and page of headings from a post. Returns * data from all headings in a paginated post if $current_page_only is false; * otherwise, returns only data from headings on the current page being * rendered. * * @access private * * @param WP_Post $post The post to extract headings from. * @param array $attributes the block attributes. * * @return array The list of headings. */ private function table_of_contents_get_headings( $post, $attributes = array() ) { global $multipage, $page, $pages; if ( $multipage ) { // Creates a list of heading lists, one list per page. $pages_of_headings = array_map( function( $page_content, $page_index ) use ( $page, $attributes ) { return $this->table_of_contents_get_headings_from_content( $page_content, $page_index + 1, $page, $attributes ); }, $pages, array_keys( $pages ) ); $current_page_only = true; if ( $current_page_only ) { // Return the headings from the current page. return $pages_of_headings[ $page - 1 ]; } else { // Concatenate the heading lists into a single array and return it. return array_merge( ...$pages_of_headings ); } } else { // Only one page, so return headings from entire post_content. if ( is_null( self::$the_headings ) ) { if ( isset( $attributes['enableDynamicSearch'] ) && $attributes['enableDynamicSearch'] ) { if ( isset( $attributes['enableTemplateSearch'] ) && $attributes['enableTemplateSearch'] && class_exists( 'Kadence_Woo_Block_Editor_Templates' ) && is_singular( 'product' ) ) { $kadence_woo_pro = Kadence_Woo_Block_Editor_Templates::get_instance(); $page_content = ''; if ( ! empty( $kadence_woo_pro::$single_override ) ) { $template_post = get_post( $kadence_woo_pro::$single_override ); $blocks = parse_blocks( $template_post->post_content ); self::$output_content = ''; foreach ( $blocks as $block ) { $this->recursively_parse_blocks( $block ); } if ( self::$output_content ) { $page_content .= do_shortcode( self::$output_content ); } } else { $blocks = parse_blocks( $post->post_content ); self::$output_content = ''; foreach ( $blocks as $block ) { $this->recursively_parse_blocks( $block ); } if ( self::$output_content ) { $page_content .= do_shortcode( self::$output_content ); } else { $page_content .= do_shortcode( $post->post_content ); } } } else if ( isset( $attributes['enableTemplateSearch'] ) && $attributes['enableTemplateSearch'] && class_exists( 'Kadence_Pro\Elements_Controller' ) && is_singular() ) { $kadence_pro = Kadence_Pro\Elements_Controller::get_instance(); $page_content = ''; if ( ! empty( $kadence_pro::$single_template ) ) { $blocks = parse_blocks( $post->post_content ); self::$output_content = ''; $the_post_content = ''; foreach ( $blocks as $block ) { $this->recursively_parse_blocks( $block ); } if ( self::$output_content ) { $the_post_content .= do_shortcode( self::$output_content ); } else { $the_post_content .= do_shortcode( $post->post_content ); } $template_post = get_post( $kadence_pro::$single_template ); $blocks = parse_blocks( $template_post->post_content ); self::$output_content = ''; foreach ( $blocks as $block ) { $this->recursively_parse_blocks( $block, $the_post_content ); } if ( self::$output_content ) { $page_content .= do_shortcode( self::$output_content ); } } else { $blocks = parse_blocks( $post->post_content ); self::$output_content = ''; foreach ( $blocks as $block ) { $this->recursively_parse_blocks( $block ); } if ( self::$output_content ) { $page_content .= do_shortcode( self::$output_content ); } else { $page_content .= do_shortcode( $post->post_content ); } } } else { $blocks = parse_blocks( $post->post_content ); self::$output_content = ''; foreach ( $blocks as $block ) { $this->recursively_parse_blocks( $block ); } if ( self::$output_content ) { $page_content = do_shortcode( self::$output_content ); } else { $page_content = do_shortcode( $post->post_content ); } } } else { $page_content = $post->post_content; } self::$the_headings = $this->table_of_contents_get_headings_from_content( $page_content, 1, 1, $attributes ); } $headings = self::$the_headings; if ( $attributes && isset( $attributes['allowedHeaders'] ) && isset( $attributes['allowedHeaders'][0] ) && is_array( $attributes['allowedHeaders'][0] ) ) { foreach ( array( 'h1' => 1, 'h2' => 2, 'h3' => 3, 'h4' => 4, 'h5' => 5, 'h6' => 6 ) as $tag => $level ) { foreach ( $headings as $headkey => $headvalue ) { // User is specifically excluding this heading. if( false === $headvalue['include'] ) { unset( $headings[$headkey] ); } // User is specifically including this heading, or, it's in the allowed headers list. if ( true === $headvalue['include'] || ( !isset( $attributes['allowedHeaders'][0][$tag] ) || $attributes['allowedHeaders'][0][$tag] ) ) { continue; } // Heading is at an unwanted level. if ( $headvalue['level'] === $level ) { unset( $headings[$headkey] ); } } } } return array_values( $headings ); } } /** * Get the ignore list of blocks. * * @access private */ private function get_ignore_list() { if ( is_null( self::$ignore_list ) ) { self::$ignore_list = apply_filters( 'kadence_toc_block_ignore_array', array( 'kadence/tableofcontents', 'kadence/tabs', 'kadence/modal', 'kadence/repeater', 'core/post-content' ) ); } return self::$ignore_list; } /** * Get the ignore list of blocks. * * @access private */ private function get_inner_block_ignore_list() { if ( is_null( self::$inner_ignore_list ) ) { self::$inner_ignore_list = apply_filters( 'kadence_toc_inner_block_ignore_array', array( 'kadence/pane' ) ); } return self::$inner_ignore_list; } /** * Get the ignore list of classes. * * @access private */ private function get_ignore_classes() { if ( is_null( self::$ignore_classes ) ) { self::$ignore_classes = apply_filters( 'kadence_toc_exclude_classes_array', array( 'kt-testimonial-title', 'image-overlay-title', 'image-overlay-subtitle', 'toc-ignore' ) ); } return self::$ignore_classes; } /** * Renders blocks to allow dynamic blocks that bring in headings to be accounted for. * * @access private * * @param string $block The page content content. */ private function recursively_parse_blocks( $block, $the_post_content = '' ) { if ( ! in_array( $block['blockName'], $this->get_ignore_list() ) ) { if ( isset( $block['innerBlocks'] ) && ! empty( $block['innerBlocks'] ) && is_array( $block['innerBlocks'] ) ) { // Can't link to inner blocks so ignore everything past. if ( in_array( $block['blockName'], $this->get_inner_block_ignore_list() ) ) { self::$output_content .= $block['innerHTML']; } else { foreach( $block['innerBlocks'] as $inner_block ) { $this->recursively_parse_blocks( $inner_block ); } } } else { self::$output_content .= render_block( $block ); } } else if ( $block['blockName'] === 'core/post-content' ) { self::$output_content .= $the_post_content; } else if ( $block['blockName'] === 'kadence/repeater' ) { //repeater needs to render all together to properly render dynamic data with context. self::$output_content .= render_block( $block ); } } /** * Registers scripts and styles. */ public function headings_enqueue() { // If in the backend, bail out. if ( is_admin() ) { return; } wp_localize_script( 'kadence-blocks-tableofcontents', 'kadence_blocks_toc', array( 'headings' => wp_json_encode( self::$headings ), 'expandText' => esc_attr__( 'Expand Table of Contents', 'kadence-blocks' ), 'collapseText' => esc_attr__( 'Collapse Table of Contents', 'kadence-blocks' ), ) ); } /** * Extracts heading content, anchor, and level from the given post content. * * @access private * * @param string $content The heading content. * * @return string The sanitized heading content for anchor link. */ private function convert_text_to_anchor( $content ) { $content = remove_accents( $content ); $content = strtolower( $content ); $content = preg_replace( "/[^A-Za-z0-9 ]/", '', $content ); $content = str_replace( ' ', '-', $content ); return $content; } /** * Process content through Kadence Blocks conditional logic (logged in/out, RCP membership, etc.). * * @access private * * @param string $content The post content to process. * * @return string The processed content with conditionally hidden blocks removed. */ private function process_content_through_kadence_conditional_logic( $content ) { $blocks = parse_blocks( $content ); if ( empty( $blocks ) ) { return $content; } // Get the Kadence Blocks Frontend instance to access conditionally_render_block $frontend_instance = Kadence_Blocks_Frontend::get_instance(); if ( ! $frontend_instance ) { return $content; } // Process each block through conditional logic $processed_blocks = array(); foreach ( $blocks as $block ) { if ( empty( $block['blockName'] ) ) { $processed_blocks[] = $block; continue; } $wp_block = new stdClass(); $wp_block->context = array(); // Check if this block should be conditionally hidden using Kadence Blocks logic // We pass a placeholder content to test if the block would be hidden $block_content = $frontend_instance->conditionally_render_block( 'PLACEHOLDER_CONTENT', $block, $wp_block ); if ( $block_content !== '' ) { $processed_blocks[] = $block; } } // Re-render the content from the processed blocks $processed_content = ''; foreach ( $processed_blocks as $block ) { $processed_content .= serialize_block( $block ); } return $processed_content; } /** * Process content through Kadence Blocks Pro conditional hiding logic. * * @access private * * @param string $content The post content to process. * * @return string The processed content with conditionally hidden blocks removed. */ private function process_content_through_conditional_hiding( $content ) { // Check if Kadence Blocks Pro is available if ( ! class_exists( 'Kadence_Blocks_Pro_Dynamic_Content' ) ) { return $content; } $dynamic_content = Kadence_Blocks_Pro_Dynamic_Content::get_instance(); if ( ! $dynamic_content ) { return $content; } // Parse the content into blocks and process each block through conditional display logic $blocks = $dynamic_content->kadence_parse_blocks( $content ); if ( empty( $blocks ) ) { return $content; } $processed_blocks = array(); foreach ( $blocks as $block ) { $wp_block = new stdClass(); $wp_block->context = array(); $should_hide = $dynamic_content->should_conditionally_hide_block( '', $block, $wp_block, null, null ); if ( ! $should_hide ) { $processed_blocks[] = $block; } } // Convert the filtered block array back to serialized content format $processed_content = ''; foreach ( $processed_blocks as $block ) { $processed_content .= serialize_block( $block ); } return $processed_content; } /** * Extracts heading content, anchor, and level from the given post content. * * @access private * * @param string $content The post content to extract headings from. * @param int $headings_page The page of the post where the headings are * located. * @param int $current_page The page of the post currently being rendered. * @param array $attributes The block attributes. * * @return array The list of headings. */ private function table_of_contents_get_headings_from_content( $content, $headings_page = 1, $current_page = 1, $attributes = array() ) { // Process content through Kadence Blocks conditional logic first $content = $this->process_content_through_kadence_conditional_logic( $content ); // Then process content through Kadence Blocks Pro conditional hiding before parsing HTML $content = $this->process_content_through_conditional_hiding( $content ); /* phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase */ // Disabled because of PHP DOMDoument and DOMXPath APIs using camelCase. // Create a document to load the post content into. //$doc = new DOMDocument(); $wp_charset = get_bloginfo( 'charset' ); $doc = new DOMDocument( '1.0', $wp_charset ); // Enable user error handling for the HTML parsing. HTML5 elements aren't // supported (as of PHP 7.4) and There's no way to guarantee that the markup // is valid anyway, so we're just going to ignore all errors in parsing. // Nested heading elements will still be parsed. // The lack of HTML5 support is a libxml2 issue: // https://bugzilla.gnome.org/show_bug.cgi?id=761534. libxml_use_internal_errors( true ); // Parse the post content into an HTML document. $content = mb_encode_numericentity( $content, [0x80, 0x10FFFF, 0, ~0], 'UTF-8' ); // loadHTML expects ISO-8859-1, so we need to convert the post content to // that format. We use htmlentities to encode Unicode characters not // supported by ISO-8859-1 as HTML entities. However, this function also // converts all special characters like < or > to HTML entities, so we use // htmlspecialchars_decode to decode them. $doc->loadHTML( htmlspecialchars_decode( mb_convert_encoding( htmlentities( '<!DOCTYPE html><html><head><title>:D</title><body>' . htmlentities( $content ) . '</body></html>', ENT_COMPAT, 'UTF-8', false ), 'ISO-8859-1', 'UTF-8' ), ENT_COMPAT ) ); // We're done parsing, so we can disable user error handling. This also // clears any existing errors, which helps avoid a memory leak. libxml_use_internal_errors( false ); $document_el = $doc->documentElement; // IE11 treats template elements like divs, so to avoid extracting heading // elements from them, we first have to remove the template elements and // their children. // We can't use foreach directly on the $templates DOMNodeList because it's a // dynamic list, and removing nodes confuses the foreach iterator. So // instead, we create a static array of the nodes we want to remove and then // iterate over that. $templates = iterator_to_array( $document_el->getElementsByTagName( 'template' ) ); foreach ( $templates as $template ) { $this->table_of_contents_delete_node_and_children( $template ); } $xpath = new DOMXPath( $doc ); $query = '//*[self::h1 or self::h2 or self::h3 or self::h4 or self::h5 or self::h6]'; // Remove this since the headings are cached, instead strip out the ones we don't want from the cached response. /* if ( $attributes && isset( $attributes['allowedHeaders'] ) && isset( $attributes['allowedHeaders'][0] ) && is_array( $attributes['allowedHeaders'][0] ) ) { $query = '//*['; $arr = array(); if ( isset( $attributes['allowedHeaders'][0]['h1'] ) && $attributes['allowedHeaders'][0]['h1'] ) { $arr[] = 'self::h1'; } if ( isset( $attributes['allowedHeaders'][0]['h2'] ) && $attributes['allowedHeaders'][0]['h2'] ) { $arr[] = 'self::h2'; } if ( isset( $attributes['allowedHeaders'][0]['h3'] ) && $attributes['allowedHeaders'][0]['h3'] ) { $arr[] = 'self::h3'; } if ( isset( $attributes['allowedHeaders'][0]['h4'] ) && $attributes['allowedHeaders'][0]['h4'] ) { $arr[] = 'self::h4'; } if ( isset( $attributes['allowedHeaders'][0]['h5'] ) && $attributes['allowedHeaders'][0]['h5'] ) { $arr[] = 'self::h5'; } if ( isset( $attributes['allowedHeaders'][0]['h6'] ) && $attributes['allowedHeaders'][0]['h6'] ) { $arr[] = 'self::h6'; } $query .= implode( ' or ', $arr ); $query .= ']'; } */ // Get all heading elements in the post content. $temp_headings = iterator_to_array( $xpath->query( $query ) ); $headings = array(); foreach ( $temp_headings as $temp_heading ) { if ( isset( $temp_heading->attributes ) ) { $heading_classes_string = $temp_heading->attributes->getNamedItem( 'class' ); if ( null !== $heading_classes_string ) { $heading_classes = explode( ' ', trim( $heading_classes_string->nodeValue ) ); $exclude_classes = $this->get_ignore_classes(); $exclude = false; foreach ( $exclude_classes as $exclude_class ) { if ( in_array( $exclude_class, $heading_classes ) ) { $exclude = true; break; } } if ( $exclude ) { continue; } } } $headings[] = $temp_heading; } return array_map( function ( $heading ) use ( $headings_page, $current_page ) { $anchor_string = false; $anchor = ''; if ( isset( $heading->attributes ) ) { $id_attribute = $heading->attributes->getNamedItem( 'id' ); if ( null !== $id_attribute ) { // The id attribute may contain many ids, so just use the first. $first_id = explode( ' ', trim( $id_attribute->nodeValue ) )[0]; if ( $headings_page === $current_page ) { $anchor = '#' . $first_id; } elseif ( 1 !== $headings_page && 1 === $current_page ) { $anchor = './' . $headings_page . '/#' . $first_id; } elseif ( 1 === $headings_page && 1 !== $current_page ) { $anchor = '../#' . $first_id; } else { $anchor = '../' . $headings_page . '/#' . $first_id; } } else { $anchor_string = $this->convert_text_to_anchor( $heading->textContent ); if ( ! $anchor_string ) { $anchor_string = uniqid( 'hd-' ); } if ( in_array( $anchor_string, self::$anchors ) ) { $anchor_string = $anchor_string . uniqid( '-hd-' ); } self::$anchors[] = $anchor_string; $anchor = '#' . $anchor_string; } } else { $anchor_string = $this->convert_text_to_anchor( $heading->textContent ); if ( ! $anchor_string ) { $anchor_string = uniqid( 'hd-' ); } if ( in_array( $anchor_string, self::$anchors ) ) { $anchor_string = $anchor_string . uniqid( '-hd-' ); } self::$anchors[] = $anchor_string; $anchor = '#' . $anchor_string; } switch ( $heading->nodeName ) { case 'h1': $level = 1; break; case 'h2': $level = 2; break; case 'h3': $level = 3; break; case 'h4': $level = 4; break; case 'h5': $level = 5; break; case 'h6': $level = 6; break; } if ( $heading->getAttribute( 'data-alt-title' ) ) { $heading->textContent = $heading->getAttribute( 'data-alt-title' ); } $headingWrapper = [ 'element' => $heading, 'include' => '' ]; if( $heading->getAttribute( 'data-toc-include' ) === 'true' ){ $headingWrapper['include'] = true; } else if ( $heading->getAttribute( 'data-toc-include' ) === 'false' ) { $headingWrapper['include'] = false; } if ( $anchor_string ) { $add = true; foreach ( self::$headings as $v ) { if ( $v['anchor'] == $anchor_string ) { $add = false; } } if ( $add ) { self::$headings[] = array( 'anchor' => $anchor_string, 'content' => $this->convert_smart_quotes( $heading->textContent ), 'level' => $level, 'include' => $headingWrapper['include'], 'page' => $headings_page, ); } } return array( 'anchor' => $anchor, 'content' => $this->convert_smart_quotes( $heading->textContent ), 'level' => $level, 'include' => $headingWrapper['include'], 'page' => $headings_page, ); }, $headings ); /* phpcs:enable */ } /** * converts special quotes characters in string to normal. * * @param string $string The string to convert. * @return string The string converted. */ public function convert_smart_quotes( $string ) { $search = array("×", "’", "‘", "”", "“", "–", "—", "…"); $replace = array("x", "'", "'", '"', '"', '-', '-', '...'); return str_replace( $search, $replace, $string ); } /** * Sets the current post for usage in template blocks. * * @return WP_Post|null The post if any, or null otherwise. */ public function get_post_from_context() { // TODO: Without this temporary fix, an infinite loop can occur where // posts with post content blocks render themselves recursively. if ( is_admin() || defined( 'REST_REQUEST' ) ) { return null; } // Prevent an issue with the terms and conditions page in the woocommerce checkout. if ( class_exists( 'woocommerce' ) && is_checkout() ) { return ''; } // Bug when called in via a hook on the front page. // if ( ! in_the_loop() && ! is_front_page() ) { // rewind_posts(); // the_post(); // } return get_post(); } /** * Converts a flat list of heading parameters to a hierarchical nested list * based on each header's immediate parent's level. * * @access private * * @param array $heading_list Flat list of heading parameters to nest. * @param int $index The current list index. * * @return array A hierarchical nested list of heading parameters. */ private function table_of_contents_linear_to_nested_heading_list( $heading_list, $index = 0 ) { $nested_heading_list = array(); foreach ( $heading_list as $key => $heading ) { if ( ! isset( $heading['content'] ) ) { break; } // Make sure we are only working with the same level as the first // iteration in our set. if ( $heading['level'] === $heading_list[0]['level'] ) { // Check that the next iteration will return a value. // If it does and the next level is greater than the current level, // the next iteration becomes a child of the current interation. if ( isset( $heading_list[ $key + 1 ] ) && $heading_list[ $key + 1 ]['level'] > $heading['level'] ) { // We need to calculate the last index before the next iteration // that has the same level (siblings). We then use this last index // to slice the array for use in recursion. This prevents duplicate // nodes. $heading_list_length = count( $heading_list ); $end_of_slice = $heading_list_length; for ( $i = $key + 1; $i < $heading_list_length; $i++ ) { if ( $heading_list[ $i ]['level'] === $heading['level'] ) { $end_of_slice = $i; break; } } // Found a child node: Push a new node onto the return array with // children. $nested_heading_list[] = array( 'heading' => $heading, 'index' => $index + $key, 'children' => $this->table_of_contents_linear_to_nested_heading_list( array_slice( $heading_list, $key + 1, $end_of_slice - ( $key + 1 ) ), $index + $key + 1 ), ); } else { // No child node: Push a new node onto the return array. $nested_heading_list[] = array( 'heading' => $heading, 'index' => $index + $key, 'children' => null, ); } } } return $nested_heading_list; } /** * Renders the heading list of the kadence table of contents block on server. * * @access private * * @param array $nested_heading_list Nested list of heading data. * @param bool $wrap if the list should be wrapped. * @param string $list_tag the tag to be used if wrapped. * * @return string The heading list rendered as HTML. */ private function table_of_contents_render_list( $nested_heading_list, $wrap = false, $list_tag = 'ul' ) { $entry_class = 'kb-table-of-contents__entry'; $child_nodes = array_map( function ( $child_node ) use ( $entry_class, $list_tag ) { $anchor = $child_node['heading']['anchor']; $content = $child_node['heading']['content']; if ( isset( $anchor ) && '' !== $anchor ) { $entry = sprintf( '<a class="%1$s" href="%2$s">%3$s</a>', $entry_class, esc_attr( $anchor ), esc_html( $content ) ); } else { $entry = sprintf( '<span class="%1$s">%2$s</span>', $entry_class, esc_html( $content ) ); } return sprintf( '<li>%1$s%2$s</li>', $entry, $child_node['children'] ? $this->table_of_contents_render_list( $child_node['children'], true, $list_tag ) : null ); }, $nested_heading_list ); return $wrap ? '<' . $list_tag . ' class="kb-table-of-contents-list-sub">' . implode( $child_nodes ) . '</' . $list_tag . '>' : implode( $child_nodes ); } /** * Render Table of Contents Block * * @param array $attributes the blocks attribtues. */ public function render_table_of_content( $attributes, $content ) { $the_post = $this->get_post_from_context(); // CSS class string. $class = 'wp-block-kadence-tableofcontents kb-table-of-content-nav kb-table-of-content-id' . $attributes['uniqueID']; // Add custom CSS classes to class string. if ( isset( $attributes['className'] ) ) { $class .= ' ' . $attributes['className']; } if ( ! $the_post ) { return ''; } $attributes = apply_filters( 'kadence_blocks_toc_block_build_only_attributes', $attributes ); $headings = $this->table_of_contents_get_headings( $the_post, $attributes ); $list_tag = ( isset( $attributes['listStyle'] ) && 'numbered' === $attributes['listStyle'] ? 'ol' : 'ul' ); // If there are no headings or the only heading is empty. if ( count( $headings ) === 0 || '' === $headings[0]['content'] ) { return ''; } if ( ! isset( $attributes['enableTitle'] ) || isset( $attributes['enableTitle'] ) && $attributes['enableTitle'] ) { if ( isset( $attributes['enableToggle'] ) && $attributes['enableToggle'] ) { $enable_toggle = true; $start_closed = ( isset( $attributes['startClosed'] ) && $attributes['startClosed'] ? true : false ); $title_toggle = ( isset( $attributes['enableTitleToggle'] ) && $attributes['enableTitleToggle'] ? true : false ); } else { $enable_toggle = false; $start_closed = false; $title_toggle = false; } } else { $enable_toggle = false; $start_closed = false; $title_toggle = false; } $enable_scroll = false; $scroll_offset = '40'; if ( isset( $attributes['enableSmoothScroll'] ) && true === $attributes['enableSmoothScroll'] ) { $enable_scroll = true; $scroll_offset = ( isset( $attributes['smoothScrollOffset'] ) && is_numeric( $attributes['smoothScrollOffset'] ) ? $attributes['smoothScrollOffset'] : '40' ); $class .= ' kb-toc-smooth-scroll'; } $output = '<nav class="' . esc_attr( $class ) . ( $enable_toggle ? ' kb-collapsible-toc kb-toc-toggle-' . ( $start_closed ? 'hidden' : 'active' ) : '' ) . '" role="navigation" aria-label="' . esc_attr__( 'Table of Contents', 'kadence-blocks' ) . '"' . ( $enable_scroll ? ' data-scroll-offset="' . esc_attr( $scroll_offset ) . '"' : '' ) . ( isset( $attributes['enableScrollSpy'] ) && true === $attributes['enableScrollSpy'] ? ' data-scroll-spy="true"' : '' ) . '>'; $output .= '<div class="kb-table-of-content-wrap">'; if ( ! isset( $attributes['enableTitle'] ) || isset( $attributes['enableTitle'] ) && $attributes['enableTitle'] ) { $output .= '<div class="kb-table-of-contents-title-wrap kb-toggle-icon-style-' . ( $enable_toggle && isset( $attributes['toggleIcon'] ) && $attributes['toggleIcon'] ? $attributes['toggleIcon'] : 'arrow' ) . '">'; if ( $title_toggle ) { $output .= '<button class="kb-table-of-contents-title-btn kb-table-of-contents-toggle" aria-expanded="' . esc_attr( $start_closed ? 'false' : 'true' ) . '" aria-label="' . ( $start_closed ? esc_attr__( 'Expand Table of Contents', 'kadence-blocks' ) : esc_attr__( 'Collapse Table of Contents', 'kadence-blocks' ) ) . '">'; } $output .= '<span class="kb-table-of-contents-title">'; $output .= ( isset( $attributes['title'] ) && $attributes['title'] !== 'Table of Contents' ? $attributes['title'] : __( 'Table of Contents', 'kadence-blocks' ) ); $output .= '</span>'; if ( $enable_toggle ) { if ( $title_toggle ) { $output .= '<span class="kb-table-of-contents-icon-trigger"></span>'; } else { $output .= '<button class="kb-table-of-contents-icon-trigger kb-table-of-contents-toggle" aria-expanded="' . esc_attr( $start_closed ? 'false' : 'true' ) . '" aria-label="' . ( $start_closed ? esc_attr__( 'Expand Table of Contents', 'kadence-blocks' ) : esc_attr__( 'Collapse Table of Contents', 'kadence-blocks' ) ) . '"></button>'; } } if ( $title_toggle ) { $output .= '</button>'; } $output .= '</div>'; } $output .= '<' . $list_tag . ' class="kb-table-of-content-list kb-table-of-content-list-columns-' . esc_attr( isset( $attributes['columns'] ) && ! empty( $attributes['columns'] ) ? $attributes['columns'] : '1' ) . ' kb-table-of-content-list-style-' . esc_attr( isset( $attributes['listStyle'] ) && ! empty( $attributes['listStyle'] ) ? $attributes['listStyle'] : 'disc' ) . ' kb-table-of-content-link-style-' . esc_attr( isset( $attributes['linkStyle'] ) && ! empty( $attributes['linkStyle'] ) ? $attributes['linkStyle'] : 'underline' ) . '">'; $output .= $this->table_of_contents_render_list( $this->table_of_contents_linear_to_nested_heading_list( $headings, false ), false, $list_tag ); $output .= '</' . $list_tag . '>'; $output .= '</div>'; $output .= '</nav>'; $content = $output . $content; if ( ! wp_style_is( 'kadence-blocks-table-of-contents', 'enqueued' ) ) { wp_enqueue_style( 'kadence-blocks-table-of-contents' ); } if ( isset( $attributes['uniqueID'] ) ) { $unique_id = $attributes['uniqueID']; $style_id = 'kb-tableofcontents' . esc_attr( $unique_id ); if ( ! wp_style_is( $style_id, 'enqueued' ) && apply_filters( 'kadence_blocks_render_inline_css', true, 'tableofcontent', $unique_id ) ) { if ( kadence_blocks_is_not_amp() ) { if ( isset( $attributes['enableScrollSpy'] ) && $attributes['enableScrollSpy'] ) { wp_enqueue_script( 'kadence-blocks-gumshoe' ); //need to laod this script with the gumshoe dependency if scrollspy is enabled wp_enqueue_script( 'kadence-blocks-table-of-contents', KADENCE_BLOCKS_URL . 'includes/assets/js/kb-table-of-contents.min.js', array('kadence-blocks-gumshoe') , KADENCE_BLOCKS_VERSION, true ); } else { wp_enqueue_script( 'kadence-blocks-table-of-contents' ); } } if ( ! doing_filter( 'the_content' ) ) { if ( ! wp_style_is( 'kadence-blocks-table-of-contents', 'done' ) ) { wp_print_styles( 'kadence-blocks-table-of-contents' ); } } else { if ( ! wp_style_is( 'kadence-blocks-table-of-contents', 'done' ) ) { ob_start(); wp_print_styles( 'kadence-blocks-table-of-contents' ); $content = ob_get_clean() . $content; } } } } return $content; } /* * Reset the instance properties for unit tests */ private static function reset_instance() { self::$headings = array(); self::$anchors = array(); self::$the_headings = null; self::$output_content = ''; } } Kadence_Blocks_Table_Of_Contents::get_instance();
💾 Save Changes
❌ Cancel