Easy Table of Contents - Version 2.0-beta2

Version Description

Download this release

Release Info

Developer shazahm1@hotmail.com
Plugin Icon 128x128 Easy Table of Contents
Version 2.0-beta2
Comparing to
See all releases

Code changes from version 1.7 to 2.0-beta2

easy-table-of-contents.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Easy Table of Contents
4
  * Plugin URI: http://connections-pro.com/
5
  * Description: Adds a user friendly and fully automatic way to create and display a table of contents generated from the page content.
6
- * Version: 1.7
7
  * Author: Steven A. Zahm
8
  * Author URI: http://connections-pro.com/
9
  * Text Domain: easy-table-of-contents
@@ -26,7 +26,7 @@
26
  * @package Easy Table of Contents
27
  * @category Plugin
28
  * @author Steven A. Zahm
29
- * @version 1.7
30
  */
31
 
32
  // Exit if accessed directly
@@ -45,7 +45,7 @@ if ( ! class_exists( 'ezTOC' ) ) {
45
  * @since 1.0
46
  * @var string
47
  */
48
- const VERSION = '1.7';
49
 
50
  /**
51
  * Stores the instance of this class.
@@ -132,7 +132,9 @@ if ( ! class_exists( 'ezTOC' ) ) {
132
  require_once( EZ_TOC_PATH . 'includes/class.admin.php' );
133
  }
134
 
 
135
  require_once( EZ_TOC_PATH . 'includes/class.widget-toc.php' );
 
136
  }
137
 
138
  /**
@@ -323,198 +325,6 @@ if ( ! class_exists( 'ezTOC' ) ) {
323
  }
324
  }
325
 
326
- /**
327
- * Returns a URL to be used as the destination anchor target.
328
- *
329
- * @access private
330
- * @since 1.0
331
- * @static
332
- *
333
- * @param string $title
334
- *
335
- * @return bool|string
336
- */
337
- private static function url_anchor_target( $title ) {
338
-
339
- $return = FALSE;
340
-
341
- if ( $title ) {
342
-
343
- // WP entity encodes the post content.
344
- $return = html_entity_decode( $title, ENT_QUOTES, get_option( 'blog_charset' ) );
345
-
346
- $return = trim( strip_tags( $return ) );
347
-
348
- // Convert accented characters to ASCII.
349
- $return = remove_accents( $return );
350
-
351
- // replace newlines with spaces (eg when headings are split over multiple lines)
352
- $return = str_replace( array( "\r", "\n", "\n\r", "\r\n" ), ' ', $return );
353
-
354
- // Remove `&` and ` ` NOTE: in order to strip "hidden" ` `,
355
- // title needs to be converted to HTML entities.
356
- // @link https://stackoverflow.com/a/21801444/5351316
357
- $return = htmlentities2( $return );
358
- $return = str_replace( array( '&', ' ' ), ' ', $return );
359
- $return = html_entity_decode( $return, ENT_QUOTES, get_option( 'blog_charset' ) );
360
-
361
- // remove non alphanumeric chars
362
- $return = preg_replace( '/[^a-zA-Z0-9 \-_]*/', '', $return );
363
-
364
- // convert spaces to _
365
- $return = preg_replace( '/\s+/', '_', $return );
366
-
367
- // remove trailing - and _
368
- $return = rtrim( $return, '-_' );
369
-
370
- // lowercase everything?
371
- if ( ezTOC_Option::get( 'lowercase' ) ) {
372
-
373
- $return = strtolower( $return );
374
- }
375
-
376
- // if blank, then prepend with the fragment prefix
377
- // blank anchors normally appear on sites that don't use the latin charset
378
- if ( ! $return ) {
379
-
380
- $return = ( ezTOC_Option::get( 'fragment_prefix' ) ) ? ezTOC_Option::get( 'fragment_prefix' ) : '_';
381
- }
382
-
383
- // hyphenate?
384
- if ( ezTOC_Option::get( 'hyphenate' ) ) {
385
-
386
- $return = str_replace( '_', '-', $return );
387
- $return = str_replace( '--', '-', $return );
388
- }
389
- }
390
-
391
- if ( array_key_exists( $return, self::$collision_collector ) ) {
392
-
393
- self::$collision_collector[ $return ]++;
394
- $return .= '-' . self::$collision_collector[ $return ];
395
-
396
- } else {
397
-
398
- self::$collision_collector[ $return ] = 1;
399
- }
400
-
401
- return apply_filters( 'ez_toc_url_anchor_target', $return, $title );
402
- }
403
-
404
- /**
405
- * Generates a nested unordered list for the table of contents.
406
- *
407
- * @access private
408
- * @since 1.0
409
- * @static
410
- *
411
- * @param array $matches
412
- * @param array $headings Array of headers to be considered for a TOC item.
413
- *
414
- * @return string
415
- */
416
- private static function build_hierarchy( &$matches, $headings ) {
417
-
418
- $current_depth = 100; // headings can't be larger than h6 but 100 as a default to be sure
419
- $html = '';
420
- $numbered_items = array();
421
- $numbered_items_min = NULL;
422
-
423
- // reset the internal collision collection
424
- self::$collision_collector = array();
425
-
426
- // find the minimum heading to establish our baseline
427
- for ( $i = 0; $i < count( $matches ); $i ++ ) {
428
- if ( $current_depth > $matches[ $i ][2] ) {
429
- $current_depth = (int) $matches[ $i ][2];
430
- }
431
- }
432
-
433
- $numbered_items[ $current_depth ] = 0;
434
- $numbered_items_min = $current_depth;
435
-
436
- for ( $i = 0; $i < count( $matches ); $i ++ ) {
437
-
438
- if ( $current_depth == (int) $matches[ $i ][2] ) {
439
-
440
- $html .= '<li>';
441
- }
442
-
443
- // start lists
444
- if ( $current_depth != (int) $matches[ $i ][2] ) {
445
-
446
- for ( $current_depth; $current_depth < (int) $matches[ $i ][2]; $current_depth++ ) {
447
-
448
- $numbered_items[ $current_depth + 1 ] = 0;
449
- $html .= '<ul><li>';
450
- }
451
- }
452
-
453
- // list item
454
- if ( in_array( $matches[ $i ][2], $headings ) ) {
455
-
456
- //$title = apply_filters( 'ez_toc_title', strip_tags( wp_kses_post( $matches[ $i ][0] ) ) );
457
- $title = strip_tags( apply_filters( 'ez_toc_title', $matches[ $i ][0] ), apply_filters( 'ez_toc_title_allowable_tags', '' ) );
458
-
459
- //$html .= '<a href="#' . self::url_anchor_target( $title ) . '">';
460
- $html .= sprintf(
461
- '<a href="%1$s" title="%2$s">',
462
- esc_url( '#' . self::url_anchor_target( $matches[ $i ][0] ) ),
463
- esc_attr( strip_tags( $title ) )
464
- );
465
-
466
- //if ( 'decimal' == ezTOC_Option::get( 'counter' ) ) {
467
- //
468
- // // attach leading numbers when lower in hierarchy
469
- // $html .= '<span class="ez-toc-number ez-toc-depth_' . ( $current_depth - $numbered_items_min + 1 ) . '">';
470
- //
471
- // for ( $j = $numbered_items_min; $j < $current_depth; $j ++ ) {
472
- //
473
- // $number = ( $numbered_items[ $j ] ) ? $numbered_items[ $j ] : 0;
474
- // $html .= $number . '.';
475
- // }
476
- //
477
- // $html .= ( $numbered_items[ $current_depth ] + 1 ) . '</span> ';
478
- // $numbered_items[ $current_depth ] ++;
479
- //}
480
-
481
- $html .= $title . '</a>';
482
- }
483
-
484
- // end lists
485
- if ( $i != count( $matches ) - 1 ) {
486
-
487
- if ( $current_depth > (int) $matches[ $i + 1 ][2] ) {
488
-
489
- for ( $current_depth; $current_depth > (int) $matches[ $i + 1 ][2]; $current_depth-- ) {
490
-
491
- $html .= '</li></ul>';
492
- $numbered_items[ $current_depth ] = 0;
493
- }
494
- }
495
-
496
- if ( $current_depth == (int) @$matches[ $i + 1 ][2] ) {
497
-
498
- $html .= '</li>';
499
- }
500
-
501
- } else {
502
-
503
- // this is the last item, make sure we close off all tags
504
- for ( $current_depth; $current_depth >= $numbered_items_min; $current_depth -- ) {
505
-
506
- $html .= '</li>';
507
-
508
- if ( $current_depth != $numbered_items_min ) {
509
- $html .= '</ul>';
510
- }
511
- }
512
- }
513
- }
514
-
515
- return $html;
516
- }
517
-
518
  /**
519
  * Returns a string with all items from the $find array replaced with their matching
520
  * items in the $replace array. This does a one to one replacement (rather than globally).
@@ -572,256 +382,6 @@ if ( ! class_exists( 'ezTOC' ) ) {
572
  return $string;
573
  }
574
 
575
- /**
576
- * This function extracts headings from the html formatted $content. It will pull out
577
- * only the required headings as specified in the options. For all qualifying headings,
578
- * this function populates the $find and $replace arrays (both passed by reference)
579
- * with what to search and replace with.
580
- *
581
- * Returns a HTML formatted string of list items for each qualifying heading. This
582
- * is everything between and NOT including <ul> and </ul>
583
- *
584
- * @access private
585
- * @since 1.0
586
- * @static
587
- *
588
- * @param array $find
589
- * @param array $replace
590
- * @param WP_Post $post
591
- *
592
- * @return bool|string
593
- */
594
- public static function extract_headings( &$find, &$replace, $post ) {
595
-
596
- $matches = array();
597
- $anchor = '';
598
- $items = '';
599
-
600
- $headings = get_post_meta( $post->ID, '_ez-toc-heading-levels', TRUE );
601
- $exclude = get_post_meta( $post->ID, '_ez-toc-exclude', TRUE );
602
- $altText = get_post_meta( $post->ID, '_ez-toc-alttext', TRUE );
603
-
604
- if ( ! is_array( $headings ) ) {
605
-
606
- $headings = array();
607
- }
608
-
609
- if ( empty( $headings ) ) {
610
-
611
- $headings = ezTOC_Option::get( 'heading_levels', array() );
612
- }
613
-
614
- if ( empty( $exclude ) ) {
615
-
616
- $exclude = ezTOC_Option::get( 'exclude' );
617
- }
618
-
619
- // reset the internal collision collection as the_content may have been triggered elsewhere
620
- // eg by themes or other plugins that need to read in content such as metadata fields in
621
- // the head html tag, or to provide descriptions to twitter/facebook
622
- self::$collision_collector = array();
623
-
624
- $content = apply_filters( 'ez_toc_extract_headings_content', $post->post_content );
625
-
626
- if ( is_array( $find ) && is_array( $replace ) && $content ) {
627
-
628
- // get all headings
629
- // the html spec allows for a maximum of 6 heading depths
630
- if ( preg_match_all( '/(<h([1-6]{1})[^>]*>).*<\/h\2>/msuU', $content, $matches, PREG_SET_ORDER ) ) {
631
-
632
- // remove undesired headings (if any) as defined by heading_levels
633
- if ( count( $headings ) != 6 ) {
634
-
635
- $new_matches = array();
636
-
637
- for ( $i = 0; $i < count( $matches ); $i ++ ) {
638
-
639
- if ( in_array( $matches[ $i ][2], $headings ) ) {
640
-
641
- $new_matches[] = $matches[ $i ];
642
- }
643
- }
644
- $matches = $new_matches;
645
- }
646
-
647
- // remove specific headings if provided via the 'exclude' property
648
- if ( $exclude ) {
649
-
650
- $excluded_headings = explode( '|', $exclude );
651
- $excluded_count = count( $excluded_headings );
652
-
653
- if ( $excluded_count > 0 ) {
654
-
655
- for ( $j = 0; $j < $excluded_count; $j++ ) {
656
-
657
- $excluded_headings[ $j ] = preg_quote( $excluded_headings[ $j ] );
658
-
659
- // escape some regular expression characters
660
- // others: http://www.php.net/manual/en/regexp.reference.meta.php
661
- $excluded_headings[ $j ] = str_replace(
662
- array( '\*' ),
663
- array( '.*' ),
664
- trim( $excluded_headings[ $j ] )
665
- );
666
- }
667
-
668
- $new_matches = array();
669
-
670
- for ( $i = 0; $i < count( $matches ); $i++ ) {
671
-
672
- $found = FALSE;
673
-
674
- for ( $j = 0; $j < $excluded_count; $j++ ) {
675
-
676
- // Since WP manipulates the post content it is required that the excluded header and
677
- // the actual header be manipulated similarly so a match can be made.
678
- $pattern = html_entity_decode(
679
- wptexturize( $excluded_headings[ $j ] ),
680
- ENT_NOQUOTES,
681
- get_option( 'blog_charset' )
682
- );
683
-
684
- $against = html_entity_decode(
685
- wptexturize( strip_tags( $matches[ $i ][0] ) ),
686
- ENT_NOQUOTES,
687
- get_option( 'blog_charset' )
688
- );
689
-
690
- if ( @preg_match( '/^' . $pattern . '$/imU', $against ) ) {
691
-
692
- $found = TRUE;
693
- break;
694
- }
695
- }
696
-
697
- if ( ! $found ) {
698
-
699
- $new_matches[] = $matches[ $i ];
700
- }
701
- }
702
-
703
- if ( count( $matches ) != count( $new_matches ) ) {
704
-
705
- $matches = $new_matches;
706
- }
707
- }
708
- }
709
-
710
- // remove empty headings
711
- $new_matches = array();
712
-
713
- for ( $i = 0; $i < count( $matches ); $i ++ ) {
714
-
715
- if ( trim( strip_tags( $matches[ $i ][0] ) ) != FALSE ) {
716
-
717
- $new_matches[] = $matches[ $i ];
718
- }
719
- }
720
-
721
- if ( count( $matches ) != count( $new_matches ) ) {
722
-
723
- $matches = $new_matches;
724
- }
725
-
726
- $toc = $matches;
727
-
728
- // Replace headers with toc alt text.
729
- if ( $altText ) {
730
-
731
- $alt_headings = array();
732
- $split_headings = preg_split( '/\r\n|[\r\n]/', $altText );
733
- $split_headings_count = count( $split_headings );
734
-
735
- if ( $split_headings ) {
736
-
737
- for ( $k = 0; $k < $split_headings_count; $k++ ) {
738
-
739
- $explode_headings = explode( '|', $split_headings[ $k ] );
740
-
741
- if ( 0 < strlen( $explode_headings[0] ) && 0 < strlen( $explode_headings[1] ) ) {
742
-
743
- $alt_headings[ $explode_headings[0] ] = $explode_headings[1];
744
- }
745
- }
746
-
747
- }
748
-
749
- if ( 0 < count( $alt_headings ) ) {
750
-
751
- for ( $i = 0; $i < count( $toc ); $i++ ) {
752
-
753
- foreach ( $alt_headings as $original_heading => $alt_heading ) {
754
-
755
- $original_heading = preg_quote( $original_heading );
756
-
757
- // escape some regular expression characters
758
- // others: http://www.php.net/manual/en/regexp.reference.meta.php
759
- $original_heading = str_replace(
760
- array( '\*' ),
761
- array( '.*' ),
762
- trim( $original_heading )
763
- );
764
-
765
- if ( @preg_match( '/^' . $original_heading . '$/imU', strip_tags( $toc[ $i ][0] ) ) ) {
766
-
767
- //$matches[ $i ][0] = str_replace( $original_heading, $alt_heading, $matches[ $i ][0] );
768
- $toc[ $i ][0] = $alt_heading;
769
- }
770
- }
771
- }
772
- }
773
- }
774
-
775
- // check minimum number of headings
776
- if ( count( $matches ) >= ezTOC_Option::get( 'start' ) ) {
777
-
778
- for ( $i = 0; $i < count( $matches ); $i++ ) {
779
-
780
- // get anchor and add to find and replace arrays
781
- $anchor = isset( $toc[ $i ][0] ) ? self::url_anchor_target( $toc[ $i ][0] ) : self::url_anchor_target( $matches[ $i ][0] );
782
- $find[] = $matches[ $i ][0];
783
- $replace[] = str_replace(
784
- array(
785
- $matches[ $i ][1], // start of heading
786
- '</h' . $matches[ $i ][2] . '>' // end of heading
787
- ),
788
- array(
789
- $matches[ $i ][1] . '<span class="ez-toc-section" id="' . $anchor . '">',
790
- '</span></h' . $matches[ $i ][2] . '>'
791
- ),
792
- $matches[ $i ][0]
793
- );
794
-
795
- // assemble flat list
796
- if ( ! ezTOC_Option::get( 'show_hierarchy' ) ) {
797
-
798
- $items .= '<li><a href="' . esc_url( '#' . $anchor ) . '">';
799
- //$title = apply_filters( 'ez_toc_title', strip_tags( wp_kses_post( $toc[ $i ][0] ) ) );
800
- $title = strip_tags( apply_filters( 'ez_toc_title', $matches[ $i ][0] ), apply_filters( 'ez_toc_title_allowable_tags', '' ) );
801
-
802
- //if ( 'decimal' == ezTOC_Option::get( 'counter' ) ) {
803
- //
804
- // $items .= count( $replace ) . ' ';
805
- //}
806
-
807
- $items .= $title . '</a></li>';
808
- }
809
- }
810
-
811
- // build a hierarchical toc?
812
- // we could have tested for $items but that var can be quite large in some cases
813
- if ( ezTOC_Option::get( 'show_hierarchy' ) ) {
814
-
815
- $items = self::build_hierarchy( $toc, $headings );
816
- }
817
-
818
- }
819
- }
820
- }
821
-
822
- return $items;
823
- }
824
-
825
  /**
826
  * Array search deep.
827
  *
@@ -850,6 +410,8 @@ if ( ! class_exists( 'ezTOC' ) ) {
850
  /**
851
  * Returns true if the table of contents is eligible to be printed, false otherwise.
852
  *
 
 
853
  * @access public
854
  * @since 1.0
855
  * @static
@@ -858,15 +420,16 @@ if ( ! class_exists( 'ezTOC' ) ) {
858
  */
859
  public static function is_eligible() {
860
 
861
- global $wp_query;
862
 
863
- $post = $wp_query->post;
864
-
865
- if ( empty( $post ) ) {
866
  return FALSE;
867
  }
868
 
869
- if ( has_shortcode( $post->post_content, 'toc' ) || has_shortcode( $post->post_content, 'ez-toc' ) ) {
 
 
 
870
  return TRUE;
871
  }
872
 
@@ -883,15 +446,6 @@ if ( ! class_exists( 'ezTOC' ) ) {
883
 
884
  if ( ezTOC_Option::get( 'restrict_path' ) ) {
885
 
886
- //if ( strpos( $_SERVER['REQUEST_URI'], ezTOC_Option::get( 'restrict_path' ) ) === 0 ) {
887
- //
888
- // return TRUE;
889
- //
890
- //} else {
891
- //
892
- // return FALSE;
893
- //}
894
-
895
  /**
896
  * @link https://wordpress.org/support/topic/restrict-path-logic-does-not-work-correctly?
897
  */
@@ -920,7 +474,6 @@ if ( ! class_exists( 'ezTOC' ) ) {
920
  }
921
 
922
  return FALSE;
923
- //return TRUE;
924
  }
925
 
926
  } else {
@@ -929,160 +482,6 @@ if ( ! class_exists( 'ezTOC' ) ) {
929
  }
930
  }
931
 
932
- /**
933
- * Build the table of contents.
934
- *
935
- * @access private
936
- * @since 1.3
937
- * @static
938
- *
939
- * @param WP_Post $post The page/post content.
940
- *
941
- * @return array
942
- */
943
- public static function build( $post ) {
944
-
945
- $css_classes = '';
946
-
947
- $html = '';
948
- $find = array();
949
- $replace = array();
950
- $items = self::extract_headings( $find, $replace, $post );
951
-
952
- if ( $items ) {
953
-
954
- // wrapping css classes
955
- switch ( ezTOC_Option::get( 'wrapping' ) ) {
956
-
957
- case 'left':
958
- $css_classes .= ' ez-toc-wrap-left';
959
- break;
960
-
961
- case 'right':
962
- $css_classes .= ' ez-toc-wrap-right';
963
- break;
964
-
965
- case 'none':
966
- default:
967
- // do nothing
968
- }
969
-
970
- if ( ezTOC_Option::get( 'show_hierarchy' ) ) {
971
-
972
- $css_classes .= ' counter-hierarchy';
973
-
974
- } else {
975
-
976
- $css_classes .= ' counter-flat';
977
- }
978
-
979
- switch ( ezTOC_Option::get( 'counter' ) ) {
980
-
981
- case 'numeric':
982
- $css_classes .= ' counter-numeric';
983
- break;
984
-
985
- case 'roman':
986
- $css_classes .= ' counter-roman';
987
- break;
988
-
989
- case 'decimal':
990
- $css_classes .= ' counter-decimal';
991
- break;
992
- }
993
-
994
- // colour themes
995
- switch ( ezTOC_Option::get( 'theme' ) ) {
996
-
997
- case 'light-blue':
998
- $css_classes .= ' ez-toc-light-blue';
999
- break;
1000
-
1001
- case 'white':
1002
- $css_classes .= ' ez-toc-white';
1003
- break;
1004
-
1005
- case 'black':
1006
- $css_classes .= ' ez-toc-black';
1007
- break;
1008
-
1009
- case 'transparent':
1010
- $css_classes .= ' ez-toc-transparent';
1011
- break;
1012
-
1013
- case 'grey':
1014
- $css_classes .= ' ez-toc-grey';
1015
- break;
1016
-
1017
- default:
1018
- // do nothing
1019
- }
1020
-
1021
- if ( ezTOC_Option::get( 'css_container_class' ) ) {
1022
-
1023
- $css_classes .= ' ' . ezTOC_Option::get( 'css_container_class' );
1024
- }
1025
-
1026
- $css_classes = trim( $css_classes );
1027
-
1028
- // an empty class="" is invalid markup!
1029
- if ( ! $css_classes ) {
1030
-
1031
- $css_classes = ' ';
1032
- }
1033
-
1034
- // add container, toc title and list items
1035
- $html .= '<div id="ez-toc-container" class="' . $css_classes . '">' . PHP_EOL;
1036
-
1037
- if ( ezTOC_Option::get( 'show_heading_text' ) ) {
1038
-
1039
- $toc_title = ezTOC_Option::get( 'heading_text' );
1040
-
1041
- if ( strpos( $toc_title, '%PAGE_TITLE%' ) !== FALSE ) {
1042
-
1043
- $toc_title = str_replace( '%PAGE_TITLE%', get_the_title(), $toc_title );
1044
- }
1045
-
1046
- if ( strpos( $toc_title, '%PAGE_NAME%' ) !== FALSE ) {
1047
-
1048
- $toc_title = str_replace( '%PAGE_NAME%', get_the_title(), $toc_title );
1049
- }
1050
-
1051
- $html .= '<div class="ez-toc-title-container">' . PHP_EOL;
1052
-
1053
- $html .= '<p class="ez-toc-title">' . esc_html( htmlentities( $toc_title, ENT_COMPAT, 'UTF-8' ) ). '</p>' . PHP_EOL;
1054
-
1055
- $html .= '<span class="ez-toc-title-toggle">';
1056
-
1057
- if ( ezTOC_Option::get( 'visibility' ) ) {
1058
-
1059
- $html .= '<a class="ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle"><i class="ez-toc-glyphicon ez-toc-icon-toggle"></i></a>';
1060
- }
1061
-
1062
- $html .= '</span>';
1063
-
1064
- $html .= '</div>' . PHP_EOL;
1065
- }
1066
-
1067
- ob_start();
1068
- do_action( 'ez_toc_before' );
1069
- $html .= ob_get_clean();
1070
-
1071
- $html .= '<nav><ul class="ez-toc-list">' . $items . '</ul></nav>';
1072
-
1073
- ob_start();
1074
- do_action( 'ez_toc_after' );
1075
- $html .= ob_get_clean();
1076
-
1077
- $html .= '</div>' . PHP_EOL;
1078
-
1079
- // Enqueue the script.
1080
- wp_enqueue_script( 'ez-toc-js' );
1081
- }
1082
-
1083
- return array( 'find' => $find, 'replace' => $replace, 'content' => $html );
1084
- }
1085
-
1086
  /**
1087
  * Callback for the registered shortcode `[ez-toc]`
1088
  *
@@ -1105,8 +504,9 @@ if ( ! class_exists( 'ezTOC' ) ) {
1105
 
1106
  if ( $run ) {
1107
 
1108
- $args = self::build( get_post( get_the_ID() ) );
1109
- $out = $args['content'];
 
1110
  $run = FALSE;
1111
  }
1112
 
@@ -1142,26 +542,14 @@ if ( ! class_exists( 'ezTOC' ) ) {
1142
  return $content;
1143
  }
1144
 
1145
- /*
1146
- * get_post() does not return post_content filtered via `the_content` filter, which is good otherwise this
1147
- * might cause an infinite loop.
1148
- *
1149
- * Since the ezTOC `the_content` filter is added at priority 100, it should run last in most situations
1150
- * and already be filtered by other plugins/themes which ezTOC should take into account when building the
1151
- * TOC. So, take the post content past via `the_content` filter callback and replace the post_content with
1152
- * it before building the TOC.
1153
- */
1154
- $post = get_post( get_the_ID() );
1155
- $post->post_content = $content;
1156
-
1157
- // build toc
1158
- $args = self::build( $post );
1159
- $find = $args['find'];
1160
- $replace = $args['replace'];
1161
- $html = $args['content'];
1162
 
1163
  // bail if no headings found
1164
- if ( empty( $find ) ) {
1165
 
1166
  return $content;
1167
  }
@@ -1219,50 +607,3 @@ if ( ! class_exists( 'ezTOC' ) ) {
1219
  // Start Easy Table of Contents.
1220
  add_action( 'plugins_loaded', 'ezTOC' );
1221
  }
1222
-
1223
-
1224
- /**
1225
- * Returns a HTML formatted string of the table of contents without the surrounding UL or OL
1226
- * tags to enable the theme editor to supply their own ID and/or classes to the outer list.
1227
- *
1228
- * There are three optional parameters you can feed this function with:
1229
- *
1230
- * - $content is the entire content with headings. If blank, will default to the current $post
1231
- *
1232
- * - $link is the URL to prefix the anchor with. If provided a string, will use it as the prefix.
1233
- * If set to true then will try to obtain the permalink from the $post object.
1234
- *
1235
- * - $apply_eligibility bool, defaults to false. When set to true, will apply the check to
1236
- * see if bit of content has the prerequisites needed for a TOC, eg minimum number of headings
1237
- * enabled post type, etc.
1238
- */
1239
- //function toc_get_index( $content = '', $prefix_url = '', $apply_eligibility = FALSE ) {
1240
- //
1241
- // global $wp_query, $tic;
1242
- //
1243
- // $return = '';
1244
- // $find = $replace = array();
1245
- // $proceed = TRUE;
1246
- //
1247
- // if ( ! $content ) {
1248
- // $post = get_post( $wp_query->post->ID );
1249
- // $content = wptexturize( $post->post_content );
1250
- // }
1251
- //
1252
- // if ( $apply_eligibility ) {
1253
- // if ( ! $tic->is_eligible() ) {
1254
- // $proceed = FALSE;
1255
- // }
1256
- // } else {
1257
- // $tic->set_option( array( 'start' => 0 ) );
1258
- // }
1259
- //
1260
- // if ( $proceed ) {
1261
- // $return = $tic->extract_headings( $find, $replace, $content );
1262
- // if ( $prefix_url ) {
1263
- // $return = str_replace( 'href="#', 'href="' . $prefix_url . '#', $return );
1264
- // }
1265
- // }
1266
- //
1267
- // return $return;
1268
- //}
3
  * Plugin Name: Easy Table of Contents
4
  * Plugin URI: http://connections-pro.com/
5
  * Description: Adds a user friendly and fully automatic way to create and display a table of contents generated from the page content.
6
+ * Version: 2.0-beta2
7
  * Author: Steven A. Zahm
8
  * Author URI: http://connections-pro.com/
9
  * Text Domain: easy-table-of-contents
26
  * @package Easy Table of Contents
27
  * @category Plugin
28
  * @author Steven A. Zahm
29
+ * @version 2.0-beta2
30
  */
31
 
32
  // Exit if accessed directly
45
  * @since 1.0
46
  * @var string
47
  */
48
+ const VERSION = '2.0-beta2';
49
 
50
  /**
51
  * Stores the instance of this class.
132
  require_once( EZ_TOC_PATH . 'includes/class.admin.php' );
133
  }
134
 
135
+ require_once( EZ_TOC_PATH . 'includes/class.post.php' );
136
  require_once( EZ_TOC_PATH . 'includes/class.widget-toc.php' );
137
+ require_once( EZ_TOC_PATH . 'includes/inc.functions.php' );
138
  }
139
 
140
  /**
325
  }
326
  }
327
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  /**
329
  * Returns a string with all items from the $find array replaced with their matching
330
  * items in the $replace array. This does a one to one replacement (rather than globally).
382
  return $string;
383
  }
384
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
  /**
386
  * Array search deep.
387
  *
410
  /**
411
  * Returns true if the table of contents is eligible to be printed, false otherwise.
412
  *
413
+ * NOTE: Must bve use only within the loop.
414
+ *
415
  * @access public
416
  * @since 1.0
417
  * @static
420
  */
421
  public static function is_eligible() {
422
 
423
+ $post = get_post();
424
 
425
+ if ( empty( $post ) || ! $post instanceof WP_Post ) {
 
 
426
  return FALSE;
427
  }
428
 
429
+ $content = get_the_content();
430
+
431
+ if ( has_shortcode( $content, apply_filters( 'ez_toc_shortcode', 'toc' ) ) ||
432
+ has_shortcode( $content, 'ez-toc' ) ) {
433
  return TRUE;
434
  }
435
 
446
 
447
  if ( ezTOC_Option::get( 'restrict_path' ) ) {
448
 
 
 
 
 
 
 
 
 
 
449
  /**
450
  * @link https://wordpress.org/support/topic/restrict-path-logic-does-not-work-correctly?
451
  */
474
  }
475
 
476
  return FALSE;
 
477
  }
478
 
479
  } else {
482
  }
483
  }
