Version Description
Download this release
Release Info
Developer | shazahm1@hotmail.com |
Plugin | 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 +22 -681
- includes/class.post.php +1077 -0
- includes/class.widget-toc.php +48 -47
- includes/inc.functions.php +96 -0
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:
|
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
|
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 = '
|
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 |
-
|
862 |
|
863 |
-
$post
|
864 |
-
|
865 |
-
if ( empty( $post ) ) {
|
866 |
return FALSE;
|
867 |
}
|
868 |
|
869 |
-
|
|
|
|
|
|
|
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 |
-
$
|
1109 |
-
$out = $
|
|
|
1110 |
$run = FALSE;
|
1111 |
}
|
1112 |
|
@@ -1142,26 +542,14 @@ if ( ! class_exists( 'ezTOC' ) ) {
|
|
1142 |
return $content;
|
1143 |
}
|
1144 |
|
1145 |
-
|
1146 |
-
|
1147 |
-
|
1148 |
-
|
1149 |
-
|
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 (
|
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 `&` and ` ` NOTE: in order to strip "hidden" ` `,
|
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( '&', ' ' ), ' ', $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 |
-
$
|
144 |
-
|
145 |
-
$
|
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 |
-
|
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 |
-
|
164 |
-
$items = ezTOC::extract_headings( $find, $replace, $post );
|
165 |
|
166 |
-
|
167 |
|
168 |
-
|
169 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
|
171 |
-
|
172 |
|
173 |
-
|
|
|
174 |
|
175 |
-
|
176 |
|
177 |
-
|
178 |
-
}
|
179 |
|
180 |
-
|
181 |
|
182 |
-
|
183 |
-
|
184 |
-
break;
|
185 |
|
186 |
-
|
187 |
-
$css_classes .= ' counter-roman';
|
188 |
-
break;
|
189 |
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
}
|
194 |
|
195 |
-
|
|
|
|
|
196 |
|
197 |
-
|
198 |
-
|
|
|
|
|
199 |
|
200 |
-
|
201 |
|
202 |
-
|
203 |
-
|
204 |
|
205 |
-
$css_classes =
|
206 |
-
}
|
207 |
|
208 |
-
|
|
|
|
|
|
|
|
|
209 |
|
210 |
echo $before_widget;
|
211 |
|
@@ -255,7 +256,7 @@ if ( ! class_exists( 'ezTOC_Widget' ) ) {
|
|
255 |
<?php
|
256 |
}
|
257 |
|
258 |
-
echo '<nav
|
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 |
+
}
|