/**
  * 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');
            }
        }