/** * This function extracts headings from the html formatted $content. It will pull out * only the required headings as specified in the options. For all qualifying headings, * this function populates the $find and $replace arrays (both passed by reference) * with what to search and replace with. * * Returns a HTML formatted string of list items for each qualifying heading. This * is everything between and NOT including <ul> and </ul> * * @access private * @since 1.0 * @static * * @param array $find * @param array $replace * @param string $content * * @return bool|string */ public static function extract_headings(&$find, &$replace, $content = '') { global $wp_query; $post = $wp_query->post; $matches = array(); $anchor = ''; $items = ''; $headings = get_post_meta($post->ID, '_ez-toc-heading-levels', TRUE); $exclude = get_post_meta($post->ID, '_ez-toc-exclude', TRUE); $altText = get_post_meta($post->ID, '_ez-toc-alttext', TRUE); if (!is_array($headings)) { $headings = array(); } if (empty($headings)) { $headings = ezTOC_Option::get('heading_levels', array()); } if (empty($exclude)) { $exclude = ezTOC_Option::get('exclude'); } // reset the internal collision collection as the_content may have been triggered elsewhere // eg by themes or other plugins that need to read in content such as metadata fields in // the head html tag, or to provide descriptions to twitter/facebook self::$collision_collector = array(); if (is_array($find) && is_array($replace) && $content) { // get all headings // the html spec allows for a maximum of 6 heading depths if (preg_match_all('/(<h([1-6]{1})[^>]*>).*<\\/h\\2>/msuU', $content, $matches, PREG_SET_ORDER)) { // remove undesired headings (if any) as defined by heading_levels if (count($headings) != 6) { $new_matches = array(); for ($i = 0; $i < count($matches); $i++) { if (in_array($matches[$i][2], $headings)) { $new_matches[] = $matches[$i]; } } $matches = $new_matches; } // remove specific headings if provided via the 'exclude' property 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(); for ($i = 0; $i < count($matches); $i++) { $found = FALSE; for ($j = 0; $j < $excluded_count; $j++) { if (@preg_match('/^' . $excluded_headings[$j] . '$/imU', strip_tags($matches[$i][0]))) { $found = TRUE; break; } } if (!$found) { $new_matches[] = $matches[$i]; } } if (count($matches) != count($new_matches)) { $matches = $new_matches; } } } // remove empty headings $new_matches = array(); for ($i = 0; $i < count($matches); $i++) { if (trim(strip_tags($matches[$i][0])) != FALSE) { $new_matches[] = $matches[$i]; } } if (count($matches) != count($new_matches)) { $matches = $new_matches; } $toc = $matches; // Replace headers with toc alt text. if ($altText) { $alt_headings = array(); $split_headings = preg_split('/\\r\\n|[\\r\\n]/', $altText); $split_headings_count = count($split_headings); if ($split_headings) { for ($k = 0; $k < $split_headings_count; $k++) { $explode_headings = explode('|', $split_headings[$k]); if (0 < strlen($explode_headings[0]) && 0 < strlen($explode_headings[1])) { $alt_headings[$explode_headings[0]] = $explode_headings[1]; } } } if (0 < count($alt_headings)) { for ($i = 0; $i < count($toc); $i++) { foreach ($alt_headings as $original_heading => $alt_heading) { $original_heading = preg_quote($original_heading); // escape some regular expression characters // others: http://www.php.net/manual/en/regexp.reference.meta.php $original_heading = str_replace(array('\\*'), array('.*'), trim($original_heading)); if (@preg_match('/^' . $original_heading . '$/imU', strip_tags($toc[$i][0]))) { //$matches[ $i ][0] = str_replace( $original_heading, $alt_heading, $matches[ $i ][0] ); $toc[$i][0] = $alt_heading; } } } } } // check minimum number of headings if (count($matches) >= ezTOC_Option::get('start')) { for ($i = 0; $i < count($matches); $i++) { // get anchor and add to find and replace arrays $anchor = isset($toc[$i][0]) ? self::url_anchor_target($toc[$i][0]) : self::url_anchor_target($matches[$i][0]); $find[] = $matches[$i][0]; $replace[] = str_replace(array($matches[$i][1], '</h' . $matches[$i][2] . '>'), array($matches[$i][1] . '<span class="ez-toc-section" id="' . $anchor . '">', '</span></h' . $matches[$i][2] . '>'), $matches[$i][0]); // assemble flat list if (!ezTOC_Option::get('show_hierarchy')) { $items .= '<li><a href="' . esc_url('#' . $anchor) . '">'; //$title = apply_filters( 'ez_toc_title', strip_tags( wp_kses_post( $toc[ $i ][0] ) ) ); $title = strip_tags(apply_filters('ez_toc_title', $toc[$i][0])); //if ( 'decimal' == ezTOC_Option::get( 'counter' ) ) { // // $items .= count( $replace ) . ' '; //} $items .= $title . '</a></li>'; } } // build a hierarchical toc? // we could have tested for $items but that var can be quite large in some cases if (ezTOC_Option::get('show_hierarchy')) { $items = self::build_hierarchy($toc); } } } } return $items; }
/** * Renders the widgets. * * @access private * @since 1.0 * * @param array $args * @param array $instance */ public function widget($args, $instance) { global $wp_query; $css_classes = ''; $find = $replace = array(); $post = get_post($wp_query->post->ID); $content = apply_filters('the_content', $post->post_content); /** * @var string $before_widget * @var string $after_widget * @var string $before_title * @var string $after_title */ extract($args); $title = apply_filters('widget_title', $instance['title'], $instance, $this->id_base); $items = ezTOC::extract_headings($find, $replace, $content); if (FALSE !== strpos($title, '%PAGE_TITLE%') || FALSE !== strpos($title, '%PAGE_NAME%')) { $title = str_replace('%PAGE_TITLE%', get_the_title(), $title); } if (ezTOC_Option::get('show_hierarchy')) { $css_classes = ' counter-hierarchy'; } else { $css_classes .= ' counter-flat'; } switch (ezTOC_Option::get('counter')) { case 'numeric': $css_classes .= ' counter-numeric'; break; case 'roman': $css_classes .= ' counter-roman'; break; case 'decimal': $css_classes .= ' counter-decimal'; break; } if ($instance['affix']) { $css_classes .= ' ez-toc-affix'; } // bullets? //if ( ezTOC_Option::get( 'bullet_spacing' ) ) { // // $css_classes = ' have_bullets'; // //} else { // // $css_classes = ' no_bullets'; //} $css_classes = trim($css_classes); // an empty class="" is invalid markup! if (!$css_classes) { $css_classes = ' '; } if ($items) { echo $before_widget; echo '<div class="ez-toc-widget-container ' . $css_classes . '">' . PHP_EOL; do_action('ez_toc_before_widget'); if (0 < strlen($title)) { ?> <?php echo $before_title; ?> <span class="ez-toc-title-container"> <style type="text/css"> #<?php echo $this->id; ?> .ez-toc-widget-container ul.ez-toc-list li.active::before { background-color: <?php echo esc_attr($instance['highlight_color']); ?> ; } </style> <span class="ez-toc-title"><?php echo $title; ?> </span> <span class="ez-toc-title-toggle"> <?php if (ezTOC_Option::get('visibility')) { echo '<a class="pull-right btn btn-xs btn-default ez-toc-toggle"><i class="glyphicon ez-toc-icon-toggle"></i></a>'; } ?> </span> </span> <?php echo $after_title; ?> <?php } echo '<ul class="ez-toc-list">' . PHP_EOL . $items . '</ul>' . PHP_EOL; do_action('ez_toc_after_widget'); echo '</div>' . PHP_EOL; echo $after_widget; // Enqueue the script. wp_enqueue_script('ez-toc-js'); } }