484
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
485
  /**
486
  * Callback for the registered shortcode `[ez-toc]`
487
  *
504
 
505
  if ( $run ) {
506
 
507
+ $post = ezTOC_Post::get( get_the_ID() )->process();
508
+ $out = $post->getTOC();
509
+
510
  $run = FALSE;
511
  }
512
 
542
  return $content;
543
  }
544
 
545
+ $post = ezTOC_Post::get( get_the_ID() )->applyContentFilter()->process();
546
+
547
+ $find = $post->getHeadings();
548
+ $replace = $post->getHeadingsWithAnchors();
549
+ $html = $post->getTOC();
 
 
 
 
 
 
 
 
 
 
 
 
550
 
551
  // bail if no headings found
552
+ if ( ! $post->hasTOCItems() ) {
553
 
554
  return $content;
555
  }
607
  // Start Easy Table of Contents.
608
  add_action( 'plugins_loaded', 'ezTOC' );
609
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/class.post.php ADDED
@@ -0,0 +1,1077 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ezTOC_Post {
4
+
5
+ /**
6
+ * @since 2.0
7
+ * @var int
8
+ */
9
+ private $queriedObjectID;
10
+
11
+ /**
12
+ * @since 2.0
13
+ * @var WP_Post
14
+ */
15
+ private $post;
16
+
17
+ /**
18
+ * @since 2.0
19
+ * @var false|string
20
+ */
21
+ private $permalink;
22
+
23
+ /**
24
+ * The post content broken into pages by user inserting `<!--nextpage-->` into the post content.
25
+ * @see ezTOC_Post::extractPages()
26
+ * @since 2.0
27
+ * @var array
28
+ */
29
+ private $pages = array();
30
+
31
+ /**
32
+ * The user defined heading levels to be included in the TOC.
33
+ * @see ezTOC_Post::getHeadingLevels()
34
+ * @since 2.0
35
+ * @var array
36
+ */
37
+ private $headingLevels = array();
38
+
39
+ /**
40
+ * The user defined strings to be used in the TOC in place of the post content headings.
41
+ * @see ezTOC_Post::getAlternateHeadings()
42
+ * @since 2.0
43
+ * @var array
44
+ */
45
+ private $alternateHeadings = array();
46
+
47
+ /**
48
+ * Keeps a track of used anchors for collision detecting.
49
+ * @see ezTOC_Post::generateHeadingIDFromTitle()
50
+ * @since 2.0
51
+ * @var array
52
+ */
53
+ private $collision_collector = array();
54
+
55
+ /**
56
+ * @var bool
57
+ */
58
+ private $hasTOCItems = FALSE;
59
+
60
+ public function __construct( WP_Post $post ) {
61
+
62
+ $this->post = $post;
63
+ $this->permalink = get_permalink( $post );
64
+ $this->queriedObjectID = get_queried_object_id();
65
+ }
66
+
67
+ /**
68
+ * @access public
69
+ * @since 2.0
70
+ *
71
+ * @param $id
72
+ *
73
+ * @return ezTOC_Post|null
74
+ */
75
+ public static function get( $id ) {
76
+
77
+ $post = get_post( $id );
78
+
79
+ if ( ! $post instanceof WP_Post ) {
80
+
81
+ return NULL;
82
+ }
83
+
84
+ return new static( $post );
85
+ }
86
+
87
+ /**
88
+ * Process post content for headings.
89
+ *
90
+ * This must be run after object init or after @see ezTOC_Post::applyContentFilter().
91
+ *
92
+ * @access public
93
+ * @since 2.0
94
+ *
95
+ * @return static
96
+ */
97
+ public function process() {
98
+
99
+ $this->processPages();
100
+
101
+ return $this;
102
+ }
103
+
104
+ /**
105
+ * Apply `the_content` filter to the post content.
106
+ *
107
+ * The `the_content` filter will only be applied once, even if this method is called multiple times.
108
+ *
109
+ * @access public
110
+ * @since 2.0
111
+ *
112
+ * @return static
113
+ */
114
+ public function applyContentFilter() {
115
+
116
+ static $run = TRUE;
117
+
118
+ if ( $run && $this->post instanceof WP_Post ) {
119
+
120
+ /*
121
+ * Ensure the ezTOC content filter is not applied when running `the_content` filter.
122
+ */
123
+ remove_filter( 'the_content', array( 'ezTOC', 'the_content' ), 100 );
124
+ $this->post->post_content = apply_filters( 'the_content', $this->post->post_content );
125
+ add_filter( 'the_content', array( 'ezTOC', 'the_content' ), 100 );
126
+
127
+ $run = FALSE;
128
+ }
129
+
130
+ return $this;
131
+ }
132
+
133
+ /**
134
+ * This is a work around for theme's and plugins
135
+ * which break the WordPress global $wp_query var by unsetting it
136
+ * or overwriting it which breaks the method call
137
+ * that `get_query_var()` uses to return the query variable.
138
+ *
139
+ * @access protected
140
+ * @since 2.0
141
+ *
142
+ * @return int
143
+ */
144
+ protected function getCurrentPage() {
145
+
146
+ global $wp_query;
147
+
148
+ // Check to see if the global `$wp_query` var is an instance of WP_Query and that the get() method is callable.
149
+ // If it is then when can simply use the get_query_var() function.
150
+ if ( $wp_query instanceof WP_Query && is_callable( array( $wp_query, 'get' ) ) ) {
151
+
152
+ $page = get_query_var( 'page', 1 );
153
+
154
+ return 1 > $page ? 1 : $page;
155
+
156
+ // 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.
157
+ } elseif ( isset( $GLOBALS['wp_query']->query_vars[ 'page' ] ) ) {
158
+
159
+ return $GLOBALS['wp_query']->query_vars[ 'page' ];
160
+
161
+ // We should not reach this, but if we do, lets check the original parsed query vars in $GLOBALS['wp_the_query']->query_vars.
162
+ } elseif ( isset( $GLOBALS['wp_the_query']->query_vars[ 'page' ] ) ) {
163
+
164
+ return $GLOBALS['wp_the_query']->query_vars[ 'page' ];
165
+
166
+ // Ok, if all else fails, check the $_REQUEST super global.
167
+ } elseif ( isset( $_REQUEST[ 'page' ] ) ) {
168
+
169
+ return $_REQUEST[ 'page' ];
170
+ }
171
+
172
+ // Finally, return the $default if it was supplied.
173
+ return 1;
174
+ }
175
+
176
+ /**
177
+ * Get the number of page the post has.
178
+ *
179
+ * @access protected
180
+ * @since 2.0
181
+ *
182
+ * @return int
183
+ */
184
+ protected function getNumberOfPages() {
185
+
186
+ return count( $this->pages );
187
+ }
188
+
189
+ /**
190
+ * Whether or not the post has multiple pages.
191
+ *
192
+ * @access protected
193
+ * @since 2.0
194
+ *
195
+ * @return bool
196
+ */
197
+ protected function isMultipage() {
198
+
199
+ return 1 < $this->getNumberOfPages();
200
+ }
201
+
202
+ /**
203
+ * Parse the post content and headings.
204
+ *
205
+ * @access private
206
+ * @since 2.0
207
+ */
208
+ private function processPages() {
209
+
210
+ $split = preg_split( '/<!--nextpage-->/msuU', $this->post->post_content );
211
+ $pages = array();
212
+
213
+ if ( is_array( $split ) ) {
214
+
215
+ $page = 1;
216
+
217
+ foreach ( $split as $content ) {
218
+
219
+ $pages[ $page ] = array(
220
+ 'headings' => $this->extractHeadings( $content ),
221
+ 'content' => $content,
222
+ );
223
+
224
+ $page++;
225
+ }
226
+
227
+ }
228
+
229
+ $this->pages = $pages;
230
+ }
231
+
232
+ /**
233
+ * Get the post's parse content and headings.
234
+ *
235
+ * @access public
236
+ * @since 2.0
237
+ *
238
+ * @return array
239
+ */
240
+ public function getPages() {
241
+
242
+ return $this->pages;
243
+ }
244
+
245
+ /**
246
+ * Extract the posts content for headings.
247
+ *
248
+ * @access private
249
+ * @since 2.0
250
+ *
251
+ * @param string $content
252
+ *
253
+ * @return array
254
+ */
255
+ private function extractHeadings( $content ) {
256
+
257
+ $matches = array();
258
+
259
+ // reset the internal collision collection as the_content may have been triggered elsewhere
260
+ // eg by themes or other plugins that need to read in content such as metadata fields in
261
+ // the head html tag, or to provide descriptions to twitter/facebook
262
+ /** @todo does this need to be used??? */
263
+ //self::$collision_collector = array();
264
+
265
+ $content = apply_filters( 'ez_toc_extract_headings_content', $content );
266
+
267
+ // get all headings
268
+ // the html spec allows for a maximum of 6 heading depths
269
+ if ( preg_match_all( '/(<h([1-6]{1})[^>]*>)(.*)<\/h\2>/msuU', $content, $matches, PREG_SET_ORDER ) ) {
270
+
271
+ $minimum = absint( ezTOC_Option::get( 'start' ) );
272
+
273
+ $this->removeHeadings( $matches );
274
+ $this->excludeHeadings( $matches );
275
+ $this->removeEmptyHeadings( $matches );
276
+
277
+ if ( count( $matches ) >= $minimum ) {
278
+
279
+ $this->alternateHeadings( $matches );
280
+ $this->headingIDs( $matches );
281
+ $this->hasTOCItems = TRUE;
282
+ }
283
+
284
+ }
285
+
286
+ return $matches;
287
+ }
288
+
289
+ /**
290
+ * Get the heading levels to be included in the TOC.
291
+ *
292
+ * @access private
293
+ * @since 2.0
294
+ *
295
+ * @return array
296
+ */
297
+ private function getHeadingLevels() {
298
+
299
+ $levels = get_post_meta( $this->post->ID, '_ez-toc-heading-levels', TRUE );
300
+
301
+ if ( ! is_array( $levels ) ) {
302
+
303
+ $levels = array();
304
+ }
305
+
306
+ if ( empty( $levels ) ) {
307
+
308
+ $levels = ezTOC_Option::get( 'heading_levels', array() );
309
+ }
310
+
311
+ $this->headingLevels = $levels;
312
+
313
+ return $this->headingLevels;
314
+ }
315
+
316
+ /**
317
+ * Remove the heading levels as defined by user settings from the TOC heading matches.
318
+ *
319
+ * @see ezTOC_Post::extractHeadings()
320
+ *
321
+ * @access private
322
+ * @since 2.0
323
+ *
324
+ * @param array $matches The heading from the post content extracted with preg_match_all().
325
+ *
326
+ * @return array
327
+ */
328
+ private function removeHeadings( &$matches ) {
329
+
330
+ $levels = $this->getHeadingLevels();
331
+
332
+ if ( count( $levels ) != 6 ) {
333
+
334
+ $new_matches = array();
335
+ $count = count( $matches );
336
+
337
+ for ( $i = 0; $i < $count; $i++ ) {
338
+
339
+ if ( in_array( $matches[ $i ][2], $levels ) ) {
340
+
341
+ $new_matches[] = $matches[ $i ];
342
+ }
343
+ }
344
+
345
+ $matches = $new_matches;
346
+ }
347
+
348
+ return $matches;
349
+ }
350
+
351
+ /**
352
+ * Exclude the heading, by title, as defined by the user settings from the TOC matches.
353
+ *
354
+ * @see ezTOC_Post::extractHeadings()
355
+ *
356
+ * @access private
357
+ * @since 2.0
358
+ *
359
+ * @param array $matches The headings from the post content extracted with preg_match_all().
360
+ *
361
+ * @return array
362
+ */
363
+ private function excludeHeadings( &$matches ) {
364
+
365
+ $exclude = get_post_meta( $this->post->ID, '_ez-toc-exclude', TRUE );
366
+
367
+ if ( empty( $exclude ) ) {
368
+
369
+ $exclude = ezTOC_Option::get( 'exclude' );
370
+ }
371
+
372
+ if ( $exclude ) {
373
+
374
+ $excluded_headings = explode( '|', $exclude );
375
+ $excluded_count = count( $excluded_headings );
376
+
377
+ if ( $excluded_count > 0 ) {
378
+
379
+ for ( $j = 0; $j < $excluded_count; $j++ ) {
380
+
381
+ $excluded_headings[ $j ] = preg_quote( $excluded_headings[ $j ] );
382
+
383
+ // escape some regular expression characters
384
+ // others: http://www.php.net/manual/en/regexp.reference.meta.php
385
+ $excluded_headings[ $j ] = str_replace(
386
+ array( '\*' ),
387
+ array( '.*' ),
388
+ trim( $excluded_headings[ $j ] )
389
+ );
390
+ }
391
+
392
+ $new_matches = array();
393
+ $count = count( $matches );
394
+
395
+ for ( $i = 0; $i < $count; $i++ ) {
396
+
397
+ $found = FALSE;
398
+
399
+ for ( $j = 0; $j < $excluded_count; $j++ ) {
400
+
401
+ // Since WP manipulates the post content it is required that the excluded header and
402
+ // the actual header be manipulated similarly so a match can be made.
403
+ $pattern = html_entity_decode(
404
+ wptexturize( $excluded_headings[ $j ] ),
405
+ ENT_NOQUOTES,
406
+ get_option( 'blog_charset' )
407
+ );
408
+
409
+ $against = html_entity_decode(
410
+ wptexturize( strip_tags( $matches[ $i ][0] ) ),
411
+ ENT_NOQUOTES,
412
+ get_option( 'blog_charset' )
413
+ );
414
+
415
+ if ( @preg_match( '/^' . $pattern . '$/imU', $against ) ) {
416
+
417
+ $found = TRUE;
418
+ break;
419
+ }
420
+ }
421
+
422
+ if ( ! $found ) {
423
+
424
+ $new_matches[] = $matches[ $i ];
425
+ }
426
+ }
427
+
428
+ if ( count( $matches ) != count( $new_matches ) ) {
429
+
430
+ $matches = $new_matches;
431
+ }
432
+ }
433
+ }
434
+
435
+ return $matches;
436
+ }
437
+
438
+ /**
439
+ * Return the alternate headings added by the user, saved in the post meta.
440
+ *
441
+ * The result is an associative array where the `key` is the original post heading
442
+ * and the `value` is the alternate heading.
443
+ *
444
+ * @access private
445
+ * @since 2.0
446
+ *
447
+ * @return array
448
+ */
449
+ private function getAlternateHeadings() {
450
+
451
+ $value = get_post_meta( $this->post->ID, '_ez-toc-alttext', TRUE );
452
+
453
+ if ( $value ) {
454
+
455
+ $alternates = array();
456
+ $headings = preg_split( '/\r\n|[\r\n]/', $value );
457
+ $count = count( $headings );
458
+
459
+ if ( $headings ) {
460
+
461
+ for ( $k = 0; $k < $count; $k++ ) {
462
+
463
+ $heading = explode( '|', $headings[ $k ] );
464
+
465
+ if ( 0 < strlen( $heading[0] ) && 0 < strlen( $heading[1] ) ) {
466
+
467
+ $alternates[ $heading[0] ] = $heading[1];
468
+ }
469
+ }
470
+
471
+ }
472
+
473
+ $this->alternateHeadings = $alternates;
474
+ }
475
+
476
+ return $this->alternateHeadings;
477
+ }
478
+
479
+ /**
480
+ * Add the alternate headings to the array.
481
+ *
482
+ * @see ezTOC_Post::extractHeadings()
483
+ *
484
+ * @access private
485
+ * @since 2.0
486
+ *
487
+ * @param array $matches The heading from the post content extracted with preg_match_all().
488
+ *
489
+ * @return array
490
+ */
491
+ private function alternateHeadings( &$matches ) {
492
+
493
+ $alt_headings = $this->getAlternateHeadings();
494
+ $count = count( $matches );
495
+
496
+ if ( 0 < count( $alt_headings ) ) {
497
+
498
+ for ( $i = 0; $i < $count; $i++ ) {
499
+
500
+ foreach ( $alt_headings as $original_heading => $alt_heading ) {
501
+
502
+ $original_heading = preg_quote( $original_heading );
503
+
504
+ // escape some regular expression characters
505
+ // others: http://www.php.net/manual/en/regexp.reference.meta.php
506
+ $original_heading = str_replace(
507
+ array( '\*' ),
508
+ array( '.*' ),
509
+ trim( $original_heading )
510
+ );
511
+
512
+ if ( @preg_match( '/^' . $original_heading . '$/imU', strip_tags( $matches[ $i ][0] ) ) ) {
513
+
514
+ $matches[ $i ]['alternate'] = $alt_heading;
515
+ }
516
+ }
517
+ }
518
+ }
519
+
520
+ return $matches;
521
+ }
522
+
523
+ /**
524
+ * Add the heading `id` to the array.
525
+ *
526
+ * @see ezTOC_Post::extractHeadings()
527
+ *
528
+ * @access private
529
+ * @since 2.0
530
+ *
531
+ * @param array $matches The heading from the post content extracted with preg_match_all().
532
+ *
533
+ * @return mixed
534
+ */
535
+ private function headingIDs( &$matches ) {
536
+
537
+ $count = count( $matches );
538
+
539
+ for ( $i = 0; $i < $count; $i++ ) {
540
+
541
+ $matches[ $i ]['id'] = $this->generateHeadingIDFromTitle( $matches[ $i ][0] );
542
+ }
543
+
544
+ return $matches;
545
+ }
546
+
547
+ /**
548
+ * Create unique heading ID from heading string.
549
+ *
550
+ * @access private
551
+ * @since 2.0
552
+ *
553
+ * @param string $heading
554
+ *
555
+ * @return bool|string
556
+ */
557
+ private function generateHeadingIDFromTitle( $heading ) {
558
+
559
+ $return = FALSE;
560
+
561
+ if ( $heading ) {
562
+
563
+ // WP entity encodes the post content.
564
+ $return = html_entity_decode( $heading, ENT_QUOTES, get_option( 'blog_charset' ) );
565
+
566
+ $return = trim( strip_tags( $return ) );
567
+
568
+ // Convert accented characters to ASCII.
569
+ $return = remove_accents( $return );
570
+
571
+ // replace newlines with spaces (eg when headings are split over multiple lines)
572
+ $return = str_replace( array( "\r", "\n", "\n\r", "\r\n" ), ' ', $return );
573
+
574
+ // Remove `&amp;` and `&nbsp;` NOTE: in order to strip "hidden" `&nbsp;`,
575
+ // title needs to be converted to HTML entities.
576
+ // @link https://stackoverflow.com/a/21801444/5351316
577
+ $return = htmlentities2( $return );
578
+ $return = str_replace( array( '&amp;', '&nbsp;' ), ' ', $return );
579
+ $return = html_entity_decode( $return, ENT_QUOTES, get_option( 'blog_charset' ) );
580
+
581
+ // remove non alphanumeric chars
582
+ $return = preg_replace( '/[^a-zA-Z0-9 \-_]*/', '', $return );
583
+
584
+ // convert spaces to _
585
+ $return = preg_replace( '/\s+/', '_', $return );
586
+
587
+ // remove trailing - and _
588
+ $return = rtrim( $return, '-_' );
589
+
590
+ // lowercase everything?
591
+ if ( ezTOC_Option::get( 'lowercase' ) ) {
592
+
593
+ $return = strtolower( $return );
594
+ }
595
+
596
+ // if blank, then prepend with the fragment prefix
597
+ // blank anchors normally appear on sites that don't use the latin charset
598
+ if ( ! $return ) {
599
+
600
+ $return = ( ezTOC_Option::get( 'fragment_prefix' ) ) ? ezTOC_Option::get( 'fragment_prefix' ) : '_';
601
+ }
602
+
603
+ // hyphenate?
604
+ if ( ezTOC_Option::get( 'hyphenate' ) ) {
605
+
606
+ $return = str_replace( '_', '-', $return );
607
+ $return = str_replace( '--', '-', $return );
608
+ }
609
+ }
610
+
611
+ if ( array_key_exists( $return, $this->collision_collector ) ) {
612
+
613
+ $this->collision_collector[ $return ]++;
614
+ $return .= '-' . $this->collision_collector[ $return ];
615
+
616
+ } else {
617
+
618
+ $this->collision_collector[ $return ] = 1;
619
+ }
620
+
621
+ return apply_filters( 'ez_toc_url_anchor_target', $return, $heading );
622
+ }
623
+
624
+ /**
625
+ * Remove any empty headings from the TOC.
626
+ *
627
+ * @access private
628
+ * @since 2.0
629
+ *
630
+ * @param array $matches The heading from the post content extracted with preg_match_all().
631
+ *
632
+ * @return array
633
+ */
634
+ private function removeEmptyHeadings( &$matches ) {
635
+
636
+ $new_matches = array();
637
+ $count = count( $matches );
638
+
639
+ for ( $i = 0; $i < $count; $i ++ ) {
640
+
641
+ if ( trim( strip_tags( $matches[ $i ][0] ) ) != FALSE ) {
642
+
643
+ $new_matches[] = $matches[ $i ];
644
+ }
645
+ }
646
+
647
+ if ( count( $matches ) != count( $new_matches ) ) {
648
+
649
+ $matches = $new_matches;
650
+ }
651
+
652
+ return $matches;
653
+ }
654
+
655
+ /**
656
+ * Whether or not the post has TOC items.
657
+ *
658
+ * @see ezTOC_Post::extractHeadings()
659
+ *
660
+ * @access public
661
+ * @since 2.0
662
+ *
663
+ * @return bool
664
+ */
665
+ public function hasTOCItems() {
666
+
667
+ return $this->hasTOCItems;
668
+ }
669
+
670
+ /**
671
+ * Get the headings of the current page of the post.
672
+ *
673
+ * @access public
674
+ * @since 2.0
675
+ *
676
+ * @param int|null $page
677
+ *
678
+ * @return array
679
+ */
680
+ public function getHeadings( $page = NULL ) {
681
+
682
+ $headings = array();
683
+
684
+ if ( is_null( $page ) ) {
685
+
686
+ $page = $this->getCurrentPage();
687
+ }
688
+
689
+ if ( isset( $this->pages[ $page ] ) ) {
690
+
691
+ $headings = wp_list_pluck( $this->pages[ $page ]['headings'], 0 );
692
+ }
693
+
694
+ return $headings;
695
+ }
696
+
697
+ /**
698
+ * Get the heading with in page anchors of the current page of the post.
699
+ *
700
+ * @access public
701
+ * @since 2.0
702
+ *
703
+ * @param int|null $page
704
+ *
705
+ * @return array
706
+ */
707
+ public function getHeadingsWithAnchors( $page = NULL ) {
708
+
709
+ $headings = array();
710
+
711
+ if ( is_null( $page ) ) {
712
+
713
+ $page = $this->getCurrentPage();
714
+ }
715
+
716
+ if ( isset( $this->pages[ $page ] ) ) {
717
+
718
+ $matches = $this->pages[ $page ]['headings'];
719
+ $count = count( $matches );
720
+
721
+ for ( $i = 0; $i < $count; $i++ ) {
722
+
723
+ $anchor = $matches[ $i ]['id'];
724
+ $headings[] = str_replace(
725
+ array(
726
+ $matches[ $i ][1], // start of heading
727
+ '</h' . $matches[ $i ][2] . '>' // end of heading
728
+ ),
729
+ array(
730
+ $matches[ $i ][1] . '<span class="ez-toc-section" id="' . $anchor . '">',
731
+ '</span></h' . $matches[ $i ][2] . '>'
732
+ ),
733
+ $matches[ $i ][0]
734
+ );
735
+ }
736
+ }
737
+
738
+ return $headings;
739
+ }
740
+
741
+ /**
742
+ * Get the post TOC list.
743
+ *
744
+ * @access public
745
+ * @since 2.0
746
+ *
747
+ * @return string
748
+ */
749
+ public function getTOCList() {
750
+
751
+ $html = '';
752
+
753
+ if ( $this->hasTOCItems ) {
754
+
755
+ foreach ( $this->pages as $page => $attribute ) {
756
+
757
+ $html .= $this->createTOC( $page, $attribute['headings'] );
758
+ }
759
+
760
+ $html = '<ul class="ez-toc-list">' . $html . '</ul>';
761
+ }
762
+
763
+ return $html;
764
+ }
765
+
766
+ /**
767
+ * Get the post TOC content block.
768
+ *
769
+ * @access public
770
+ * @since 2.0
771
+ *
772
+ * @return string
773
+ */
774
+ public function getTOC() {
775
+
776
+ $css_classes = '';
777
+ $html = '';
778
+
779
+ if ( $this->hasTOCItems() ) {
780
+
781
+ // wrapping css classes
782
+ switch ( ezTOC_Option::get( 'wrapping' ) ) {
783
+
784
+ case 'left':
785
+ $css_classes .= ' ez-toc-wrap-left';
786
+ break;
787
+
788
+ case 'right':
789
+ $css_classes .= ' ez-toc-wrap-right';
790
+ break;
791
+
792
+ case 'none':
793
+ default:
794
+ // do nothing
795
+ }
796
+
797
+ if ( ezTOC_Option::get( 'show_hierarchy' ) ) {
798
+
799
+ $css_classes .= ' counter-hierarchy';
800
+
801
+ } else {
802
+
803
+ $css_classes .= ' counter-flat';
804
+ }
805
+
806
+ switch ( ezTOC_Option::get( 'counter' ) ) {
807
+
808
+ case 'numeric':
809
+ $css_classes .= ' counter-numeric';
810
+ break;
811
+
812
+ case 'roman':
813
+ $css_classes .= ' counter-roman';
814
+ break;
815
+
816
+ case 'decimal':
817
+ $css_classes .= ' counter-decimal';
818
+ break;
819
+ }
820
+
821
+ // colour themes
822
+ switch ( ezTOC_Option::get( 'theme' ) ) {
823
+
824
+ case 'light-blue':
825
+ $css_classes .= ' ez-toc-light-blue';
826
+ break;
827
+
828
+ case 'white':
829
+ $css_classes .= ' ez-toc-white';
830
+ break;
831
+
832
+ case 'black':
833
+ $css_classes .= ' ez-toc-black';
834
+ break;
835
+
836
+ case 'transparent':
837
+ $css_classes .= ' ez-toc-transparent';
838
+ break;
839
+
840
+ case 'grey':
841
+ $css_classes .= ' ez-toc-grey';
842
+ break;
843
+
844
+ default:
845
+ // do nothing
846
+ }
847
+
848
+ if ( ezTOC_Option::get( 'css_container_class' ) ) {
849
+
850
+ $css_classes .= ' ' . ezTOC_Option::get( 'css_container_class' );
851
+ }
852
+
853
+ $css_classes = trim( $css_classes );
854
+
855
+ // an empty class="" is invalid markup!
856
+ if ( ! $css_classes ) {
857
+
858
+ $css_classes = ' ';
859
+ }
860
+
861
+ // add container, toc title and list items
862
+ $html .= '<div id="ez-toc-container" class="' . $css_classes . '">' . PHP_EOL;
863
+
864
+ if ( ezTOC_Option::get( 'show_heading_text' ) ) {
865
+
866
+ $toc_title = ezTOC_Option::get( 'heading_text' );
867
+
868
+ if ( strpos( $toc_title, '%PAGE_TITLE%' ) !== FALSE ) {
869
+
870
+ $toc_title = str_replace( '%PAGE_TITLE%', get_the_title(), $toc_title );
871
+ }
872
+
873
+ if ( strpos( $toc_title, '%PAGE_NAME%' ) !== FALSE ) {
874
+
875
+ $toc_title = str_replace( '%PAGE_NAME%', get_the_title(), $toc_title );
876
+ }
877
+
878
+ $html .= '<div class="ez-toc-title-container">' . PHP_EOL;
879
+
880
+ $html .= '<p class="ez-toc-title">' . esc_html__( htmlentities( $toc_title, ENT_COMPAT, 'UTF-8' ), 'easy-table-of-contents' ). '</p>' . PHP_EOL;
881
+
882
+ $html .= '<span class="ez-toc-title-toggle">';
883
+
884
+ if ( ezTOC_Option::get( 'visibility' ) ) {
885
+
886
+ $html .= '<a class="ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle"><i class="ez-toc-glyphicon ez-toc-icon-toggle"></i></a>';
887
+ }
888
+
889
+ $html .= '</span>';
890
+
891
+ $html .= '</div>' . PHP_EOL;
892
+ }
893
+
894
+ ob_start();
895
+ do_action( 'ez_toc_before' );
896
+ $html .= ob_get_clean();
897
+
898
+ $html .= '<nav>' . $this->getTOCList() . '</nav>';
899
+
900
+ ob_start();
901
+ do_action( 'ez_toc_after' );
902
+ $html .= ob_get_clean();
903
+
904
+ $html .= '</div>' . PHP_EOL;
905
+
906
+ // Enqueue the script.
907
+ wp_enqueue_script( 'ez-toc-js' );
908
+ }
909
+
910
+ return $html;
911
+ }
912
+
913
+ /**
914
+ * Displays the post's TOC.
915
+ *
916
+ * @access public
917
+ * @since 2.0
918
+ */
919
+ public function toc() {
920
+
921
+ echo $this->getTOC();
922
+ }
923
+
924
+ /**
925
+ * Generate the TOC list items for a given page within a post.
926
+ *
927
+ * @access private
928
+ * @since 2.0
929
+ *
930
+ * @param int $page The page of the post to create the TOC items for.
931
+ * @param array $matches The heading from the post content extracted with preg_match_all().
932
+ *
933
+ * @return string The HTML list of TOC items.
934
+ */
935
+ private function createTOC( $page, $matches ) {
936
+
937
+ // Whether or not the TOC should be built flat or hierarchical.
938
+ $hierarchical = ezTOC_Option::get( 'show_hierarchy' );
939
+ $html = '';
940
+
941
+ if ( $hierarchical ) {
942
+
943
+ $current_depth = 100; // headings can't be larger than h6 but 100 as a default to be sure
944
+ $numbered_items = array();
945
+ $numbered_items_min = NULL;
946
+
947
+ // reset the internal collision collection
948
+ /** @todo does this need to be used??? */
949
+ //self::$collision_collector = array();
950
+
951
+ // find the minimum heading to establish our baseline
952
+ for ( $i = 0; $i < count( $matches ); $i ++ ) {
953
+ if ( $current_depth > $matches[ $i ][2] ) {
954
+ $current_depth = (int) $matches[ $i ][2];
955
+ }
956
+ }
957
+
958
+ $numbered_items[ $current_depth ] = 0;
959
+ $numbered_items_min = $current_depth;
960
+
961
+ for ( $i = 0; $i < count( $matches ); $i ++ ) {
962
+
963
+ if ( $current_depth == (int) $matches[ $i ][2] ) {
964
+
965
+ $html .= '<li>';
966
+ }
967
+
968
+ // start lists
969
+ if ( $current_depth != (int) $matches[ $i ][2] ) {
970
+
971
+ for ( $current_depth; $current_depth < (int) $matches[ $i ][2]; $current_depth++ ) {
972
+
973
+ $numbered_items[ $current_depth + 1 ] = 0;
974
+ $html .= '<ul><li>';
975
+ }
976
+ }
977
+
978
+ $title = isset( $matches[ $i ]['alternate'] ) ? $matches[ $i ]['alternate'] : $matches[ $i ][0];
979
+ $title = strip_tags( apply_filters( 'ez_toc_title', $title ), apply_filters( 'ez_toc_title_allowable_tags', '' ) );
980
+
981
+ $html .= $this->createTOCItemAnchor( $page, $matches[ $i ]['id'], $title );
982
+
983
+ // end lists
984
+ if ( $i != count( $matches ) - 1 ) {
985
+
986
+ if ( $current_depth > (int) $matches[ $i + 1 ][2] ) {
987
+
988
+ for ( $current_depth; $current_depth > (int) $matches[ $i + 1 ][2]; $current_depth-- ) {
989
+
990
+ $html .= '</li></ul>';
991
+ $numbered_items[ $current_depth ] = 0;
992
+ }
993
+ }
994
+
995
+ if ( $current_depth == (int) @$matches[ $i + 1 ][2] ) {
996
+
997
+ $html .= '</li>';
998
+ }
999
+
1000
+ } else {
1001
+
1002
+ // this is the last item, make sure we close off all tags
1003
+ for ( $current_depth; $current_depth >= $numbered_items_min; $current_depth-- ) {
1004
+
1005
+ $html .= '</li>';
1006
+
1007
+ if ( $current_depth != $numbered_items_min ) {
1008
+ $html .= '</ul>';
1009
+ }
1010
+ }
1011
+ }
1012
+ }
1013
+
1014
+ } else {
1015
+
1016
+ for ( $i = 0; $i < count( $matches ); $i++ ) {
1017
+
1018
+ $title = isset( $matches[ $i ]['alternate'] ) ? $matches[ $i ]['alternate'] : $matches[ $i ][0];
1019
+ $title = strip_tags( apply_filters( 'ez_toc_title', $title ), apply_filters( 'ez_toc_title_allowable_tags', '' ) );
1020
+
1021
+ $html .= '<li>';
1022
+
1023
+ $html .= $this->createTOCItemAnchor( $page, $matches[ $i ]['id'], $title );
1024
+
1025
+ $html .= '</li>';
1026
+ }
1027
+ }
1028
+
1029
+ return $html;
1030
+ }
1031
+
1032
+ /**
1033
+ * @access private
1034
+ * @since 2.0
1035
+ *
1036
+ * @param int $page
1037
+ * @param string $id
1038
+ * @param string $title
1039
+ *
1040
+ * @return string
1041
+ */
1042
+ private function createTOCItemAnchor( $page, $id, $title ) {
1043
+
1044
+ return sprintf(
1045
+ '<a href="%1$s" title="%2$s">' . $title . '</a>',
1046
+ esc_url( $this->createTOCItemURL( $id, $page ) ),
1047
+ esc_attr( strip_tags( $title ) )
1048
+ );
1049
+ }
1050
+
1051
+ /**
1052
+ * @access private
1053
+ * @since 2.0
1054
+ *
1055
+ * @param string $id
1056
+ * @param int $page
1057
+ *
1058
+ * @return string
1059
+ */
1060
+ private function createTOCItemURL( $id, $page ) {
1061
+
1062
+ $current_post = $this->post->ID === $this->queriedObjectID;
1063
+ $current_page = $this->getCurrentPage();
1064
+
1065
+ if ( $page === $current_page && $current_post ) {
1066
+
1067
+ return '#' . $id;
1068
+
1069
+ } elseif ( 1 === $page ) {
1070
+
1071
+ return $this->permalink . '#' . $id;
1072
+
1073
+ }
1074
+
1075
+ return $this->permalink . $page . '/#' . $id;
1076
+ }
1077
+ }
includes/class.widget-toc.php CHANGED
@@ -138,74 +138,75 @@ if ( ! class_exists( 'ezTOC_Widget' ) ) {
138
 
139
  if ( is_404() || is_archive() || is_search() || ( ! is_front_page() && is_home() ) ) return;
140
 
141
- global $wp_query;
142
 
143
- $css_classes = '';
144
-
145
- $find = $replace = array();
146
- $post = get_post( $wp_query->post->ID );
147
 
148
  /*
149
  * Ensure the ezTOC content filter is not applied when running `the_content` filter.
150
  */
151
- remove_filter( 'the_content', array( 'ezTOC', 'the_content' ), 100 );
152
- $post->post_content = apply_filters( 'the_content', $post->post_content );
153
- add_filter( 'the_content', array( 'ezTOC', 'the_content' ), 100 );
154
-
155
- /**
156
- * @var string $before_widget
157
- * @var string $after_widget
158
- * @var string $before_title
159
- * @var string $after_title
160
- */
161
- extract( $args );
162
 
163
- $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
164
- $items = ezTOC::extract_headings( $find, $replace, $post );
165
 
166
- if ( FALSE !== strpos( $title, '%PAGE_TITLE%' ) || FALSE !== strpos( $title, '%PAGE_NAME%' ) ) {
167
 
168
- $title = str_replace( '%PAGE_TITLE%', get_the_title(), $title );
169
- }
 
 
 
 
 
 
 
 
170
 
171
- if ( ezTOC_Option::get( 'show_hierarchy' ) ) {
172
 
173
- $css_classes = ' counter-hierarchy';
 
174
 
175
- } else {
176
 
177
- $css_classes .= ' counter-flat';
178
- }
179
 
180
- switch ( ezTOC_Option::get( 'counter' ) ) {
181
 
182
- case 'numeric':
183
- $css_classes .= ' counter-numeric';
184
- break;
185
 
186
- case 'roman':
187
- $css_classes .= ' counter-roman';
188
- break;
189
 
190
- case 'decimal':
191
- $css_classes .= ' counter-decimal';
192
- break;
193
- }
194
 
195
- if ( $instance['affix'] ) {
 
 
196
 
197
- $css_classes .= ' ez-toc-affix';
198
- }
 
 
199
 
200
- $css_classes = trim( $css_classes );
201
 
202
- // an empty class="" is invalid markup!
203
- if ( ! $css_classes ) {
204
 
205
- $css_classes = ' ';
206
- }
207
 
208
- if ( $items ) {
 
 
 
 
209
 
210
  echo $before_widget;
211
 
@@ -255,7 +256,7 @@ if ( ! class_exists( 'ezTOC_Widget' ) ) {
255
  <?php
256
  }
257
 
258
- echo '<nav><ul class="ez-toc-list">'. PHP_EOL . $items . '</ul></nav>' . PHP_EOL;
259
 
260
  do_action( 'ez_toc_after_widget' );
261
 
138
 
139
  if ( is_404() || is_archive() || is_search() || ( ! is_front_page() && is_home() ) ) return;
140
 
141
+ //global $wp_query;
142
 
143
+ //$find = $replace = array();
144
+ //$post = get_post( $wp_query->post->ID );
145
+ $post = ezTOC_Post::get( get_the_ID() )->applyContentFilter()->process();
 
146
 
147
  /*
148
  * Ensure the ezTOC content filter is not applied when running `the_content` filter.
149
  */
150
+ //remove_filter( 'the_content', array( 'ezTOC', 'the_content' ), 100 );
151
+ //$post->post_content = apply_filters( 'the_content', $post->post_content );
152
+ //add_filter( 'the_content', array( 'ezTOC', 'the_content' ), 100 );
 
 
 
 
 
 
 
 
153
 
154
+ if ( $post->hasTOCItems() ) {
 
155
 
156
+ $css_classes = '';
157
 
158
+ /**
159
+ * @var string $before_widget
160
+ * @var string $after_widget
161
+ * @var string $before_title
162
+ * @var string $after_title
163
+ */
164
+ extract( $args );
165
+
166
+ $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
167
+ //$items = ezTOC::extract_headings( $find, $replace, $post );
168
 
169
+ if ( FALSE !== strpos( $title, '%PAGE_TITLE%' ) || FALSE !== strpos( $title, '%PAGE_NAME%' ) ) {
170
 
171
+ $title = str_replace( '%PAGE_TITLE%', get_the_title(), $title );
172
+ }
173
 
174
+ if ( ezTOC_Option::get( 'show_hierarchy' ) ) {
175
 
176
+ $css_classes = ' counter-hierarchy';
 
177
 
178
+ } else {
179
 
180
+ $css_classes .= ' counter-flat';
181
+ }
 
182
 
183
+ switch ( ezTOC_Option::get( 'counter' ) ) {
 
 
184
 
185
+ case 'numeric':
186
+ $css_classes .= ' counter-numeric';
187
+ break;
 
188
 
189
+ case 'roman':
190
+ $css_classes .= ' counter-roman';
191
+ break;
192
 
193
+ case 'decimal':
194
+ $css_classes .= ' counter-decimal';
195
+ break;
196
+ }
197
 
198
+ if ( $instance['affix'] ) {
199
 
200
+ $css_classes .= ' ez-toc-affix';
201
+ }
202
 
203
+ $css_classes = trim( $css_classes );
 
204
 
205
+ // an empty class="" is invalid markup!
206
+ if ( ! $css_classes ) {
207
+
208
+ $css_classes = ' ';
209
+ }
210
 
211
  echo $before_widget;
212
 
256
  <?php
257
  }
258
 
259
+ echo '<nav>'. PHP_EOL . $post->getTOCList() . '</nav>' . PHP_EOL;
260
 
261
  do_action( 'ez_toc_after_widget' );
262
 
includes/inc.functions.php ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ /**
7
+ * Get the current post's TOC list or supplied post's TOC list.
8
+ *
9
+ * @access public
10
+ * @since 2.0
11
+ *
12
+ * @param int|null|WP_Post $post An instance of WP_Post or post ID. Defaults to current post.
13
+ * @param bool $apply_content_filter Whether or not to apply `the_content` filter when processing post for headings.
14
+ *
15
+ * @return string
16
+ */
17
+ function get_ez_toc_list( $post = NULL, $apply_content_filter = TRUE ) {
18
+
19
+ if ( ! $post instanceof WP_Post ) {
20
+
21
+ $post = get_post( $post );
22
+ }
23
+
24
+ $ezPost = new ezTOC_Post( $post );
25
+
26
+ if ( $apply_content_filter ) {
27
+
28
+ $ezPost->applyContentFilter()->process();
29
+
30
+ } else {
31
+
32
+ $ezPost->process();
33
+ }
34
+
35
+ return $ezPost->getTOCList();
36
+ }
37
+
38
+ /**
39
+ * Display the current post's TOC list or supplied post's TOC list.
40
+ *
41
+ * @access public
42
+ * @since 2.0
43
+ *
44
+ * @param null|WP_Post $post An instance of WP_Post
45
+ * @param bool $apply_content_filter Whether or not to apply `the_content` filter when processing post for headings.
46
+ */
47
+ function ez_toc_list( $post = NULL, $apply_content_filter = TRUE ) {
48
+
49
+ echo get_ez_toc_list( $post, $apply_content_filter );
50
+ }
51
+
52
+ /**
53
+ * Get the current post's TOC content block or supplied post's TOC content block.
54
+ *
55
+ * @access public
56
+ * @since 2.0
57
+ *
58
+ * @param int|null|WP_Post $post An instance of WP_Post or post ID. Defaults to current post.
59
+ * @param bool $apply_content_filter Whether or not to apply `the_content` filter when processing post for headings.
60
+ *
61
+ * @return string
62
+ */
63
+ function get_ez_toc_block( $post = NULL, $apply_content_filter = TRUE ) {
64
+
65
+ if ( ! $post instanceof WP_Post ) {
66
+
67
+ $post = get_post( $post );
68
+ }
69
+
70
+ $ezPost = new ezTOC_Post( $post );
71
+
72
+ if ( $apply_content_filter ) {
73
+
74
+ $ezPost->applyContentFilter()->process();
75
+
76
+ } else {
77
+
78
+ $ezPost->process();
79
+ }
80
+
81
+ return $ezPost->getTOC();
82
+ }
83
+
84
+ /**
85
+ * Display the current post's TOC content or supplied post's TOC content.
86
+ *
87
+ * @access public
88
+ * @since 2.0
89
+ *
90
+ * @param null|WP_Post $post An instance of WP_Post
91
+ * @param bool $apply_content_filter Whether or not to apply `the_content` filter when processing post for headings.
92
+ */
93
+ function ez_toc_block( $post = NULL, $apply_content_filter = TRUE ) {
94
+
95
+ echo get_ez_toc_block( $post, $apply_content_filter );
96
+ }