PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 2.3.6
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v2.3.6
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
betterdocs / public / class-betterdocs-toc.php

class-betterdocs-toc.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 2.3.6, at public/class-betterdocs-toc.php

189 lines 8.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class Node {
4 public $title;
5 public $tag;
6 public $tag_number;
7 public $key;
8
9 public function print( $items, $toc_hierarchy, $level ){
10 $html = '';
11 if( ! empty ( $items ) ) {
12 $toc_hierarchy_class = ( $toc_hierarchy != 'off' && $toc_hierarchy != '' ) ? 'toc-list betterdocs-hierarchial-toc' : 'toc-list';
13 $html .= $level === 1 ? '<ul class = "'.$toc_hierarchy_class.'">' : '<ul class = "betterdocs-toc-list-level-'.$level.'">';
14 foreach( $items as $item ) {
15 $html .= '<li class = "betterdocs-toc-heading-level-'.($item->key).'">';
16 $html .= '<a href="#'.$item->tag_number.'">'.strip_tags( $item->title ).'</a>';
17 if( ! empty ( $items ) ) {
18 $html .= $this->print( $item->items, $toc_hierarchy, ++$level );
19 } else {
20 $level = 1;
21 break;
22 }
23 $html .= '</li>';
24 }
25 $html .= '</ul>';
26 }
27 return $html;
28 }
29 }
30
31
32 class Betterdocs_TOC{
33
34 public function __construct() {
35 add_shortcode( 'betterdocs_toc', array( $this, 'betterdocs_toc' ) );
36 }
37
38 public function betterdocs_toc( $atts )
39 {
40 do_action( 'betterdocs_before_shortcode_load' );
41
42 $get_args = shortcode_atts(
43 array(
44 'post_type' => 'docs',
45 'post_id' => get_the_ID(),
46 'htags' => '1,2,3,4,5,6',
47 'hierarchy' => '',
48 'list_number' => '',
49 'collapsible_on_mobile' => '',
50 'toc_title' => '',
51 ),
52 $atts
53 );
54
55 $get_post = get_post( $get_args['post_id'] );
56 $post_content = $get_post->post_content;
57
58 return self::betterdocs_toc_content(
59 $post_content,
60 $get_args['htags'],
61 $get_args['hierarchy'],
62 $get_args['list_number'],
63 $get_args['collapsible_on_mobile'],
64 $get_args['toc_title']
65 );
66 }
67
68 public static function betterdocs_toc_content( $content, $htags, $toc_hierarchy, $list_number, $collapsible, $toc_title='' ) {
69 $html = '';
70 $toc_data = self::format_toc_data( $content, $htags, $toc_hierarchy );
71
72 if ( ! empty( $toc_data->items ) ) {
73 $toc_class = array( 'betterdocs-toc' );
74 if ( empty( $toc_title ) ) {
75 $toc_title = BetterDocs_DB::get_settings('toc_title') ? BetterDocs_DB::get_settings('toc_title') : esc_html__( 'Table of Contents', 'betterdocs' );
76 $toc_title = stripslashes($toc_title);
77 }
78
79 if ( $collapsible == '1' ) {
80 $toc_class[] = 'collapsible-sm';
81 $collapsible_arrow = "<svg class='angle-icon angle-up' aria-hidden='true' focusable='false' data-prefix='fas' data-icon='angle-up' class='svg-inline--fa fa-angle-up fa-w-10' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'><path fill='currentColor' d='M177 159.7l136 136c9.4 9.4 9.4 24.6 0 33.9l-22.6 22.6c-9.4 9.4-24.6 9.4-33.9 0L160 255.9l-96.4 96.4c-9.4 9.4-24.6 9.4-33.9 0L7 329.7c-9.4-9.4-9.4-24.6 0-33.9l136-136c9.4-9.5 24.6-9.5 34-.1z'></path></svg><svg class='angle-icon angle-down' aria-hidden='true' focusable='false' data-prefix='fas' data-icon='angle-down' class='svg-inline--fa fa-angle-down fa-w-10' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'><path fill='currentColor' d='M143 352.3L7 216.3c-9.4-9.4-9.4-24.6 0-33.9l22.6-22.6c9.4-9.4 24.6-9.4 33.9 0l96.4 96.4 96.4-96.4c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9l-136 136c-9.2 9.4-24.4 9.4-33.8 0z'></path></svg>";
82 } else {
83 $collapsible_arrow = null;
84 }
85
86 if ( $list_number == '1' ) {
87 $toc_class[] = 'toc-list-number';
88 }
89
90 $html = '<div class="' . implode( ' ', $toc_class ) . '">';
91 $html .= '<span class="toc-title">' . $toc_title . $collapsible_arrow . '</span>';
92 $html .= $toc_data->print( $toc_data->items, $toc_hierarchy, 1 );
93 $html .='</div>';
94 }
95
96 return $html;
97 }
98
99 /**
100 * This method is responsible for re-arranging the TOC data based on hierarchy or non-hierarchy
101 *
102 * @param string $post_content
103 * @param string $htag_support
104 * @return object
105 */
106 public static function format_toc_data( $post_content, $htag_support, $toc_hierarchy )
107 {
108 $matches = array();
109
110 if( $htag_support != '' ) {
111 preg_match_all( '/(<h(['.$htag_support.']{1})[^>]*>).*<\/h\2>/msuU', $post_content, $matches, PREG_SET_ORDER );
112 }
113
114 if( ! empty( $matches ) ) {
115
116 /*
117 |--------------------------------------------------------------------------
118 | Backtracking Algorithm Using Iteration | Main Login For Hierarchy TOC
119 |--------------------------------------------------------------------------
120 |
121 | Initially an object with key null and empty item of arrays are inserted into the stack of arrays.
122 | When inside the loop condition for the first time, the last stack value is assigned in a variable $last_data.
123 | And a new node object $new_data which is instantiated and the tag number is inserted for comparison as key, and empty items
124 | as a array are inserted into items property of the new node object. On the 'if' condition it checks, if the current tag number
125 | is smaller or equal to the last stack number. If the condition is true, the it enters into the while loop condition, which also
126 | checks if the current last stack tag number is greater or equal to the current tag number. It pops values from the stack until and unless the
127 | condition becomes false. The while loop condition becomes false only when the last stack value tag number becomes null and the last stack number is not
128 | greater or equal to the current node tag number.
129 |
130 | Backtracking occurs when the current tag_number $number[2] is less than or equal to the stacks last tag_number which is assigned as $last_data
131 |
132 | And then the last value is inserted as the new last_data, and the new_data node is inserted into the last_data node items.
133 | Additionally the $new_data is inserted into the stack to keep track of the used node. Somehow if the if condition becomes false
134 | then the stack last data is taken, and the new_data is inserted into the last_data->items and the new_data is inserted into the stack.
135 |
136 */
137
138 $stack = array();
139 $root = new Node();
140 $root->key = null;
141 $root->items = array();
142 $tag_counter = 0;
143
144 array_push( $stack, $root );
145
146 foreach( $matches as $number ) {
147 $last_data = $stack[count($stack) - 1];
148 $current_tag_number = isset( $number[2] ) ? $number[2] : '';
149 $current_title = isset( $number[0] ) ? $number[0] : '';
150 $current_tag = isset( $number[1] ) ? $number[1] : '';
151
152 // Get The Heading Name & Heading ID using REGEX
153 $heading_name = preg_replace( '/<[^<]+?>/', '', $current_title );
154 $heading_name = ! empty( $heading_name ) ? strtolower( str_replace( " ", '-', preg_replace('/<[^>]+>|[^a-zA-Z\s\d]+/', "", html_entity_decode( $heading_name ) ) ) ) : '';
155 preg_match('/id="(.+?)"/', $current_title, $id_matches);
156 $heading_id = isset( $id_matches[1] ) ? strtolower( $id_matches[1] ) : '';
157
158 $tag_number = ! empty( $heading_id ) ? $heading_id : ( ! empty( $heading_name ) && BetterDocs_DB::get_settings('toc_dynamic_title') != 'off' ? $heading_name : $tag_counter . '-toc-title' );
159
160 $new_data = new Node();
161 $new_data->key = $current_tag_number;
162 $new_data->tag = $current_tag;
163 $new_data->title = $current_title;
164 $new_data->tag_number = $tag_number;
165 $new_data->items = array();
166
167 if( $last_data->key != null && $current_tag_number <= $last_data->key ) {
168 while( $stack[count($stack) - 1]->key != null && $stack[count($stack) - 1]->key >= $current_tag_number ) {
169 array_pop( $stack );
170 }
171 $last_data = $stack[count($stack) - 1];
172 }
173
174 array_push( $last_data->items, $new_data );
175
176 if( $toc_hierarchy != 'off' && $toc_hierarchy != '' ) {
177 array_push( $stack, $new_data );
178 }
179
180 $tag_counter++;
181 }
182
183 return $root;
184
185 }
186 }
187 }
188
189 new Betterdocs_TOC();