Easy Table of Contents - Version 2.0.22

Version Description

05/06/2022 = * BUG: Double hyphens are getting removed from content issue fixed #163

Download this release

Release Info

Developer magazine3
Plugin Icon 128x128 Easy Table of Contents
Version 2.0.22
Comparing to
See all releases

Code changes from version 2.0.19 to 2.0.22

README.txt CHANGED
@@ -1,480 +1,501 @@
1
- === Easy Table of Contents ===
2
- Contributors: magazine3
3
- Donate link: https://magazine3.company/
4
- Tags: table of contents, toc
5
- Requires at least: 5.3
6
- Tested up to: 5.9.3
7
- Requires PHP: 5.6.20
8
- Stable tag: 2.0.19
9
- License: GPLv2 or later
10
- License URI: https://www.gnu.org/licenses/gpl-2.0.html
11
-
12
- Adds a user friendly and fully automatic way to create and display a table of contents generated from the page content.
13
-
14
- == Description ==
15
-
16
- A user friendly, featured focused plugin which allows you to insert a table of contents into your posts, pages and custom post types.
17
-
18
- ### Features
19
- * Automatically generate a table of contents for your posts, pages and custom post types by parsing its contents for headers.
20
- * Supports the `<!--nextpage-->` tag.
21
- * Supports the Rank Math plugin.
22
- * Works with the Classic Editor, Gutenberg, Divi, Elementor, WPBakery Page Builder and Visual Composer page editors.
23
- * Optionally enable for pages and/or posts. Custom post types are supported, as long as their content is output with the `the_content()` template tag.
24
- * Optionally auto insert the table of contents into the page, selectable by enabled post type.
25
- * Provides many easy to understand options to configure when and where to insert the table of contents.
26
- * Many options are available to configure how the inserted table of contents appears which include several builtin themes. If the supplied themes do no meet you needs, you can create your own by choosing you own colors for the border, background and link color.
27
- * Multiple counter bullet formats to choose from; none, decimal, numeric and roman.
28
- * Choose to display the table of contents hierarchical or not. This means headings of lower priority will be nested under headings of higher priority.
29
- * User can optionally hide the table of contents. You full control of this feature. It can be disabled and you can choose to have it hidden by default.
30
- * Supports smooth scrolling.
31
- * Selectively enable or disabled the table of contents on a post by post basis.
32
- * Choose which headings are used to generate the table of contents. This too can be set on a post by post basis.
33
- * Easily exclude headers globally and on a post by post basis.
34
- * If you rather not insert the table of contents in the post content, you can use the supplied widget and place the table of contents in your theme's sidebar.
35
- * The widgets supports being affixed or stuck on the page so it is always visible as you scroll down the page. NOTE: this is an advanced option since every theme is different, you might need support from your theme developer to learn what the correct item selector to use in the settings to enable this feature.
36
- * The widget auto highlights the sections currently visible on the page. The highlight color is configurable.
37
- * Developer friendly with many action hooks and filters available. More can be added by request on [Github](https://github.com/shazahm1/Easy-Table-of-Contents). Pull requests are welcomed.
38
-
39
- ### Support
40
-
41
- We try our best to provide support on WordPress.org forums. However, We have a special [team support](https://magazine3.company/contact/) where you can ask us questions and get help. Delivering a good user experience means a lot to us and so we try our best to reply each and every question that gets asked.
42
-
43
- ### Bug Reports
44
-
45
- Bug reports for Easy Table of Contents are [welcomed on GitHub](https://github.com/ahmedkaludi/Easy-Table-of-Contents). Please note GitHub is not a support forum, and issues that aren't properly qualified as bugs will be closed.
46
-
47
- ### Live Examples
48
-
49
- * [cMap Template Docs](http://connections-pro.com/documentation/cmap/)
50
- * [Circled Template Docs](http://connections-pro.com/documentation/circled/)
51
- * [Gridder Template Docs](http://connections-pro.com/documentation/gridder/)
52
-
53
- ### Roadmap
54
- * Fragment caching for improved performance.
55
- * Improve SEO by adding options to add nofollow to TOC link and wrap TOC nav in noindex tag.
56
- * Improve accessibility.
57
- * Add Bullet and Arrow options for list counter style.
58
-
59
- ### Credit
60
-
61
- Easy Table Contents is a fork of the excellent [Table of Contents Plus](https://wordpress.org/plugins/table-of-contents-plus/) plugin by [Michael Tran](http://dublue.com/plugins/toc/).
62
-
63
- ### Screenshots
64
-
65
- 1. The General section of the settings.
66
- 2. The Appearance section of the settings.
67
- 3. The Advanced section of the settings.
68
-
69
- ### Installation
70
-
71
- = Using the WordPress Plugin Search =
72
-
73
- 1. Navigate to the `Add New` sub-page under the Plugins admin page.
74
- 2. Search for `easy table of contents`.
75
- 3. The plugin should be listed first in the search results.
76
- 4. Click the `Install Now` link.
77
- 5. Lastly click the `Activate Plugin` link to activate the plugin.
78
-
79
- = Uploading in WordPress Admin =
80
-
81
- 1. [Download the plugin zip file](http://wordpress.org/plugins/easy-table-of-contents/) and save it to your computer.
82
- 2. Navigate to the `Add New` sub-page under the Plugins admin page.
83
- 3. Click the `Upload` link.
84
- 4. Select Easy Table of Contents zip file from where you saved the zip file on your computer.
85
- 5. Click the `Install Now` button.
86
- 6. Lastly click the `Activate Plugin` link to activate the plugin.
87
-
88
- = Using FTP =
89
-
90
- 1. [Download the plugin zip file](http://wordpress.org/plugins/easy-table-of-contents/) and save it to your computer.
91
- 2. Extract the Easy Table of Contents zip file.
92
- 3. Create a new directory named `easy-table-of-contents` directory in the `../wp-content/plugins/` directory.
93
- 4. Upload the files from the folder extracted in Step 2.
94
- 4. Activate the plugin on the Plugins admin page.
95
-
96
- == Changelog ==
97
-
98
- = 2.0.19 04/16/2022 =
99
- * Bug Fixed : While Using Elementor Page builder TOC is not working when special characters are used in headings. #150
100
- * Bug Fixed : Need to load CSS/JS files only on the selected post types. #154
101
-
102
- = 2.0.18 03/29/2022 =
103
- * TWEAK: Added Technical Support Tab in Settings Panel.
104
-
105
- = 2.0.17 03/26/2021 =
106
- * TWEAK: Add additional check to prevent `Uncaught Error: Call to undefined function is_woocommerce()`.
107
- * TWEAK: Ensure an instance of `ezTOC_Post ` is returned before accessing methods/properties.
108
-
109
- = 2.0.16 02/01/2021 =
110
- * TWEAK: Remove special characters such as fancy quotes, en and, em dashes when generating in-page anchor IDs.
111
-
112
- = 2.0.15 01/27/2021 =
113
- * TWEAK: Remove additional reserved characters when generating in-page anchor IDs.
114
-
115
- = 2.0.14 01/26/2021 =
116
- * TWEAK: Refactor debug log as a Singleton.
117
- * TWEAK: Add additional logging to aid in debugging.
118
- * BUG: Correct logic for PHP where empty string no longer evaluates as integer `0`.
119
-
120
- = 2.0.13 01/25/2021 =
121
- * TWEAK: Restrict debug logging to when `WP_DEBUG` is enabled *and* current user capability of `manage_options`.
122
- * TWEAK: Add logging to aid in support.
123
- * DEV: phpDoc update.
124
-
125
- = 2.0.12 01/22/2021 =
126
- * TWEAK: Allow `_` and `-` in anchors.
127
- * TWEAK: Minor CSS tweaks that prevent theme from breaking the layout.
128
- * TWEAK: Minor tweak to class initialization.
129
- * TWEAK: Do not display the view toggle if JavaScript is broken on the site.
130
- * TWEAK: Add the ability to enable displaying of displaying debug information on the page.
131
- * BUG: Check for array and keys before accessing values.
132
- * BUG: Check for array key be fore access.
133
- * BUG: Remove reserved characters when generating in-page anchor IDs.
134
- * DEV: Remove unnecessary vendor library files.
135
- * DEV: Deal with phpStorm showing a warning about path not found when including files.
136
-
137
- = 2.0.11 05/01/2020 =
138
- * COMPATIBILITY: Add support for the Uncode theme.
139
- * COMPATIBILITY: Do not run on WooCommerce pages.
140
- * DEV: Correct typo in phpDoc.
141
-
142
- = 2.0.10 04/20/2020 =
143
- * TWEAK: Add trailing `span` to heading, to prepare for `#` option and to fix duplicate heading title matching.
144
- * TWEAK: Add second heading search/replace function to search for heading in content with heading html entities decoded. May help Beaver Builder users as it seems like it does not encode HTML entities as WP core does.
145
-
146
-
147
- = 2.0.9 04/08/2020 =
148
- * TWEAK: AMP/Caching plugins seems to break anchors with colons and periods even though they are valid characters for the id attribute in HTML5.
149
- * TWEAK: Replace multiple underscores with a single underscore.
150
- * DEV: Update the UWS library which fixes the deprecation notice for PHP 7.4.
151
- * DEV: Add phpcs.xml.dist.
152
- * DEV: Strict type checks.
153
- * DEV: Inline doc updates.
154
-
155
- = 2.0.8 04/03/2020 =
156
- * TWEAK: Convert `<br />` tags in headings to a space.
157
- * TWEAK: Add additional widget classes.
158
- * TWEAK: Improve the sanitization of the excluded headings field post setting.
159
- * TWEAK: Minor optimization of creating the matching pattern for excluding headings for improved performance.
160
- * COMPATIBILITY: Exclude Create by Mediavine from heading eligibility.
161
- * BUG: Ensure excluded headings are removed from the headings array.
162
- * BUG: Ensure empty headings are removed from the headings array.
163
-
164
- = 2.0.7 04/02/2020 =
165
- * NEW: Exclude any HTML nodes with the class of `.ez-toc-exclude-headings`.
166
- * TWEAK: Change smooth scroll selector from `'body a'` to `'a.ez-toc-link'`.
167
- * TWEAK: Declare JS variables.
168
- * TWEAK: Support unicode characters for the `id` attribute. Permitted by HTML5.
169
- * TWEAK: Move the in-page anchor/span to before the heading text to account for long headings where it line wraps.
170
- * TWEAK: Slight rework to ezTOC widget container classes logic.
171
- * TWEAK: Cache bust the JS to make dev easier.
172
- * TWEAK: JavaScript cleanup.
173
- * TWEAK: URI Encode the id attribute to deal with reserved characters in JavaScript. Technically not necessary for the id attribute but needed to work with the jQuery smoothScroll library.
174
- * COMPATIBILITY: Reintroduce filter to exclude Ultimate Addons for VC Composer Tabs from heading eligibility.
175
- * BUG: Correct array iteration logic when processing headings.
176
- * BUG: Tighten matching for headings in excluded HTML nodes. The loose matching was excluding far too many headings.
177
- * BUG: Use `esc_attr()` instead of `esc_url()` for the anchor href because valid id attribute characters would cause it to return an empty href which cause a nonworking link.
178
-
179
- = 2.0.6 03/30/2020 =
180
- * BUG: Ensure minified files are current.
181
-
182
- = 2.0.5 03/27/2020 =
183
- * BUG: Prevent possible "strpos(): Empty needle in" warnings when excluding nodes from TOC eligibility.
184
-
185
- = 2.0.4 03/16/2020 =
186
- * NEW: Introduce the `ez_toc_container_class` filter.
187
- * TWEAK: Slight rework to ezTOC container classes logic.
188
- * BUG: `sprintf()` was eating `%` in the TOC heading item.
189
- * BUG: Do not insert TOC at top of post if before first heading option is selected even if first heading can not be found. Some page builders cause the TOC to insert twice or on blog pages.
190
-
191
- = 2.0.3 03/12/2020 =
192
- * TWEAK: Slightly tighten heading matching, last update made it a little too loose.
193
- * BUG: Correct logic required to place TOC before first heading which is required for the more lax heading matching required for page builders.
194
-
195
- = 2.0.2 03/12/2020 =
196
- * COMPATIBILITY: Remove filter to exclude Ultimate Addons for VC Composer Tabs from heading eligibility.
197
- * COMPATIBILITY: Add additional filters to improve Elementor compatibility.
198
- * TWEAK: Loosen heading matching when doing find/replace to insert in page links. Excluding the opening heading tag to allow matching heading where page builders dynamically add classes and id which break heading matching during find/replace.
199
-
200
- = 2.0.1 03/09/2020 =
201
- * COMPATIBILITY: Exclude the WordPress Related Posts plugin nodes.
202
- * COMPATIBILITY: Exclude a couple Atomic Block plugin nodes.
203
- * COMPATIBILITY: Exclude JetPack Related Posts from heading eligibility.
204
- * COMPATIBILITY: Exclude Ultimate Addons for VC Composer Tabs from heading eligibility.
205
- * COMPATIBILITY: Exclude WP Product Reviews from heading eligibility.
206
- * TWEAK: Prevent possible "strpos(): Empty needle in" warnings when excluding nodes from TOC eligibility.
207
-
208
- = 2.0 02/01/2020 =
209
- * NEW: Major rewrite of all code and processing logic to make it faster and more reliable.
210
- * NEW: Support for the <!--nextpage--> tag.
211
- * NEW: Introduce helper functions for devs.
212
- * NEW: Support WPML.
213
- * NEW: Support Polylang.
214
- * NEW: Add filter to support the Rank Math plugin.
215
- * NEW: Introduce the `ez_toc_maybe_apply_the_content_filter` filter.
216
- * TWEAK: Improve translation compatibility.
217
- * TWEAK: Rework widget logic to allow multi-line TOC items, improve active item highlighting while removing the use of the jQuery Waypoints library.
218
- * TWEAK Add additional classes to TOC list items.
219
- * TWEAK: Add WOFF2 format for icon format and change font references in CSS.
220
- * TWEAK: Add font-display: swap for toggle icon.
221
- * TWEAK: Update JS Cookie to 2.2.1.
222
- * TWEAK: Update jQuery Smooth Scroll to 2.2.0.
223
- * TWEAK: Allow forward slash and angle brackets in headings and alternate headings.
224
- * TWEAK: Allow forward slash in excluded headings.
225
- * TWEAK: Remove new line/returns when matching excluded headings.
226
- * TWEAK: Simple transient cache to ensure a post is only processed once per request for a TOC.
227
- * TWEAK: Improve sanitization of alternate headings field value.
228
- * TWEAK: Deal with non-breaking-spaces in alternate headings.
229
- * TWEAK: Add the ability to exclude by selector content eligible to be included in the TOC.
230
- * TWEAK: Change the shortcode priority to a higher value.
231
- * TWEAK: Add filter to remove shortcodes from the content prior to the `the_content` filter being run to exclude shortcode content from being eligible as TOC items.
232
- * TWEAK: Add compatibility filters to remove shortcodes for Connections and Striking theme to remove them from eligible TOC item content.
233
- * TWEAK: Do not execute if root current filter is the `wp_head` or `get_the_excerpt` filters.
234
- * TWEAK: Add filter to exclude content by selector.
235
- * TWEAK: Move in-page anchor to after the heading instead of wrapping the heading to prevent conflicts with theme styling.
236
- * TWEAK: Utilize the `ez_toc_exclude_by_selector` filter the exclude the JetPack share buttons from eligible headings.
237
- * TWEAK: Remove the Elegant Themes Bloom plugin node from the post content before extracting headings.
238
- * TWEAK: Add compatibility filter for the Visual Composer plugin.
239
- * TWEAK: Utilize the `ez_toc_exclude_by_selector` filter the exclude the Starbox author heading from eligible headings.
240
- * I18N: Add wpml-config.xml file.
241
- * BUG: Correct option misspelling.
242
- * BUG: Do not need to run values for alternate and exclude headings thru `wp_unslash()` because `update_post_meta()` already does.
243
- * BUG: Do not need to run `stripslashes()` when escaping the alternate heading value.
244
- * BUG: Sanitize the excluded heading string before saving post meta.
245
- * DEV: Change PHP keywords to comply with PSR2.
246
- * DEV:Bump minimum PHP version to 5.6.20 which matches WP core.
247
-
248
- = 1.7 05/09/2018 =
249
- * NEW: Introduce the `ez_toc_shortcode` filter.
250
- * TWEAK: Fix notices due to late eligibility check. props unixtam
251
- * TWEAK: Tweak eligibility check to support the TOC widget.
252
- * TWEAK: Prefix a few CSS classes in order to prevent collisions with theme's and other plugins.
253
- * TWEAK: Avoid potential PHP notice in admin when saving the post by checking for nonce before validating it.
254
- * TWEAK: Using the shortcode now overrides global options.
255
- * TWEAK: `the_content()` now caches result of `is_eligible()`.
256
- * TWEAK: Refactor to pass the WP_Post object internally vs. accessing it via the `$wp_query->post` which may not in all cases exist.
257
- * TWEAK: Use `pre_replace()` to replace one or more spaces with an underscore.
258
- * TWEAK: Return original title in the `ez_toc_url_anchor_target` filter.
259
- * TWEAK: Strip `&nbsp;`, replacing it with a space character.
260
- * TWEAK: Minor tweaks to the in page URL creating.
261
- * TWEAK: Wrap TOC list in a nav element.
262
- * TWEAK: Init plugin on the `plugins_loaded` hook.
263
- * TWEAK: Tweak the minimum number of headers to 1.
264
- * BUG: The header options from the post meta should be used when building the TOC hierarchy, not the header options from the global settings.
265
- * BUG: Do not double escape field values.
266
- * BUG: Ensure Apostrophe / Single quote use in Exclude Headings work.
267
- * OTHER: Update CSS to include the newly prefixed classes.
268
- * DEV: Remove some commented out unused code.
269
-
270
- = 1.6.1 03/16/2018 =
271
- * TWEAK: Revert change made to allow HTML added via the `ez_toc_title` filter as it caused undesirable side effects.
272
- * BUG: Ensure Smooth Scroll Offset is parsed as an integer.
273
-
274
- = 1.6 03/15/2018 =
275
- * NEW: Add `px` option for font size unit.
276
- * NEW: Add title font size and weight settings options.
277
- * NEW: Add the Mobile Smooth Scroll Offset option.
278
- * TWEAK: Change default for font size unit from `px` to `%` to match the default options values.
279
- * TWEAK: Correct CSS selector so margin is properly applied between the title and TOC items.
280
- * TWEAK: Honor HTML added via `ez_toc_title` filter.
281
- * TWEAK: Ensure the ezTOC content filter is not applied when running `the_content` filter.
282
- * TWEAK: Only enqueue the javascript if the page is eligible for a TOC.
283
- * TWEAK: Update icomoon CSS to remove unecessary CSS selectors to prevent possible conflicts.
284
- * TWEAK: The smooth scroll offset needs to be taken into account when defining the offset_top property when affixing the widget.
285
- * OTHER: Update frontend minified CSS file.
286
- * OTHER: Update the frontend minified javascript file.
287
- * DEV: phpDoc corrections.
288
-
289
- = 1.5 02/20/2018 =
290
- * BUG: Correct CSS selector to properly target the link color.
291
- * OTHER: Update the WayPoints library.
292
- * DEV: Add a couple @todo's.
293
-
294
- = 1.4 01/29/2018 =
295
- * TWEAK: Change text domain from ez_toc to easy-table-of-contents.
296
- * TWEAK: Rename translation files with correct text domain.
297
- * BUG: Ensure page headers are processed to add the in page header link when using the shortcodes.
298
- * BUG: Add forward slash to domain path in the plugin header.
299
- * I18N: Update POT file.
300
- * I18N: Update Dutch (nl_NL) translation.
301
-
302
- = 1.3 12/18/2017 =
303
- * FEATURE: Add support for the `[ez-toc]` shortcode.
304
- * NEW: For backwards compatibility with "Table of Content Plus", register the `[toc]` shortcode.
305
- * NEW: Introduce the `ez_toc_extract_headings_content` filter.
306
- * TWEAK: Update the tested to and required readme header text.
307
- * TWEAK: Do not show the widget on the 404, archive, search and posts pages.
308
- * I18N: Add the nl_NL translation.
309
-
310
- = 1.2 04/29/2016 =
311
- * TWEAK: Remove the font family from styling the TOC title header.
312
- * TWEAK: Pass the raw title to the `ez_toc_title` filter.
313
- * BUG: A jQuery 1.12 fix for WordPress 4.5.
314
-
315
- = 1.1 02/24/2016 =
316
- * FEATURE: Add option to replace header wither alternate header text in the table of content.
317
- * NEW: Introduce the ez_toc_filter.
318
- * NEW: Introduce ezTOC_Option::textarea() to render textareas.
319
- * NEW: Introduce array_search_deep() to recursively search an array for a value.
320
- * TWEAK: Run table of contents headers thru wp_kses_post().
321
- * TWEAK: Escape URL.
322
- * TWEAK: Count excluded headings only once instead of multiple times.
323
- * TWEAK: Escape translated string before rendering.
324
- * TWEAK: Use wp_unslash() instead of stripslashes().
325
- * TWEAK: Escape translated string.
326
- * BUG: Fix restrict path logic.
327
- * OTHER: Readme tweaks.
328
- * I18N: Add POT file.
329
- * I18N: Add Dutch translation.
330
- * DEV: Update .gitignore to allow PO files.
331
- * DEV: phpDoc fix.
332
-
333
- = 1.0 09/08/2015 =
334
- * Initial release.
335
- - Complete refactor and restructure of the original code for better design and separation of function to make code base much easier to maintain and extend.
336
- - Update all third party libraries.
337
- - Make much better use of the WordPress Settings API.
338
- - Minified CSS and JS files are used by default. Using SCRIPT_DEBUG will use the un-minified versions.
339
- - Add substantial amounts of phpDoc for developers.
340
- - Add many hooks to permit third party integrations.
341
- - Widget can be affixed/stuck to the page so it is always visible.
342
- - Widget will highlight the table of content sections that are currently visible in the browser viewport.
343
- - Widget will now generate table of contents using output from third party shortcodes.
344
- - Use wpColorPicker instead of farbtastic.
345
- - Remove all shortcodes.
346
- - Per post options are saved in post meta instead of set by shortcode.
347
-
348
- == Frequently Asked Questions ==
349
-
350
- = Ok, I've installed this... what do I do next? =
351
-
352
- You first stop should be the Table of Contents settings admin page. You can find this under the Settings menu item.
353
-
354
- You first and only required decision is you need to decide which post types you want to enable Table of Contents support for. By default it is the Pages post type. If on Pages is the only place you plan on using Table of Contents, you have nothing to do on the Settings page. To keep things simple, I recommend not changing any of the other settings at this point. Many of the other settings control when and where the table of contents is inserted and changing these settings could cause it not to display making getting started a bit more difficult. After you get comfortable with how this works... then tweak away :)
355
-
356
- With that out of the way make sure to read the **How are the tables of contents created?** FAQ so you know how the Table of Contents is automatically generated. After you have the page headers setup, or before, either way... Scroll down on the page you'll see a metabox named "*Table of Contents*", enable the *Insert table of contents.* option and Update and/or Publish you page. The table of contents should automatically be shown at the top of the page.
357
-
358
- = How are the tables of contents created? =
359
-
360
- The table of contents is generated by the headers found on a page. Headers are the [`<h1>,<h2>,<h3>,<h4>,<h5>,<h6>` HTML tags](http://www.w3schools.com/tags/tag_hn.asp). If you are using the WordPres Visual Post Editor, these header tags are used and inserted into the post when you select one of the [*Heading n* options from the formatting drop down](http://torquemag.io/wordpress-heading-tags/). Each header that is found on the page will create a table of content item. Here's an example which will create a table of contents containing the six items.
361
-
362
- `<h1>Item 1</h1>
363
- <h1>Item 2</h1>
364
- <h1>Item 3</h1>
365
- <h1>Item 4</h1>
366
- <h1>Item 5</h1>
367
- <h1>Item 6</h1>`
368
-
369
- You can also create "nested" table of contents. This is difficult to explain so I'll illustrate building on the previous example. In this example a table of contents will be created with the same six items but now the first three will each an child item nested underneath it. The indentation is not necessary, it was only added for illustration purposes.
370
-
371
- `<h1>Item 1</h1>
372
- <h2>Item 1.1 -- Level 2</h2>
373
- <h1>Item 2</h1>
374
- <h2>Item 2.1 -- Level 2</h2>
375
- <h1>Item 3</h1>
376
- <h2>Item 3.1 -- Level 2</h2>
377
- <h1>Item 4</h1>
378
- <h1>Item 5</h1>
379
- <h1>Item 6</h1>`
380
-
381
- You are not limited to a single a single nested item either. You can add as many as you need. You can even create multiple nested levels...
382
-
383
- `<h1>Item 1</h1>
384
- <h2>Item 1.1 -- Level 2</h2>
385
- <h3>Item 1.1.1 -- Level 3</h3>
386
- <h3>Item 1.1.2 -- Level 3</h3>
387
- <h3>Item 1.1.3 -- Level 3</h3>
388
- <h2>Item 1.2 -- Level 2</h2>
389
- <h3>Item 1.2.1 -- Level 3</h3>
390
- <h3>Item 1.2.2 -- Level 3</h3>
391
- <h3>Item 1.2.3 -- Level 3</h3>
392
- <h2>Item 1.3 -- Level 2</h2>
393
- <h1>Item 2</h1>
394
- <h2>Item 2.1 -- Level 2</h2>
395
- <h2>Item 2.2 -- Level 2</h2>
396
- <h1>Item 3</h1>
397
- <h2>Item 3.1 -- Level 2</h2>
398
- <h2>Item 3.2 -- Level 2</h2>
399
- <h1>Item 4</h1>
400
- <h1>Item 5</h1>
401
- <h1>Item 6</h1>`
402
-
403
- You can nest up 6 levels deep if needed. I hope this helps you understand how to create and build your own auto generated table of contents on your sites!
404
-
405
- == Upgrade Notice ==
406
-
407
- = 1.0 =
408
- Initial release.
409
-
410
- = 1.3 =
411
- Requires WordPress >= 4.4 and PHP >= 5.3. PHP version >= 7.1 recommended.
412
-
413
- = 1.4 =
414
- Requires WordPress >= 4.4 and PHP >= 5.3. PHP version >= 7.1 recommended.
415
-
416
- = 1.5 =
417
- Requires WordPress >= 4.4 and PHP >= 5.3. PHP version >= 7.1 recommended.
418
-
419
- = 1.6 =
420
- Requires WordPress >= 4.4 and PHP >= 5.3. PHP version >= 7.1 recommended.
421
-
422
- = 1.6.1 =
423
- Requires WordPress >= 4.4 and PHP >= 5.3. PHP version >= 7.1 recommended.
424
-
425
- = 1.7 =
426
- Requires WordPress >= 4.4 and PHP >= 5.3. PHP version >= 7.1 recommended.
427
-
428
- = 2.0-rc4 =
429
- Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
430
-
431
- = 2.0.1 =
432
- Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
433
-
434
- = 2.0.2 =
435
- Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
436
-
437
- = 2.0.3 =
438
- Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
439
-
440
- = 2.0.4 =
441
- Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
442
-
443
- = 2.0.5 =
444
- Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
445
-
446
- = 2.0.6 =
447
- Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
448
-
449
- = 2.0.7 =
450
- Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
451
-
452
- = 2.0.8 =
453
- Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
454
-
455
- = 2.0.9 =
456
- Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
457
-
458
- = 2.0.10 =
459
- Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
460
-
461
- = 2.0.11 =
462
- Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
463
-
464
- = 2.0.12 =
465
- Requires WordPress >= 5.3 and PHP version >= 5.6.20 (>= 7.4 is recommended).
466
-
467
- = 2.0.13 =
468
- Requires WordPress >= 5.3 and PHP version >= 5.6.20 (>= 7.4 is recommended).
469
-
470
- = 2.0.14 =
471
- Requires WordPress >= 5.3 and PHP version >= 5.6.20 (>= 7.4 is recommended).
472
-
473
- = 2.0.15 =
474
- Requires WordPress >= 5.3 and PHP version >= 5.6.20 (>= 7.4 is recommended).
475
-
476
- = 2.0.16 =
477
- Requires WordPress >= 5.3 and PHP version >= 5.6.20 (>= 7.4 is recommended).
478
-
479
- = 2.0.17 =
480
- Requires WordPress >= 5.3 and PHP version >= 5.6.20 (>= 7.4 is recommended).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Easy Table of Contents ===
2
+ Contributors: magazine3
3
+ Donate link: https://magazine3.company/
4
+ Tags: table of contents, toc
5
+ Requires at least: 5.3
6
+ Tested up to: 5.9.3
7
+ Requires PHP: 5.6.20
8
+ Stable tag: 2.0.22
9
+ License: GPLv2 or later
10
+ License URI: https://www.gnu.org/licenses/gpl-2.0.html
11
+
12
+ Adds a user friendly and fully automatic way to create and display a table of contents generated from the page content.
13
+
14
+ == Description ==
15
+
16
+ A user friendly, featured focused plugin which allows you to insert a table of contents into your posts, pages and custom post types.
17
+
18
+ ### Features
19
+ * Automatically generate a table of contents for your posts, pages and custom post types by parsing its contents for headers.
20
+ * Supports the `<!--nextpage-->` tag.
21
+ * Supports the Rank Math plugin.
22
+ * Works with the Classic Editor, Gutenberg, Divi, Elementor, WPBakery Page Builder and Visual Composer page editors.
23
+ * Optionally enable for pages and/or posts. Custom post types are supported, as long as their content is output with the `the_content()` template tag.
24
+ * Optionally auto insert the table of contents into the page, selectable by enabled post type.
25
+ * Provides many easy to understand options to configure when and where to insert the table of contents.
26
+ * Many options are available to configure how the inserted table of contents appears which include several builtin themes. If the supplied themes do no meet you needs, you can create your own by choosing you own colors for the border, background and link color.
27
+ * Multiple counter bullet formats to choose from; none, decimal, numeric and roman.
28
+ * Choose to display the table of contents hierarchical or not. This means headings of lower priority will be nested under headings of higher priority.
29
+ * User can optionally hide the table of contents. You full control of this feature. It can be disabled and you can choose to have it hidden by default.
30
+ * Supports smooth scrolling.
31
+ * Selectively enable or disabled the table of contents on a post by post basis.
32
+ * Choose which headings are used to generate the table of contents. This too can be set on a post by post basis.
33
+ * Easily exclude headers globally and on a post by post basis.
34
+ * If you rather not insert the table of contents in the post content, you can use the supplied widget and place the table of contents in your theme's sidebar.
35
+ * The widgets supports being affixed or stuck on the page so it is always visible as you scroll down the page. NOTE: this is an advanced option since every theme is different, you might need support from your theme developer to learn what the correct item selector to use in the settings to enable this feature.
36
+ * The widget auto highlights the sections currently visible on the page. The highlight color is configurable.
37
+ * Developer friendly with many action hooks and filters available. More can be added by request on [Github](https://github.com/shazahm1/Easy-Table-of-Contents). Pull requests are welcomed.
38
+
39
+ ### Support
40
+
41
+ We try our best to provide support on WordPress.org forums. However, We have a special [team support](https://magazine3.company/contact/) where you can ask us questions and get help. Delivering a good user experience means a lot to us and so we try our best to reply each and every question that gets asked.
42
+
43
+ ### Bug Reports
44
+
45
+ Bug reports for Easy Table of Contents are [welcomed on GitHub](https://github.com/ahmedkaludi/Easy-Table-of-Contents). Please note GitHub is not a support forum, and issues that aren't properly qualified as bugs will be closed.
46
+
47
+ ### [JOIN TELEGRAM GROUP COMMUNITY](https://t.me/+XADGN24lHNk0YjE1/)**: Purpose of this group is to get proper suggestions and feedback from plugin users and the community so that we can make the plugin even better.
48
+
49
+ ### Live Examples
50
+
51
+ * [cMap Template Docs](http://connections-pro.com/documentation/cmap/)
52
+ * [Circled Template Docs](http://connections-pro.com/documentation/circled/)
53
+ * [Gridder Template Docs](http://connections-pro.com/documentation/gridder/)
54
+
55
+ ### Roadmap
56
+ * Fragment caching for improved performance.
57
+ * Improve SEO by adding options to add nofollow to TOC link and wrap TOC nav in noindex tag.
58
+ * Improve accessibility.
59
+ * Add Bullet and Arrow options for list counter style.
60
+
61
+ ### Credit
62
+
63
+ Easy Table Contents is a fork of the excellent [Table of Contents Plus](https://wordpress.org/plugins/table-of-contents-plus/) plugin by [Michael Tran](http://dublue.com/plugins/toc/).
64
+
65
+ ### Screenshots
66
+
67
+ 1. The General section of the settings.
68
+ 2. The Appearance section of the settings.
69
+ 3. The Advanced section of the settings.
70
+
71
+ ### Installation
72
+
73
+ = Using the WordPress Plugin Search =
74
+
75
+ 1. Navigate to the `Add New` sub-page under the Plugins admin page.
76
+ 2. Search for `easy table of contents`.
77
+ 3. The plugin should be listed first in the search results.
78
+ 4. Click the `Install Now` link.
79
+ 5. Lastly click the `Activate Plugin` link to activate the plugin.
80
+
81
+ = Uploading in WordPress Admin =
82
+
83
+ 1. [Download the plugin zip file](http://wordpress.org/plugins/easy-table-of-contents/) and save it to your computer.
84
+ 2. Navigate to the `Add New` sub-page under the Plugins admin page.
85
+ 3. Click the `Upload` link.
86
+ 4. Select Easy Table of Contents zip file from where you saved the zip file on your computer.
87
+ 5. Click the `Install Now` button.
88
+ 6. Lastly click the `Activate Plugin` link to activate the plugin.
89
+
90
+ = Using FTP =
91
+
92
+ 1. [Download the plugin zip file](http://wordpress.org/plugins/easy-table-of-contents/) and save it to your computer.
93
+ 2. Extract the Easy Table of Contents zip file.
94
+ 3. Create a new directory named `easy-table-of-contents` directory in the `../wp-content/plugins/` directory.
95
+ 4. Upload the files from the folder extracted in Step 2.
96
+ 4. Activate the plugin on the Plugins admin page.
97
+
98
+ == Changelog ==
99
+
100
+ = 2.0.22 05/06/2022 =
101
+ * BUG: Double hyphens are getting removed from content issue fixed #163
102
+
103
+ = 2.0.21 05/06/2022 =
104
+ * BUG: Critical error fixed #147
105
+
106
+ = 2.0.20 05/05/2022 =
107
+ * TWEAK: Added Toggle with CSS for websites runs without jQuery #153
108
+ * TWEAK: Added telegram group join link for suggestions and feedback #159
109
+ * BUG: TOC links not jumping in some posts which have special characters #163
110
+ * BUG: Incorrect email ID updated in the plugin #165
111
+ * BUG: Proper documentation added for adding TOC with shortcodes & do_shortcode #152
112
+ * BUG: TOC links not working when do_shortcode added directly in the template #147
113
+ * BUG: TOC links not working with some specical character with Elementor #162
114
+
115
+ = 2.0.19 04/16/2022 =
116
+ * Bug Fixed : While Using Elementor Page builder TOC is not working when special characters are used in headings. #150
117
+ * Bug Fixed : Need to load CSS/JS files only on the selected post types. #154
118
+
119
+ = 2.0.18 03/29/2022 =
120
+ * TWEAK: Added Technical Support Tab in Settings Panel.
121
+
122
+ = 2.0.17 03/26/2021 =
123
+ * TWEAK: Add additional check to prevent `Uncaught Error: Call to undefined function is_woocommerce()`.
124
+ * TWEAK: Ensure an instance of `ezTOC_Post ` is returned before accessing methods/properties.
125
+
126
+ = 2.0.16 02/01/2021 =
127
+ * TWEAK: Remove special characters such as fancy quotes, en and, em dashes when generating in-page anchor IDs.
128
+
129
+ = 2.0.15 01/27/2021 =
130
+ * TWEAK: Remove additional reserved characters when generating in-page anchor IDs.
131
+
132
+ = 2.0.14 01/26/2021 =
133
+ * TWEAK: Refactor debug log as a Singleton.
134
+ * TWEAK: Add additional logging to aid in debugging.
135
+ * BUG: Correct logic for PHP where empty string no longer evaluates as integer `0`.
136
+
137
+ = 2.0.13 01/25/2021 =
138
+ * TWEAK: Restrict debug logging to when `WP_DEBUG` is enabled *and* current user capability of `manage_options`.
139
+ * TWEAK: Add logging to aid in support.
140
+ * DEV: phpDoc update.
141
+
142
+ = 2.0.12 01/22/2021 =
143
+ * TWEAK: Allow `_` and `-` in anchors.
144
+ * TWEAK: Minor CSS tweaks that prevent theme from breaking the layout.
145
+ * TWEAK: Minor tweak to class initialization.
146
+ * TWEAK: Do not display the view toggle if JavaScript is broken on the site.
147
+ * TWEAK: Add the ability to enable displaying of displaying debug information on the page.
148
+ * BUG: Check for array and keys before accessing values.
149
+ * BUG: Check for array key be fore access.
150
+ * BUG: Remove reserved characters when generating in-page anchor IDs.
151
+ * DEV: Remove unnecessary vendor library files.
152
+ * DEV: Deal with phpStorm showing a warning about path not found when including files.
153
+
154
+ = 2.0.11 05/01/2020 =
155
+ * COMPATIBILITY: Add support for the Uncode theme.
156
+ * COMPATIBILITY: Do not run on WooCommerce pages.
157
+ * DEV: Correct typo in phpDoc.
158
+
159
+ = 2.0.10 04/20/2020 =
160
+ * TWEAK: Add trailing `span` to heading, to prepare for `#` option and to fix duplicate heading title matching.
161
+ * TWEAK: Add second heading search/replace function to search for heading in content with heading html entities decoded. May help Beaver Builder users as it seems like it does not encode HTML entities as WP core does.
162
+
163
+
164
+ = 2.0.9 04/08/2020 =
165
+ * TWEAK: AMP/Caching plugins seems to break anchors with colons and periods even though they are valid characters for the id attribute in HTML5.
166
+ * TWEAK: Replace multiple underscores with a single underscore.
167
+ * DEV: Update the UWS library which fixes the deprecation notice for PHP 7.4.
168
+ * DEV: Add phpcs.xml.dist.
169
+ * DEV: Strict type checks.
170
+ * DEV: Inline doc updates.
171
+
172
+ = 2.0.8 04/03/2020 =
173
+ * TWEAK: Convert `<br />` tags in headings to a space.
174
+ * TWEAK: Add additional widget classes.
175
+ * TWEAK: Improve the sanitization of the excluded headings field post setting.
176
+ * TWEAK: Minor optimization of creating the matching pattern for excluding headings for improved performance.
177
+ * COMPATIBILITY: Exclude Create by Mediavine from heading eligibility.
178
+ * BUG: Ensure excluded headings are removed from the headings array.
179
+ * BUG: Ensure empty headings are removed from the headings array.
180
+
181
+ = 2.0.7 04/02/2020 =
182
+ * NEW: Exclude any HTML nodes with the class of `.ez-toc-exclude-headings`.
183
+ * TWEAK: Change smooth scroll selector from `'body a'` to `'a.ez-toc-link'`.
184
+ * TWEAK: Declare JS variables.
185
+ * TWEAK: Support unicode characters for the `id` attribute. Permitted by HTML5.
186
+ * TWEAK: Move the in-page anchor/span to before the heading text to account for long headings where it line wraps.
187
+ * TWEAK: Slight rework to ezTOC widget container classes logic.
188
+ * TWEAK: Cache bust the JS to make dev easier.
189
+ * TWEAK: JavaScript cleanup.
190
+ * TWEAK: URI Encode the id attribute to deal with reserved characters in JavaScript. Technically not necessary for the id attribute but needed to work with the jQuery smoothScroll library.
191
+ * COMPATIBILITY: Reintroduce filter to exclude Ultimate Addons for VC Composer Tabs from heading eligibility.
192
+ * BUG: Correct array iteration logic when processing headings.
193
+ * BUG: Tighten matching for headings in excluded HTML nodes. The loose matching was excluding far too many headings.
194
+ * BUG: Use `esc_attr()` instead of `esc_url()` for the anchor href because valid id attribute characters would cause it to return an empty href which cause a nonworking link.
195
+
196
+ = 2.0.6 03/30/2020 =
197
+ * BUG: Ensure minified files are current.
198
+
199
+ = 2.0.5 03/27/2020 =
200
+ * BUG: Prevent possible "strpos(): Empty needle in" warnings when excluding nodes from TOC eligibility.
201
+
202
+ = 2.0.4 03/16/2020 =
203
+ * NEW: Introduce the `ez_toc_container_class` filter.
204
+ * TWEAK: Slight rework to ezTOC container classes logic.
205
+ * BUG: `sprintf()` was eating `%` in the TOC heading item.
206
+ * BUG: Do not insert TOC at top of post if before first heading option is selected even if first heading can not be found. Some page builders cause the TOC to insert twice or on blog pages.
207
+
208
+ = 2.0.3 03/12/2020 =
209
+ * TWEAK: Slightly tighten heading matching, last update made it a little too loose.
210
+ * BUG: Correct logic required to place TOC before first heading which is required for the more lax heading matching required for page builders.
211
+
212
+ = 2.0.2 03/12/2020 =
213
+ * COMPATIBILITY: Remove filter to exclude Ultimate Addons for VC Composer Tabs from heading eligibility.
214
+ * COMPATIBILITY: Add additional filters to improve Elementor compatibility.
215
+ * TWEAK: Loosen heading matching when doing find/replace to insert in page links. Excluding the opening heading tag to allow matching heading where page builders dynamically add classes and id which break heading matching during find/replace.
216
+
217
+ = 2.0.1 03/09/2020 =
218
+ * COMPATIBILITY: Exclude the WordPress Related Posts plugin nodes.
219
+ * COMPATIBILITY: Exclude a couple Atomic Block plugin nodes.
220
+ * COMPATIBILITY: Exclude JetPack Related Posts from heading eligibility.
221
+ * COMPATIBILITY: Exclude Ultimate Addons for VC Composer Tabs from heading eligibility.
222
+ * COMPATIBILITY: Exclude WP Product Reviews from heading eligibility.
223
+ * TWEAK: Prevent possible "strpos(): Empty needle in" warnings when excluding nodes from TOC eligibility.
224
+
225
+ = 2.0 02/01/2020 =
226
+ * NEW: Major rewrite of all code and processing logic to make it faster and more reliable.
227
+ * NEW: Support for the <!--nextpage--> tag.
228
+ * NEW: Introduce helper functions for devs.
229
+ * NEW: Support WPML.
230
+ * NEW: Support Polylang.
231
+ * NEW: Add filter to support the Rank Math plugin.
232
+ * NEW: Introduce the `ez_toc_maybe_apply_the_content_filter` filter.
233
+ * TWEAK: Improve translation compatibility.
234
+ * TWEAK: Rework widget logic to allow multi-line TOC items, improve active item highlighting while removing the use of the jQuery Waypoints library.
235
+ * TWEAK Add additional classes to TOC list items.
236
+ * TWEAK: Add WOFF2 format for icon format and change font references in CSS.
237
+ * TWEAK: Add font-display: swap for toggle icon.
238
+ * TWEAK: Update JS Cookie to 2.2.1.
239
+ * TWEAK: Update jQuery Smooth Scroll to 2.2.0.
240
+ * TWEAK: Allow forward slash and angle brackets in headings and alternate headings.
241
+ * TWEAK: Allow forward slash in excluded headings.
242
+ * TWEAK: Remove new line/returns when matching excluded headings.
243
+ * TWEAK: Simple transient cache to ensure a post is only processed once per request for a TOC.
244
+ * TWEAK: Improve sanitization of alternate headings field value.
245
+ * TWEAK: Deal with non-breaking-spaces in alternate headings.
246
+ * TWEAK: Add the ability to exclude by selector content eligible to be included in the TOC.
247
+ * TWEAK: Change the shortcode priority to a higher value.
248
+ * TWEAK: Add filter to remove shortcodes from the content prior to the `the_content` filter being run to exclude shortcode content from being eligible as TOC items.
249
+ * TWEAK: Add compatibility filters to remove shortcodes for Connections and Striking theme to remove them from eligible TOC item content.
250
+ * TWEAK: Do not execute if root current filter is the `wp_head` or `get_the_excerpt` filters.
251
+ * TWEAK: Add filter to exclude content by selector.
252
+ * TWEAK: Move in-page anchor to after the heading instead of wrapping the heading to prevent conflicts with theme styling.
253
+ * TWEAK: Utilize the `ez_toc_exclude_by_selector` filter the exclude the JetPack share buttons from eligible headings.
254
+ * TWEAK: Remove the Elegant Themes Bloom plugin node from the post content before extracting headings.
255
+ * TWEAK: Add compatibility filter for the Visual Composer plugin.
256
+ * TWEAK: Utilize the `ez_toc_exclude_by_selector` filter the exclude the Starbox author heading from eligible headings.
257
+ * I18N: Add wpml-config.xml file.
258
+ * BUG: Correct option misspelling.
259
+ * BUG: Do not need to run values for alternate and exclude headings thru `wp_unslash()` because `update_post_meta()` already does.
260
+ * BUG: Do not need to run `stripslashes()` when escaping the alternate heading value.
261
+ * BUG: Sanitize the excluded heading string before saving post meta.
262
+ * DEV: Change PHP keywords to comply with PSR2.
263
+ * DEV:Bump minimum PHP version to 5.6.20 which matches WP core.
264
+
265
+ = 1.7 05/09/2018 =
266
+ * NEW: Introduce the `ez_toc_shortcode` filter.
267
+ * TWEAK: Fix notices due to late eligibility check. props unixtam
268
+ * TWEAK: Tweak eligibility check to support the TOC widget.
269
+ * TWEAK: Prefix a few CSS classes in order to prevent collisions with theme's and other plugins.
270
+ * TWEAK: Avoid potential PHP notice in admin when saving the post by checking for nonce before validating it.
271
+ * TWEAK: Using the shortcode now overrides global options.
272
+ * TWEAK: `the_content()` now caches result of `is_eligible()`.
273
+ * TWEAK: Refactor to pass the WP_Post object internally vs. accessing it via the `$wp_query->post` which may not in all cases exist.
274
+ * TWEAK: Use `pre_replace()` to replace one or more spaces with an underscore.
275
+ * TWEAK: Return original title in the `ez_toc_url_anchor_target` filter.
276
+ * TWEAK: Strip `&nbsp;`, replacing it with a space character.
277
+ * TWEAK: Minor tweaks to the in page URL creating.
278
+ * TWEAK: Wrap TOC list in a nav element.
279
+ * TWEAK: Init plugin on the `plugins_loaded` hook.
280
+ * TWEAK: Tweak the minimum number of headers to 1.
281
+ * BUG: The header options from the post meta should be used when building the TOC hierarchy, not the header options from the global settings.
282
+ * BUG: Do not double escape field values.
283
+ * BUG: Ensure Apostrophe / Single quote use in Exclude Headings work.
284
+ * OTHER: Update CSS to include the newly prefixed classes.
285
+ * DEV: Remove some commented out unused code.
286
+
287
+ = 1.6.1 03/16/2018 =
288
+ * TWEAK: Revert change made to allow HTML added via the `ez_toc_title` filter as it caused undesirable side effects.
289
+ * BUG: Ensure Smooth Scroll Offset is parsed as an integer.
290
+
291
+ = 1.6 03/15/2018 =
292
+ * NEW: Add `px` option for font size unit.
293
+ * NEW: Add title font size and weight settings options.
294
+ * NEW: Add the Mobile Smooth Scroll Offset option.
295
+ * TWEAK: Change default for font size unit from `px` to `%` to match the default options values.
296
+ * TWEAK: Correct CSS selector so margin is properly applied between the title and TOC items.
297
+ * TWEAK: Honor HTML added via `ez_toc_title` filter.
298
+ * TWEAK: Ensure the ezTOC content filter is not applied when running `the_content` filter.
299
+ * TWEAK: Only enqueue the javascript if the page is eligible for a TOC.
300
+ * TWEAK: Update icomoon CSS to remove unecessary CSS selectors to prevent possible conflicts.
301
+ * TWEAK: The smooth scroll offset needs to be taken into account when defining the offset_top property when affixing the widget.
302
+ * OTHER: Update frontend minified CSS file.
303
+ * OTHER: Update the frontend minified javascript file.
304
+ * DEV: phpDoc corrections.
305
+
306
+ = 1.5 02/20/2018 =
307
+ * BUG: Correct CSS selector to properly target the link color.
308
+ * OTHER: Update the WayPoints library.
309
+ * DEV: Add a couple @todo's.
310
+
311
+ = 1.4 01/29/2018 =
312
+ * TWEAK: Change text domain from ez_toc to easy-table-of-contents.
313
+ * TWEAK: Rename translation files with correct text domain.
314
+ * BUG: Ensure page headers are processed to add the in page header link when using the shortcodes.
315
+ * BUG: Add forward slash to domain path in the plugin header.
316
+ * I18N: Update POT file.
317
+ * I18N: Update Dutch (nl_NL) translation.
318
+
319
+ = 1.3 12/18/2017 =
320
+ * FEATURE: Add support for the `[ez-toc]` shortcode.
321
+ * NEW: For backwards compatibility with "Table of Content Plus", register the `[toc]` shortcode.
322
+ * NEW: Introduce the `ez_toc_extract_headings_content` filter.
323
+ * TWEAK: Update the tested to and required readme header text.
324
+ * TWEAK: Do not show the widget on the 404, archive, search and posts pages.
325
+ * I18N: Add the nl_NL translation.
326
+
327
+ = 1.2 04/29/2016 =
328
+ * TWEAK: Remove the font family from styling the TOC title header.
329
+ * TWEAK: Pass the raw title to the `ez_toc_title` filter.
330
+ * BUG: A jQuery 1.12 fix for WordPress 4.5.
331
+
332
+ = 1.1 02/24/2016 =
333
+ * FEATURE: Add option to replace header wither alternate header text in the table of content.
334
+ * NEW: Introduce the ez_toc_filter.
335
+ * NEW: Introduce ezTOC_Option::textarea() to render textareas.
336
+ * NEW: Introduce array_search_deep() to recursively search an array for a value.
337
+ * TWEAK: Run table of contents headers thru wp_kses_post().
338
+ * TWEAK: Escape URL.
339
+ * TWEAK: Count excluded headings only once instead of multiple times.
340
+ * TWEAK: Escape translated string before rendering.
341
+ * TWEAK: Use wp_unslash() instead of stripslashes().
342
+ * TWEAK: Escape translated string.
343
+ * BUG: Fix restrict path logic.
344
+ * OTHER: Readme tweaks.
345
+ * I18N: Add POT file.
346
+ * I18N: Add Dutch translation.
347
+ * DEV: Update .gitignore to allow PO files.
348
+ * DEV: phpDoc fix.
349
+
350
+ = 1.0 09/08/2015 =
351
+ * Initial release.
352
+ - Complete refactor and restructure of the original code for better design and separation of function to make code base much easier to maintain and extend.
353
+ - Update all third party libraries.
354
+ - Make much better use of the WordPress Settings API.
355
+ - Minified CSS and JS files are used by default. Using SCRIPT_DEBUG will use the un-minified versions.
356
+ - Add substantial amounts of phpDoc for developers.
357
+ - Add many hooks to permit third party integrations.
358
+ - Widget can be affixed/stuck to the page so it is always visible.
359
+ - Widget will highlight the table of content sections that are currently visible in the browser viewport.
360
+ - Widget will now generate table of contents using output from third party shortcodes.
361
+ - Use wpColorPicker instead of farbtastic.
362
+ - Remove all shortcodes.
363
+ - Per post options are saved in post meta instead of set by shortcode.
364
+
365
+ == Frequently Asked Questions ==
366
+
367
+ = Ok, I've installed this... what do I do next? =
368
+
369
+ You first stop should be the Table of Contents settings admin page. You can find this under the Settings menu item.
370
+
371
+ You first and only required decision is you need to decide which post types you want to enable Table of Contents support for. By default it is the Pages post type. If on Pages is the only place you plan on using Table of Contents, you have nothing to do on the Settings page. To keep things simple, I recommend not changing any of the other settings at this point. Many of the other settings control when and where the table of contents is inserted and changing these settings could cause it not to display making getting started a bit more difficult. After you get comfortable with how this works... then tweak away :)
372
+
373
+ With that out of the way make sure to read the **How are the tables of contents created?** FAQ so you know how the Table of Contents is automatically generated. After you have the page headers setup, or before, either way... Scroll down on the page you'll see a metabox named "*Table of Contents*", enable the *Insert table of contents.* option and Update and/or Publish you page. The table of contents should automatically be shown at the top of the page.
374
+
375
+ = How are the tables of contents created? =
376
+
377
+ The table of contents is generated by the headers found on a page. Headers are the [`<h1>,<h2>,<h3>,<h4>,<h5>,<h6>` HTML tags](http://www.w3schools.com/tags/tag_hn.asp). If you are using the WordPres Visual Post Editor, these header tags are used and inserted into the post when you select one of the [*Heading n* options from the formatting drop down](http://torquemag.io/wordpress-heading-tags/). Each header that is found on the page will create a table of content item. Here's an example which will create a table of contents containing the six items.
378
+
379
+ `<h1>Item 1</h1>
380
+ <h1>Item 2</h1>
381
+ <h1>Item 3</h1>
382
+ <h1>Item 4</h1>
383
+ <h1>Item 5</h1>
384
+ <h1>Item 6</h1>`
385
+
386
+ You can also create "nested" table of contents. This is difficult to explain so I'll illustrate building on the previous example. In this example a table of contents will be created with the same six items but now the first three will each an child item nested underneath it. The indentation is not necessary, it was only added for illustration purposes.
387
+
388
+ `<h1>Item 1</h1>
389
+ <h2>Item 1.1 -- Level 2</h2>
390
+ <h1>Item 2</h1>
391
+ <h2>Item 2.1 -- Level 2</h2>
392
+ <h1>Item 3</h1>
393
+ <h2>Item 3.1 -- Level 2</h2>
394
+ <h1>Item 4</h1>
395
+ <h1>Item 5</h1>
396
+ <h1>Item 6</h1>`
397
+
398
+ You are not limited to a single a single nested item either. You can add as many as you need. You can even create multiple nested levels...
399
+
400
+ `<h1>Item 1</h1>
401
+ <h2>Item 1.1 -- Level 2</h2>
402
+ <h3>Item 1.1.1 -- Level 3</h3>
403
+ <h3>Item 1.1.2 -- Level 3</h3>
404
+ <h3>Item 1.1.3 -- Level 3</h3>
405
+ <h2>Item 1.2 -- Level 2</h2>
406
+ <h3>Item 1.2.1 -- Level 3</h3>
407
+ <h3>Item 1.2.2 -- Level 3</h3>
408
+ <h3>Item 1.2.3 -- Level 3</h3>
409
+ <h2>Item 1.3 -- Level 2</h2>
410
+ <h1>Item 2</h1>
411
+ <h2>Item 2.1 -- Level 2</h2>
412
+ <h2>Item 2.2 -- Level 2</h2>
413
+ <h1>Item 3</h1>
414
+ <h2>Item 3.1 -- Level 2</h2>
415
+ <h2>Item 3.2 -- Level 2</h2>
416
+ <h1>Item 4</h1>
417
+ <h1>Item 5</h1>
418
+ <h1>Item 6</h1>`
419
+
420
+ You can nest up 6 levels deep if needed. I hope this helps you understand how to create and build your own auto generated table of contents on your sites!
421
+
422
+ = Is there any shortcode to add the table of content to anywhere I want ? =
423
+
424
+ Yes you can add the TOC with this shortcode – [ez-toc] and with the help of this you can easily add the TOC in the content or anywhere in the WordPress and if you want to add the shortcode on the theme file then you can add it with the help of this code – <?php echo do_shortcode( ‘[ez-toc]’ ); ?> and with this, you can add the TOC on any file according to your need.
425
+
426
+ == Upgrade Notice ==
427
+
428
+ = 1.0 =
429
+ Initial release.
430
+
431
+ = 1.3 =
432
+ Requires WordPress >= 4.4 and PHP >= 5.3. PHP version >= 7.1 recommended.
433
+
434
+ = 1.4 =
435
+ Requires WordPress >= 4.4 and PHP >= 5.3. PHP version >= 7.1 recommended.
436
+
437
+ = 1.5 =
438
+ Requires WordPress >= 4.4 and PHP >= 5.3. PHP version >= 7.1 recommended.
439
+
440
+ = 1.6 =
441
+ Requires WordPress >= 4.4 and PHP >= 5.3. PHP version >= 7.1 recommended.
442
+
443
+ = 1.6.1 =
444
+ Requires WordPress >= 4.4 and PHP >= 5.3. PHP version >= 7.1 recommended.
445
+
446
+ = 1.7 =
447
+ Requires WordPress >= 4.4 and PHP >= 5.3. PHP version >= 7.1 recommended.
448
+
449
+ = 2.0-rc4 =
450
+ Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
451
+
452
+ = 2.0.1 =
453
+ Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
454
+
455
+ = 2.0.2 =
456
+ Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
457
+
458
+ = 2.0.3 =
459
+ Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
460
+
461
+ = 2.0.4 =
462
+ Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
463
+
464
+ = 2.0.5 =
465
+ Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
466
+
467
+ = 2.0.6 =
468
+ Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
469
+
470
+ = 2.0.7 =
471
+ Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
472
+
473
+ = 2.0.8 =
474
+ Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
475
+
476
+ = 2.0.9 =
477
+ Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
478
+
479
+ = 2.0.10 =
480
+ Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
481
+
482
+ = 2.0.11 =
483
+ Requires WordPress >= 5.0 and PHP version >= 5.6.20 (>= 7.1 is recommended).
484
+
485
+ = 2.0.12 =
486
+ Requires WordPress >= 5.3 and PHP version >= 5.6.20 (>= 7.4 is recommended).
487
+
488
+ = 2.0.13 =
489
+ Requires WordPress >= 5.3 and PHP version >= 5.6.20 (>= 7.4 is recommended).
490
+
491
+ = 2.0.14 =
492
+ Requires WordPress >= 5.3 and PHP version >= 5.6.20 (>= 7.4 is recommended).
493
+
494
+ = 2.0.15 =
495
+ Requires WordPress >= 5.3 and PHP version >= 5.6.20 (>= 7.4 is recommended).
496
+
497
+ = 2.0.16 =
498
+ Requires WordPress >= 5.3 and PHP version >= 5.6.20 (>= 7.4 is recommended).
499
+
500
+ = 2.0.17 =
501
+ Requires WordPress >= 5.3 and PHP version >= 5.6.20 (>= 7.4 is recommended).
assets/css/admin.css CHANGED
@@ -1,222 +1,222 @@
1
- div.tab_content table {
2
- margin-bottom: 1em;
3
- }
4
- table.more_toc_options_table th, table.more_toc_options_table td {
5
- padding: 0;
6
- margin: 0;
7
- }
8
- table.more_toc_options_table th {
9
- width: auto;
10
- padding-right: 4px;
11
- padding-top: 2px;
12
- }
13
- div.tab_content ul li {
14
- margin-left: 2em;
15
- list-style-type: disc;
16
- }
17
- div.tab_content ol li {
18
- list-style: inherit;
19
- }
20
- div.tab_content pre {
21
- margin-left: 2em;
22
- }
23
- ul#tabbed-nav {
24
- margin-top: 1em;
25
- }
26
- #tabbed-nav {
27
- margin: 0;
28
- padding: 0;
29
- float: left;
30
- list-style: none;
31
- height: 32px;
32
- border-bottom: 1px solid #DFDFDF;
33
- border-left: 1px solid #DFDFDF;
34
- width: 100%;
35
- }
36
- #tabbed-nav li {
37
- float: left;
38
- margin: 0;
39
- padding: 0;
40
- height: 31px;
41
- line-height: 31px;
42
- border: 1px solid #DFDFDF;
43
- border-left: none;
44
- margin-bottom: -1px;
45
- overflow: hidden;
46
- position: relative;
47
- background: #F5F5F5;
48
- background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#F5F5F5));
49
- background-image: -webkit-linear-gradient(center top, #fff, #F5F5F5);
50
- background-image: -moz-linear-gradient(center top, #fff, #F5F5F5);
51
- background-image: -ms-linear-gradient(center top, #fff, #F5F5F5);
52
- background-image: -o-linear-gradient(center top, #fff, #F5F5F5);
53
- background-image: linear-gradient(center top, #fff, #F5F5F5);
54
- }
55
- #tabbed-nav li a {
56
- text-decoration: none;
57
- color: #000;
58
- display: block;
59
- font-size: 1.2em;
60
- padding: 0 20px;
61
- border: 1px solid #fff;
62
- outline: none;
63
- }
64
- #tabbed-nav li a:hover {
65
- background: #ECECEC;
66
- }
67
- html #tabbed-nav li.active, html #tabbed-nav li.active a:hover {
68
- background: #fff;
69
- border-bottom: 1px solid #fff;
70
- }
71
- div.tab_container {
72
- border: 1px solid #DFDFDF;
73
- border-top: none;
74
- overflow: hidden;
75
- clear: both;
76
- float: left; width: 100%;
77
- background: #fff;
78
- margin-bottom: 2em;
79
- padding-bottom: 2em;
80
- }
81
- div.tab_content {
82
- padding: 10px;
83
- padding-bottom: 0;
84
- font-size: 1em;
85
- }
86
- h3 span.show_hide {
87
- font-size: 0.85em;
88
- font-weight: normal;
89
- }
90
- div.more_toc_options {
91
- margin-top: 4px;
92
- margin-left: 2em;
93
- }
94
- div.toc_theme_option {
95
- width: 200px;
96
- float: left;
97
- margin-right: 5px;
98
- }
99
- #wpcontent select optgroup option {
100
- padding-left: 15px;
101
- }
102
- input#width_custom,
103
- input#font_size,
104
- input#smooth_scroll_offset {
105
- width: 50px;
106
- text-align: center;
107
- }
108
- input.custom_colour_option {
109
- width: 75px;
110
- }
111
- table#theme_custom, div#farbtastic_colour_wheel {
112
- float: left;
113
- }
114
- table#theme_custom {
115
- margin-top: 30px;
116
- }
117
- table#theme_custom img {
118
- vertical-align: middle;
119
- opacity: 0.4;
120
- }
121
- table#theme_custom img:hover {
122
- cursor: pointer;
123
- opacity: 1;
124
- }
125
- div#farbtastic_colour_wheel {
126
- margin-left: 20px;
127
- }
128
- #tab3 h3:not(:first-child) {
129
- margin-top: 2em;
130
- }
131
-
132
- /* My styles */
133
- #toc input.small-text {
134
- width: 50px;
135
- padding: 2px;
136
- height: 28px;
137
- line-height: 28px;
138
- vertical-align: bottom;
139
- }
140
-
141
- #toc .form-table tr > th > strong {
142
- font-size: 18px;
143
- font-style: italic;
144
- }
145
- /* Tab panel styles */
146
- .postbox{
147
- padding: 10px;
148
- }
149
- html {
150
- scroll-behavior: smooth;
151
- }
152
- .toc-tab-panel {
153
- overflow: hidden;
154
- border: 1px solid #ccc;
155
- background-color: #fff;
156
- margin-top: 15px;}
157
- .toc-tab-panel a {
158
- background-color: inherit;
159
- text-decoration: none;
160
- float: left;
161
- border: none;
162
- outline: none;
163
- cursor: pointer;
164
- padding: 14px 16px;
165
- transition: 0s;
166
- font-size: 15px;
167
- color: #2271b1;
168
- }
169
- .toc-tab-panel a:hover {
170
- color: #0a4b78;
171
- }
172
- .toc-tab-panel a.active {
173
- box-shadow: none;
174
- border-bottom: 4px solid #646970;
175
- color: #1d2327;
176
- }
177
- .toc-tab-panel a:focus {
178
- box-shadow: none;
179
- outline: none;
180
- }
181
- .eztoc-tabcontent {
182
- display: none;
183
- border-top: none;
184
- animation: fadeEffect 1s;
185
- }
186
- .eztoc_support_div {
187
- background: #fff;
188
- margin-top: 10px;
189
- padding: 28px;
190
- min-width: 255px;
191
- border: 1px solid #c3c4c7;
192
- box-shadow: 0 1px 1px rgb(0 0 0 / 4%);
193
- }
194
- .support-label {
195
- float: left;
196
- width: 70px;
197
- font-size: 14px;
198
- }
199
- .star-mark{
200
- color: red;
201
- margin-left: 4px;
202
- font-family:bold;
203
- }
204
- .eztoc_support_div li {
205
- margin:25px 0px 20px 0px;
206
- }
207
- .eztoc-query-success{
208
- color: green;
209
- }
210
- .eztoc-query-error{
211
- color: red;
212
- }
213
- .eztoc_hide{
214
- display: none;
215
- }
216
- .eztoc-result{
217
- margin-left: 70px;
218
- }
219
- .eztoc-send-query{
220
- margin-left: 70px !important;;
221
- }
222
- @keyframes fadeEffect { from {opacity: 0;} to {opacity: 1;} }
1
+ div.tab_content table {
2
+ margin-bottom: 1em;
3
+ }
4
+ table.more_toc_options_table th, table.more_toc_options_table td {
5
+ padding: 0;
6
+ margin: 0;
7
+ }
8
+ table.more_toc_options_table th {
9
+ width: auto;
10
+ padding-right: 4px;
11
+ padding-top: 2px;
12
+ }
13
+ div.tab_content ul li {
14
+ margin-left: 2em;
15
+ list-style-type: disc;
16
+ }
17
+ div.tab_content ol li {
18
+ list-style: inherit;
19
+ }
20
+ div.tab_content pre {
21
+ margin-left: 2em;
22
+ }
23
+ ul#tabbed-nav {
24
+ margin-top: 1em;
25
+ }
26
+ #tabbed-nav {
27
+ margin: 0;
28
+ padding: 0;
29
+ float: left;
30
+ list-style: none;
31
+ height: 32px;
32
+ border-bottom: 1px solid #DFDFDF;
33
+ border-left: 1px solid #DFDFDF;
34
+ width: 100%;
35
+ }
36
+ #tabbed-nav li {
37
+ float: left;
38
+ margin: 0;
39
+ padding: 0;
40
+ height: 31px;
41
+ line-height: 31px;
42
+ border: 1px solid #DFDFDF;
43
+ border-left: none;
44
+ margin-bottom: -1px;
45
+ overflow: hidden;
46
+ position: relative;
47
+ background: #F5F5F5;
48
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#F5F5F5));
49
+ background-image: -webkit-linear-gradient(center top, #fff, #F5F5F5);
50
+ background-image: -moz-linear-gradient(center top, #fff, #F5F5F5);
51
+ background-image: -ms-linear-gradient(center top, #fff, #F5F5F5);
52
+ background-image: -o-linear-gradient(center top, #fff, #F5F5F5);
53
+ background-image: linear-gradient(center top, #fff, #F5F5F5);
54
+ }
55
+ #tabbed-nav li a {
56
+ text-decoration: none;
57
+ color: #000;
58
+ display: block;
59
+ font-size: 1.2em;
60
+ padding: 0 20px;
61
+ border: 1px solid #fff;
62
+ outline: none;
63
+ }
64
+ #tabbed-nav li a:hover {
65
+ background: #ECECEC;
66
+ }
67
+ html #tabbed-nav li.active, html #tabbed-nav li.active a:hover {
68
+ background: #fff;
69
+ border-bottom: 1px solid #fff;
70
+ }
71
+ div.tab_container {
72
+ border: 1px solid #DFDFDF;
73
+ border-top: none;
74
+ overflow: hidden;
75
+ clear: both;
76
+ float: left; width: 100%;
77
+ background: #fff;
78
+ margin-bottom: 2em;
79
+ padding-bottom: 2em;
80
+ }
81
+ div.tab_content {
82
+ padding: 10px;
83
+ padding-bottom: 0;
84
+ font-size: 1em;
85
+ }
86
+ h3 span.show_hide {
87
+ font-size: 0.85em;
88
+ font-weight: normal;
89
+ }
90
+ div.more_toc_options {
91
+ margin-top: 4px;
92
+ margin-left: 2em;
93
+ }
94
+ div.toc_theme_option {
95
+ width: 200px;
96
+ float: left;
97
+ margin-right: 5px;
98
+ }
99
+ #wpcontent select optgroup option {
100
+ padding-left: 15px;
101
+ }
102
+ input#width_custom,
103
+ input#font_size,
104
+ input#smooth_scroll_offset {
105
+ width: 50px;
106
+ text-align: center;
107
+ }
108
+ input.custom_colour_option {
109
+ width: 75px;
110
+ }
111
+ table#theme_custom, div#farbtastic_colour_wheel {
112
+ float: left;
113
+ }
114
+ table#theme_custom {
115
+ margin-top: 30px;
116
+ }
117
+ table#theme_custom img {
118
+ vertical-align: middle;
119
+ opacity: 0.4;
120
+ }
121
+ table#theme_custom img:hover {
122
+ cursor: pointer;
123
+ opacity: 1;
124
+ }
125
+ div#farbtastic_colour_wheel {
126
+ margin-left: 20px;
127
+ }
128
+ #tab3 h3:not(:first-child) {
129
+ margin-top: 2em;
130
+ }
131
+
132
+ /* My styles */
133
+ #toc input.small-text {
134
+ width: 50px;
135
+ padding: 2px;
136
+ height: 28px;
137
+ line-height: 28px;
138
+ vertical-align: bottom;
139
+ }
140
+
141
+ #toc .form-table tr > th > strong {
142
+ font-size: 18px;
143
+ font-style: italic;
144
+ }
145
+ /* Tab panel styles */
146
+ .postbox{
147
+ padding: 10px;
148
+ }
149
+ html {
150
+ scroll-behavior: smooth;
151
+ }
152
+ .toc-tab-panel {
153
+ overflow: hidden;
154
+ border: 1px solid #ccc;
155
+ background-color: #fff;
156
+ margin-top: 15px;}
157
+ .toc-tab-panel a {
158
+ background-color: inherit;
159
+ text-decoration: none;
160
+ float: left;
161
+ border: none;
162
+ outline: none;
163
+ cursor: pointer;
164
+ padding: 14px 16px;
165
+ transition: 0s;
166
+ font-size: 15px;
167
+ color: #2271b1;
168
+ }
169
+ .toc-tab-panel a:hover {
170
+ color: #0a4b78;
171
+ }
172
+ .toc-tab-panel a.active {
173
+ box-shadow: none;
174
+ border-bottom: 4px solid #646970;
175
+ color: #1d2327;
176
+ }
177
+ .toc-tab-panel a:focus {
178
+ box-shadow: none;
179
+ outline: none;
180
+ }
181
+ .eztoc-tabcontent {
182
+ display: none;
183
+ border-top: none;
184
+ animation: fadeEffect 1s;
185
+ }
186
+ .eztoc_support_div {
187
+ background: #fff;
188
+ margin-top: 10px;
189
+ padding: 28px;
190
+ min-width: 255px;
191
+ border: 1px solid #c3c4c7;
192
+ box-shadow: 0 1px 1px rgb(0 0 0 / 4%);
193
+ }
194
+ .support-label {
195
+ float: left;
196
+ width: 70px;
197
+ font-size: 14px;
198
+ }
199
+ .star-mark{
200
+ color: red;
201
+ margin-left: 4px;
202
+ font-family:bold;
203
+ }
204
+ .eztoc_support_div li {
205
+ margin:25px 0px 20px 0px;
206
+ }
207
+ .eztoc-query-success{
208
+ color: green;
209
+ }
210
+ .eztoc-query-error{
211
+ color: red;
212
+ }
213
+ .eztoc_hide{
214
+ display: none;
215
+ }
216
+ .eztoc-result{
217
+ margin-left: 70px;
218
+ }
219
+ .eztoc-send-query{
220
+ margin-left: 70px !important;;
221
+ }
222
+ @keyframes fadeEffect { from {opacity: 0;} to {opacity: 1;} }
assets/css/screen.css CHANGED
@@ -1,347 +1,374 @@
1
- #ez-toc-container {
2
- background: #F9F9F9;
3
- border: 1px solid #AAAAAA;
4
- border-radius: 4px;
5
- -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
6
- box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
7
- display: table;
8
- /*font-size: 95%;*/
9
- margin-bottom: 1em;
10
- padding: 10px;
11
- position: relative;
12
- width: auto;
13
- }
14
-
15
- div.ez-toc-widget-container {
16
- padding: 0;
17
- position: relative;
18
- }
19
-
20
- #ez-toc-container.ez-toc-light-blue {
21
- background: #EDF6FF;
22
- }
23
-
24
- #ez-toc-container.ez-toc-white {
25
- background: #FFFFFF;
26
- }
27
-
28
- #ez-toc-container.ez-toc-black {
29
- background: #000000;
30
- }
31
-
32
- #ez-toc-container.ez-toc-transparent {
33
- background: none transparent;
34
- }
35
-
36
- div.ez-toc-widget-container ul {
37
- display: block;
38
- }
39
-
40
- div.ez-toc-widget-container li {
41
- border: none;
42
- padding: 0;
43
- }
44
-
45
- div.ez-toc-widget-container ul.ez-toc-list {
46
- padding: 10px;
47
- }
48
-
49
- #ez-toc-container ul ul,
50
- .ez-toc div.ez-toc-widget-container ul ul {
51
- margin-left: 1.5em;
52
- }
53
-
54
- #ez-toc-container ul,
55
- #ez-toc-container li {
56
- margin: 0;
57
- padding: 0;
58
- }
59
-
60
- #ez-toc-container ul,
61
- #ez-toc-container li,
62
- #ez-toc-container ul li,
63
- div.ez-toc-widget-container,
64
- div.ez-toc-widget-container li {
65
- background: none;
66
- list-style: none none;
67
- line-height: 1.6;
68
- margin: 0;
69
- overflow: hidden;
70
- z-index: 1;
71
- }
72
-
73
- /*#ez-toc-container.have_bullets li {*/
74
- /*padding-left: 12px;*/
75
- /*}*/
76
-
77
- #ez-toc-container p.ez-toc-title {
78
- text-align: left;
79
- /*font-family: "Arial Narrow", sans-serif;*/
80
- /*font-size: 120%;*/
81
- /*font-weight: 500;*/
82
- line-height: 1.45;
83
- margin: 0;
84
- padding: 0;
85
- }
86
-
87
- .ez-toc-title-container {
88
- display: table;
89
- width: 100%;
90
- }
91
-
92
- .ez-toc-title,
93
- .ez-toc-title-toggle {
94
- display: table-cell;
95
- text-align: left;
96
- vertical-align: middle;
97
- }
98
-
99
- #ez-toc-container.ez-toc-black p.ez-toc-title {
100
- color: #FFF;
101
- }
102
-
103
- /*#ez-toc-container span.ez-toc-toggle {*/
104
- /*font-weight: 400;*/
105
- /*font-size: 90%;*/
106
- /*}*/
107
-
108
- #ez-toc-container div.ez-toc-title-container + ul.ez-toc-list {
109
- margin-top: 1em;
110
- }
111
-
112
- .ez-toc-wrap-left {
113
- float: left;
114
- margin-right: 10px;
115
- }
116
-
117
- .ez-toc-wrap-right {
118
- float: right;
119
- margin-left: 10px;
120
- }
121
-
122
- #ez-toc-container a {
123
- color: #444444;
124
- box-shadow: none;
125
- text-decoration: none;
126
- text-shadow: none;
127
- }
128
-
129
- #ez-toc-container a:visited {
130
- color: #9f9f9f;
131
- }
132
-
133
- #ez-toc-container a:hover {
134
- text-decoration: underline;
135
- }
136
-
137
- #ez-toc-container.ez-toc-black a {
138
- color: #FFF;
139
- }
140
-
141
- #ez-toc-container.ez-toc-black a:visited {
142
- color: #FFF;
143
- }
144
-
145
- #ez-toc-container a.ez-toc-toggle {
146
- color: #444444;
147
- }
148
-
149
- #ez-toc-container.counter-hierarchy ul,
150
- .ez-toc-widget-container.counter-hierarchy ul,
151
- #ez-toc-container.counter-flat ul,
152
- .ez-toc-widget-container.counter-flat ul {
153
- counter-reset: item;
154
- }
155
-
156
- #ez-toc-container.counter-numeric li,
157
- .ez-toc-widget-container.counter-numeric li {
158
- list-style-type: decimal;
159
- list-style-position: inside;
160
- }
161
-
162
- #ez-toc-container.counter-decimal ul.ez-toc-list li a::before,
163
- .ez-toc-widget-container.counter-decimal ul.ez-toc-list li a::before {
164
- content: counters(item, ".") ". ";
165
- display: inline-block;
166
- counter-increment: item;
167
- margin-right: .2em;
168
- }
169
-
170
- #ez-toc-container.counter-roman li a::before,
171
- .ez-toc-widget-container.counter-roman ul.ez-toc-list li a::before {
172
- content: counters(item, ".", upper-roman) ". ";
173
- counter-increment: item;
174
- }
175
-
176
- .ez-toc-widget-container ul.ez-toc-list li::before {
177
- content: ' ';
178
- position: absolute;
179
- left: 0;
180
- right: 0;
181
- height: 30px;
182
- line-height: 30px;
183
- z-index: -1;
184
- }
185
-
186
- .ez-toc-widget-container ul.ez-toc-list li.active::before {
187
- background-color: #EDEDED;
188
- }
189
-
190
- .ez-toc-widget-container li.active > a {
191
- font-weight: 900;
192
- }
193
-
194
- .ez-toc-btn {
195
- display: inline-block;
196
- padding: 6px 12px;
197
- margin-bottom: 0;
198
- font-size: 14px;
199
- font-weight: normal;
200
- line-height: 1.428571429;
201
- text-align: center;
202
- white-space: nowrap;
203
- vertical-align: middle;
204
- cursor: pointer;
205
- background-image: none;
206
- border: 1px solid transparent;
207
- border-radius: 4px;
208
- -webkit-user-select: none;
209
- -moz-user-select: none;
210
- -ms-user-select: none;
211
- -o-user-select: none;
212
- user-select: none
213
- }
214
-
215
- .ez-toc-btn:focus {
216
- outline: thin dotted #333;
217
- outline: 5px auto -webkit-focus-ring-color;
218
- outline-offset: -2px
219
- }
220
-
221
- .ez-toc-btn:hover,.ez-toc-btn:focus {
222
- color: #333;
223
- text-decoration: none
224
- }
225
-
226
- .ez-toc-btn:active,.ez-toc-btn.active {
227
- background-image: none;
228
- outline: 0;
229
- -webkit-box-shadow: inset 0 3px 5px rgba(0,0,0,0.125);
230
- box-shadow: inset 0 3px 5px rgba(0,0,0,0.125)
231
- }
232
-
233
- .ez-toc-btn-default {
234
- color: #333;
235
- background-color: #fff;
236
- border-color: #ccc
237
- }
238
-
239
- .ez-toc-btn-default:hover,.ez-toc-btn-default:focus,.ez-toc-btn-default:active,.ez-toc-btn-default.active {
240
- color: #333;
241
- background-color: #ebebeb;
242
- border-color: #adadad
243
- }
244
-
245
- .ez-toc-btn-default:active,.ez-toc-btn-default.active {
246
- background-image: none
247
- }
248
-
249
- /*.btn-lg {*/
250
- /*padding: 10px 16px;*/
251
- /*font-size: 18px;*/
252
- /*line-height: 1.33;*/
253
- /*border-radius: 6px*/
254
- /*}*/
255
-
256
- .ez-toc-btn-sm,.ez-toc-btn-xs {
257
- padding: 5px 10px;
258
- font-size: 12px;
259
- line-height: 1.5;
260
- border-radius: 3px
261
- }
262
-
263
- .ez-toc-btn-xs {
264
- padding: 1px 5px
265
- }
266
-
267
- .ez-toc-btn-default {
268
- text-shadow: 0 -1px 0 rgba(0,0,0,0.2);
269
- -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);
270
- box-shadow: inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)
271
- }
272
-
273
- .ez-toc-btn-default:active {
274
- -webkit-box-shadow: inset 0 3px 5px rgba(0,0,0,0.125);
275
- box-shadow: inset 0 3px 5px rgba(0,0,0,0.125)
276
- }
277
-
278
- .ez-toc-btn:active,.btn.active {
279
- background-image: none
280
- }
281
-
282
- .ez-toc-btn-default {
283
- text-shadow: 0 1px 0 #fff;
284
- background-image: -webkit-gradient(linear,left 0,left 100%,from(#fff),to(#e0e0e0));
285
- background-image: -webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);
286
- background-image: -moz-linear-gradient(top,#fff 0,#e0e0e0 100%);
287
- background-image: linear-gradient(to bottom,#fff 0,#e0e0e0 100%);
288
- background-repeat: repeat-x;
289
- border-color: #dbdbdb;
290
- border-color: #ccc;
291
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe0e0e0',GradientType=0);
292
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false)
293
- }
294
-
295
- .ez-toc-btn-default:hover,.ez-toc-btn-default:focus {
296
- background-color: #e0e0e0;
297
- background-position: 0 -15px
298
- }
299
-
300
- .ez-toc-btn-default:active,.ez-toc-btn-default.active {
301
- background-color: #e0e0e0;
302
- border-color: #dbdbdb
303
- }
304
-
305
- .ez-toc-pull-right {
306
- float: right !important;
307
- margin-left: 10px;
308
- }
309
-
310
- .ez-toc-glyphicon {
311
- position: relative;
312
- top: 1px;
313
- display: inline-block;
314
- font-family: 'Glyphicons Halflings';
315
- -webkit-font-smoothing: antialiased;
316
- font-style: normal;
317
- font-weight: normal;
318
- line-height: 1;
319
- -moz-osx-font-smoothing: grayscale
320
- }
321
-
322
- .ez-toc-glyphicon:empty {
323
- width: 1em
324
- }
325
-
326
- .ez-toc-toggle i.ez-toc-glyphicon {
327
- font-size: 16px;
328
- margin-left: 2px;
329
- }
330
-
331
- [class*="ez-toc-icon-"] {
332
- font-family: 'ez-toc-icomoon' !important; /* For better glyphicon compatibility */
333
- speak: none;
334
- font-style: normal;
335
- font-weight: normal;
336
- font-variant: normal;
337
- text-transform: none;
338
- line-height: 1;
339
-
340
- /* Better Font Rendering =========== */
341
- -webkit-font-smoothing: antialiased;
342
- -moz-osx-font-smoothing: grayscale;
343
- }
344
-
345
- .ez-toc-icon-toggle:before {
346
- content: "\e87a";
347
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #ez-toc-container {
2
+ background: #F9F9F9;
3
+ border: 1px solid #AAAAAA;
4
+ border-radius: 4px;
5
+ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
6
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
7
+ display: table;
8
+ /*font-size: 95%;*/
9
+ margin-bottom: 1em;
10
+ padding: 10px;
11
+ position: relative;
12
+ width: auto;
13
+ }
14
+
15
+ div.ez-toc-widget-container {
16
+ padding: 0;
17
+ position: relative;
18
+ }
19
+
20
+ #ez-toc-container.ez-toc-light-blue {
21
+ background: #EDF6FF;
22
+ }
23
+
24
+ #ez-toc-container.ez-toc-white {
25
+ background: #FFFFFF;
26
+ }
27
+
28
+ #ez-toc-container.ez-toc-black {
29
+ background: #000000;
30
+ }
31
+
32
+ #ez-toc-container.ez-toc-transparent {
33
+ background: none transparent;
34
+ }
35
+
36
+ div.ez-toc-widget-container ul {
37
+ display: block;
38
+ }
39
+
40
+ div.ez-toc-widget-container li {
41
+ border: none;
42
+ padding: 0;
43
+ }
44
+
45
+ div.ez-toc-widget-container ul.ez-toc-list {
46
+ padding: 10px;
47
+ }
48
+
49
+ #ez-toc-container ul ul,
50
+ .ez-toc div.ez-toc-widget-container ul ul {
51
+ margin-left: 1.5em;
52
+ }
53
+
54
+ #ez-toc-container ul,
55
+ #ez-toc-container li {
56
+ margin: 0;
57
+ padding: 0;
58
+ }
59
+
60
+ #ez-toc-container ul,
61
+ #ez-toc-container li,
62
+ #ez-toc-container ul li,
63
+ div.ez-toc-widget-container,
64
+ div.ez-toc-widget-container li {
65
+ background: none;
66
+ list-style: none none;
67
+ line-height: 1.6;
68
+ margin: 0;
69
+ overflow: hidden;
70
+ z-index: 1;
71
+ }
72
+
73
+ /*#ez-toc-container.have_bullets li {*/
74
+ /*padding-left: 12px;*/
75
+ /*}*/
76
+
77
+ #ez-toc-container p.ez-toc-title {
78
+ text-align: left;
79
+ /*font-family: "Arial Narrow", sans-serif;*/
80
+ /*font-size: 120%;*/
81
+ /*font-weight: 500;*/
82
+ line-height: 1.45;
83
+ margin: 0;
84
+ padding: 0;
85
+ }
86
+
87
+ .ez-toc-title-container {
88
+ display: table;
89
+ width: 100%;
90
+ }
91
+
92
+ .ez-toc-title,
93
+ .ez-toc-title-toggle {
94
+ display: table-cell;
95
+ text-align: left;
96
+ vertical-align: middle;
97
+ }
98
+
99
+ #ez-toc-container.ez-toc-black p.ez-toc-title {
100
+ color: #FFF;
101
+ }
102
+
103
+ /*#ez-toc-container span.ez-toc-toggle {*/
104
+ /*font-weight: 400;*/
105
+ /*font-size: 90%;*/
106
+ /*}*/
107
+
108
+ #ez-toc-container div.ez-toc-title-container + ul.ez-toc-list {
109
+ margin-top: 1em;
110
+ }
111
+
112
+ .ez-toc-wrap-left {
113
+ float: left;
114
+ margin-right: 10px;
115
+ }
116
+
117
+ .ez-toc-wrap-right {
118
+ float: right;
119
+ margin-left: 10px;
120
+ }
121
+
122
+ #ez-toc-container a {
123
+ color: #444444;
124
+ box-shadow: none;
125
+ text-decoration: none;
126
+ text-shadow: none;
127
+ }
128
+
129
+ #ez-toc-container a:visited {
130
+ color: #9f9f9f;
131
+ }
132
+
133
+ #ez-toc-container a:hover {
134
+ text-decoration: underline;
135
+ }
136
+
137
+ #ez-toc-container.ez-toc-black a {
138
+ color: #FFF;
139
+ }
140
+
141
+ #ez-toc-container.ez-toc-black a:visited {
142
+ color: #FFF;
143
+ }
144
+
145
+ #ez-toc-container a.ez-toc-toggle {
146
+ color: #444444;
147
+ }
148
+
149
+ #ez-toc-container.counter-hierarchy ul,
150
+ .ez-toc-widget-container.counter-hierarchy ul,
151
+ #ez-toc-container.counter-flat ul,
152
+ .ez-toc-widget-container.counter-flat ul {
153
+ counter-reset: item;
154
+ }
155
+
156
+ #ez-toc-container.counter-numeric li,
157
+ .ez-toc-widget-container.counter-numeric li {
158
+ list-style-type: decimal;
159
+ list-style-position: inside;
160
+ }
161
+
162
+ #ez-toc-container.counter-decimal ul.ez-toc-list li a::before,
163
+ .ez-toc-widget-container.counter-decimal ul.ez-toc-list li a::before {
164
+ content: counters(item, ".") ". ";
165
+ display: inline-block;
166
+ counter-increment: item;
167
+ margin-right: .2em;
168
+ }
169
+
170
+ #ez-toc-container.counter-roman li a::before,
171
+ .ez-toc-widget-container.counter-roman ul.ez-toc-list li a::before {
172
+ content: counters(item, ".", upper-roman) ". ";
173
+ counter-increment: item;
174
+ }
175
+
176
+ .ez-toc-widget-container ul.ez-toc-list li::before {
177
+ content: ' ';
178
+ position: absolute;
179
+ left: 0;
180
+ right: 0;
181
+ height: 30px;
182
+ line-height: 30px;
183
+ z-index: -1;
184
+ }
185
+
186
+ .ez-toc-widget-container ul.ez-toc-list li.active::before {
187
+ background-color: #EDEDED;
188
+ }
189
+
190
+ .ez-toc-widget-container li.active > a {
191
+ font-weight: 900;
192
+ }
193
+
194
+ .ez-toc-btn {
195
+ display: inline-block;
196
+ padding: 6px 12px;
197
+ margin-bottom: 0;
198
+ font-size: 14px;
199
+ font-weight: normal;
200
+ line-height: 1.428571429;
201
+ text-align: center;
202
+ white-space: nowrap;
203
+ vertical-align: middle;
204
+ cursor: pointer;
205
+ background-image: none;
206
+ border: 1px solid transparent;
207
+ border-radius: 4px;
208
+ -webkit-user-select: none;
209
+ -moz-user-select: none;
210
+ -ms-user-select: none;
211
+ -o-user-select: none;
212
+ user-select: none
213
+ }
214
+
215
+ .ez-toc-btn:focus {
216
+ outline: thin dotted #333;
217
+ outline: 5px auto -webkit-focus-ring-color;
218
+ outline-offset: -2px
219
+ }
220
+
221
+ .ez-toc-btn:hover,.ez-toc-btn:focus {
222
+ color: #333;
223
+ text-decoration: none
224
+ }
225
+
226
+ .ez-toc-btn:active,.ez-toc-btn.active {
227
+ background-image: none;
228
+ outline: 0;
229
+ -webkit-box-shadow: inset 0 3px 5px rgba(0,0,0,0.125);
230
+ box-shadow: inset 0 3px 5px rgba(0,0,0,0.125)
231
+ }
232
+
233
+ .ez-toc-btn-default {
234
+ color: #333;
235
+ background-color: #fff;
236
+ border-color: #ccc
237
+ }
238
+
239
+ .ez-toc-btn-default:hover,.ez-toc-btn-default:focus,.ez-toc-btn-default:active,.ez-toc-btn-default.active {
240
+ color: #333;
241
+ background-color: #ebebeb;
242
+ border-color: #adadad
243
+ }
244
+
245
+ .ez-toc-btn-default:active,.ez-toc-btn-default.active {
246
+ background-image: none
247
+ }
248
+
249
+ /*.btn-lg {*/
250
+ /*padding: 10px 16px;*/
251
+ /*font-size: 18px;*/
252
+ /*line-height: 1.33;*/
253
+ /*border-radius: 6px*/
254
+ /*}*/
255
+
256
+ .ez-toc-btn-sm,.ez-toc-btn-xs {
257
+ padding: 5px 10px;
258
+ font-size: 12px;
259
+ line-height: 1.5;
260
+ border-radius: 3px
261
+ }
262
+
263
+ .ez-toc-btn-xs {
264
+ padding: 1px 5px
265
+ }
266
+
267
+ .ez-toc-btn-default {
268
+ text-shadow: 0 -1px 0 rgba(0,0,0,0.2);
269
+ -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);
270
+ box-shadow: inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)
271
+ }
272
+
273
+ .ez-toc-btn-default:active {
274
+ -webkit-box-shadow: inset 0 3px 5px rgba(0,0,0,0.125);
275
+ box-shadow: inset 0 3px 5px rgba(0,0,0,0.125)
276
+ }
277
+
278
+ .ez-toc-btn:active,.btn.active {
279
+ background-image: none
280
+ }
281
+
282
+ .ez-toc-btn-default {
283
+ text-shadow: 0 1px 0 #fff;
284
+ background-image: -webkit-gradient(linear,left 0,left 100%,from(#fff),to(#e0e0e0));
285
+ background-image: -webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);
286
+ background-image: -moz-linear-gradient(top,#fff 0,#e0e0e0 100%);
287
+ background-image: linear-gradient(to bottom,#fff 0,#e0e0e0 100%);
288
+ background-repeat: repeat-x;
289
+ border-color: #dbdbdb;
290
+ border-color: #ccc;
291
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe0e0e0',GradientType=0);
292
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false)
293
+ }
294
+
295
+ .ez-toc-btn-default:hover,.ez-toc-btn-default:focus {
296
+ background-color: #e0e0e0;
297
+ background-position: 0 -15px
298
+ }
299
+
300
+ .ez-toc-btn-default:active,.ez-toc-btn-default.active {
301
+ background-color: #e0e0e0;
302
+ border-color: #dbdbdb
303
+ }
304
+
305
+ .ez-toc-pull-right {
306
+ float: right !important;
307
+ margin-left: 10px;
308
+ }
309
+
310
+ .ez-toc-glyphicon {
311
+ position: relative;
312
+ top: 1px;
313
+ display: inline-block;
314
+ font-family: 'Glyphicons Halflings';
315
+ -webkit-font-smoothing: antialiased;
316
+ font-style: normal;
317
+ font-weight: normal;
318
+ line-height: 1;
319
+ -moz-osx-font-smoothing: grayscale
320
+ }
321
+
322
+ .ez-toc-glyphicon:empty {
323
+ width: 1em
324
+ }
325
+
326
+ .ez-toc-toggle i.ez-toc-glyphicon {
327
+ font-size: 16px;
328
+ margin-left: 2px;
329
+ }
330
+
331
+ [class*="ez-toc-icon-"] {
332
+ font-family: 'ez-toc-icomoon' !important; /* For better glyphicon compatibility */
333
+ speak: none;
334
+ font-style: normal;
335
+ font-weight: normal;
336
+ font-variant: normal;
337
+ text-transform: none;
338
+ line-height: 1;
339
+
340
+ /* Better Font Rendering =========== */
341
+ -webkit-font-smoothing: antialiased;
342
+ -moz-osx-font-smoothing: grayscale;
343
+ }
344
+
345
+ .ez-toc-icon-toggle:before {
346
+ content: "\e87a";
347
+ }
348
+ #ez-toc-container input {
349
+ position: absolute;
350
+ left: -999em;
351
+ }
352
+ #ez-toc-container input[type="checkbox"]:checked + nav {
353
+ opacity: 0;
354
+ max-height: 0;
355
+ border: none;
356
+ }
357
+
358
+ #ez-toc-container label {
359
+ float: right;
360
+ position: relative;
361
+ left: 10px;
362
+ font-size: 16px;
363
+ background: #f9efef;
364
+ padding: 0px 4px 0px 5px;
365
+ border: 1px solid #999191;
366
+ border-radius: 5px;
367
+ cursor: pointer;
368
+ }
369
+ div#ez-toc-container p.ez-toc-title {
370
+ display: contents;
371
+ }
372
+ div#ez-toc-container {
373
+ padding-right: 20px;
374
+ }
assets/css/screen.min.css CHANGED
@@ -1 +1 @@
1
- #ez-toc-container{background:#f9f9f9;border:1px solid #aaa;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,.05);display:table;margin-bottom:1em;padding:10px;position:relative;width:auto}div.ez-toc-widget-container{padding:0;position:relative}#ez-toc-container.ez-toc-light-blue{background:#edf6ff}#ez-toc-container.ez-toc-white{background:#fff}#ez-toc-container.ez-toc-black{background:#000}#ez-toc-container.ez-toc-transparent{background:none transparent}div.ez-toc-widget-container ul{display:block}div.ez-toc-widget-container li{border:none;padding:0}div.ez-toc-widget-container ul.ez-toc-list{padding:10px}#ez-toc-container ul ul,.ez-toc div.ez-toc-widget-container ul ul{margin-left:1.5em}#ez-toc-container li,#ez-toc-container ul{margin:0;padding:0}#ez-toc-container li,#ez-toc-container ul,#ez-toc-container ul li,div.ez-toc-widget-container,div.ez-toc-widget-container li{background:0 0;list-style:none none;line-height:1.6;margin:0;overflow:hidden;z-index:1}#ez-toc-container p.ez-toc-title{text-align:left;line-height:1.45;margin:0;padding:0}.ez-toc-title-container{display:table;width:100%}.ez-toc-title,.ez-toc-title-toggle{display:table-cell;text-align:left;vertical-align:middle}#ez-toc-container.ez-toc-black p.ez-toc-title{color:#fff}#ez-toc-container div.ez-toc-title-container+ul.ez-toc-list{margin-top:1em}.ez-toc-wrap-left{float:left;margin-right:10px}.ez-toc-wrap-right{float:right;margin-left:10px}#ez-toc-container a{color:#444;box-shadow:none;text-decoration:none;text-shadow:none}#ez-toc-container a:visited{color:#9f9f9f}#ez-toc-container a:hover{text-decoration:underline}#ez-toc-container.ez-toc-black a{color:#fff}#ez-toc-container.ez-toc-black a:visited{color:#fff}#ez-toc-container a.ez-toc-toggle{color:#444}#ez-toc-container.counter-flat ul,#ez-toc-container.counter-hierarchy ul,.ez-toc-widget-container.counter-flat ul,.ez-toc-widget-container.counter-hierarchy ul{counter-reset:item}#ez-toc-container.counter-numeric li,.ez-toc-widget-container.counter-numeric li{list-style-type:decimal;list-style-position:inside}#ez-toc-container.counter-decimal ul.ez-toc-list li a::before,.ez-toc-widget-container.counter-decimal ul.ez-toc-list li a::before{content:counters(item, ".") ". ";display:inline-block;counter-increment:item;margin-right:.2em}#ez-toc-container.counter-roman li a::before,.ez-toc-widget-container.counter-roman ul.ez-toc-list li a::before{content:counters(item, ".", upper-roman) ". ";counter-increment:item}.ez-toc-widget-container ul.ez-toc-list li::before{content:' ';position:absolute;left:0;right:0;height:30px;line-height:30px;z-index:-1}.ez-toc-widget-container ul.ez-toc-list li.active::before{background-color:#ededed}.ez-toc-widget-container li.active>a{font-weight:900}.ez-toc-btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.428571429;text-align:center;white-space:nowrap;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.ez-toc-btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.ez-toc-btn:focus,.ez-toc-btn:hover{color:#333;text-decoration:none}.ez-toc-btn.active,.ez-toc-btn:active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.ez-toc-btn-default{color:#333;background-color:#fff;border-color:#ccc}.ez-toc-btn-default.active,.ez-toc-btn-default:active,.ez-toc-btn-default:focus,.ez-toc-btn-default:hover{color:#333;background-color:#ebebeb;border-color:#adadad}.ez-toc-btn-default.active,.ez-toc-btn-default:active{background-image:none}.ez-toc-btn-sm,.ez-toc-btn-xs{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.ez-toc-btn-xs{padding:1px 5px}.ez-toc-btn-default{text-shadow:0 -1px 0 rgba(0,0,0,.2);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.ez-toc-btn-default:active{box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.active,.ez-toc-btn:active{background-image:none}.ez-toc-btn-default{text-shadow:0 1px 0 #fff;background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.ez-toc-btn-default:focus,.ez-toc-btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.ez-toc-btn-default.active,.ez-toc-btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.ez-toc-pull-right{float:right!important;margin-left:10px}.ez-toc-glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';-webkit-font-smoothing:antialiased;font-style:normal;font-weight:400;line-height:1;-moz-osx-font-smoothing:grayscale}.ez-toc-glyphicon:empty{width:1em}.ez-toc-toggle i.ez-toc-glyphicon{font-size:16px;margin-left:2px}[class*=ez-toc-icon-]{font-family:ez-toc-icomoon!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.ez-toc-icon-toggle:before{content:"\e87a"}
1
+ #ez-toc-container {background: #F9F9F9;border: 1px solid #AAAAAA;border-radius: 4px;-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);display: table;margin-bottom: 1em;padding: 10px;position: relative;width: auto;}div.ez-toc-widget-container {padding: 0;position: relative;}#ez-toc-container.ez-toc-light-blue {background: #EDF6FF;}#ez-toc-container.ez-toc-white {background: #FFFFFF;}#ez-toc-container.ez-toc-black {background: #000000;}#ez-toc-container.ez-toc-transparent {background: none transparent;}div.ez-toc-widget-container ul {display: block;}div.ez-toc-widget-container li {border: none;padding: 0;}div.ez-toc-widget-container ul.ez-toc-list {padding: 10px;}#ez-toc-container ul ul, .ez-toc div.ez-toc-widget-container ul ul {margin-left: 1.5em;}#ez-toc-container ul, #ez-toc-container li {margin: 0;padding: 0;}#ez-toc-container ul, #ez-toc-container li, #ez-toc-container ul li, div.ez-toc-widget-container, div.ez-toc-widget-container li {background: none;list-style: none none;line-height: 1.6;margin: 0;overflow: hidden;z-index: 1;}#ez-toc-container p.ez-toc-title {text-align: left;line-height: 1.45;margin: 0;padding: 0;}.ez-toc-title-container {display: table;width: 100%;}.ez-toc-title, .ez-toc-title-toggle {display: table-cell;text-align: left;vertical-align: middle;}#ez-toc-container.ez-toc-black p.ez-toc-title {color: #FFF;}#ez-toc-container div.ez-toc-title-container + ul.ez-toc-list {margin-top: 1em;}.ez-toc-wrap-left {float: left;margin-right: 10px;}.ez-toc-wrap-right {float: right;margin-left: 10px;}#ez-toc-container a {color: #444444;box-shadow: none;text-decoration: none;text-shadow: none;}#ez-toc-container a:visited {color: #9f9f9f;}#ez-toc-container a:hover {text-decoration: underline;}#ez-toc-container.ez-toc-black a {color: #FFF;}#ez-toc-container.ez-toc-black a:visited {color: #FFF;}#ez-toc-container a.ez-toc-toggle {color: #444444;}#ez-toc-container.counter-hierarchy ul, .ez-toc-widget-container.counter-hierarchy ul, #ez-toc-container.counter-flat ul, .ez-toc-widget-container.counter-flat ul {counter-reset: item;}#ez-toc-container.counter-numeric li, .ez-toc-widget-container.counter-numeric li {list-style-type: decimal;list-style-position: inside;}#ez-toc-container.counter-decimal ul.ez-toc-list li a::before, .ez-toc-widget-container.counter-decimal ul.ez-toc-list li a::before {content: counters(item, ".") ". ";display: inline-block;counter-increment: item;margin-right: .2em;}#ez-toc-container.counter-roman li a::before, .ez-toc-widget-container.counter-roman ul.ez-toc-list li a::before {content: counters(item, ".", upper-roman) ". ";counter-increment: item;}.ez-toc-widget-container ul.ez-toc-list li::before {content: ' ';position: absolute;left: 0;right: 0;height: 30px;line-height: 30px;z-index: -1;}.ez-toc-widget-container ul.ez-toc-list li.active::before {background-color: #EDEDED;}.ez-toc-widget-container li.active > a {font-weight: 900;}.ez-toc-btn {display: inline-block;padding: 6px 12px;margin-bottom: 0;font-size: 14px;font-weight: normal;line-height: 1.428571429;text-align: center;white-space: nowrap;vertical-align: middle;cursor: pointer;background-image: none;border: 1px solid transparent;border-radius: 4px;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;-o-user-select: none;user-select: none }.ez-toc-btn:focus {outline: thin dotted #333;outline: 5px auto -webkit-focus-ring-color;outline-offset: -2px }.ez-toc-btn:hover,.ez-toc-btn:focus {color: #333;text-decoration: none }.ez-toc-btn:active,.ez-toc-btn.active {background-image: none;outline: 0;-webkit-box-shadow: inset 0 3px 5px rgba(0,0,0,0.125);box-shadow: inset 0 3px 5px rgba(0,0,0,0.125) }.ez-toc-btn-default {color: #333;background-color: #fff;border-color: #ccc }.ez-toc-btn-default:hover,.ez-toc-btn-default:focus,.ez-toc-btn-default:active,.ez-toc-btn-default.active {color: #333;background-color: #ebebeb;border-color: #adadad }.ez-toc-btn-default:active,.ez-toc-btn-default.active {background-image: none }.ez-toc-btn-sm,.ez-toc-btn-xs {padding: 5px 10px;font-size: 12px;line-height: 1.5;border-radius: 3px }.ez-toc-btn-xs {padding: 1px 5px }.ez-toc-btn-default {text-shadow: 0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow: inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075) }.ez-toc-btn-default:active {-webkit-box-shadow: inset 0 3px 5px rgba(0,0,0,0.125);box-shadow: inset 0 3px 5px rgba(0,0,0,0.125) }.ez-toc-btn:active,.btn.active {background-image: none }.ez-toc-btn-default {text-shadow: 0 1px 0 #fff;background-image: -webkit-gradient(linear,left 0,left 100%,from(#fff),to(#e0e0e0));background-image: -webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image: -moz-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image: linear-gradient(to bottom,#fff 0,#e0e0e0 100%);background-repeat: repeat-x;border-color: #dbdbdb;border-color: #ccc;filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe0e0e0',GradientType=0);filter: progid:DXImageTransform.Microsoft.gradient(enabled=false) }.ez-toc-btn-default:hover,.ez-toc-btn-default:focus {background-color: #e0e0e0;background-position: 0 -15px }.ez-toc-btn-default:active,.ez-toc-btn-default.active {background-color: #e0e0e0;border-color: #dbdbdb }.ez-toc-pull-right {float: right !important;margin-left: 10px;}.ez-toc-glyphicon {position: relative;top: 1px;display: inline-block;font-family: 'Glyphicons Halflings';-webkit-font-smoothing: antialiased;font-style: normal;font-weight: normal;line-height: 1;-moz-osx-font-smoothing: grayscale }.ez-toc-glyphicon:empty {width: 1em }.ez-toc-toggle i.ez-toc-glyphicon {font-size: 16px;margin-left: 2px;}[class*="ez-toc-icon-"] {font-family: 'ez-toc-icomoon' !important;speak: none;font-style: normal;font-weight: normal;font-variant: normal;text-transform: none;line-height: 1;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}.ez-toc-icon-toggle:before {content: "\e87a";}#ez-toc-container input {position: absolute;left: -999em;}#ez-toc-container input[type="checkbox"]:checked + nav {opacity: 0;max-height: 0;border: none;}#ez-toc-container label {float: right;position: relative;left: 3px;font-size: 16px;background: #f9efef;padding: 0px 4px 0px 5px;border: 1px solid #999191;border-radius: 5px;cursor: pointer;}div#ez-toc-container p.ez-toc-title {display: contents;}div#ez-toc-container {padding-right: 20px;}#ez-toc-container label {left: 10px;}
assets/js/front.js CHANGED
@@ -1,309 +1,309 @@
1
- jQuery( function( $ ) {
2
-
3
- /**
4
- * @typedef ezTOC
5
- * @type {Object} ezTOC
6
- * @property {string} affixSelector
7
- * @property {string} scroll_offset
8
- * @property {string} smooth_scroll
9
- * @property {string} visibility_hide_by_default
10
- */
11
-
12
- if ( typeof ezTOC != 'undefined' ) {
13
-
14
- var affix = $( '.ez-toc-widget-container.ez-toc-affix' );
15
-
16
- if ( 0 !== affix.length ) {
17
-
18
- /**
19
- * The smooth scroll offset needs to be taken into account when defining the offset_top property.
20
- * @link https://github.com/shazahm1/Easy-Table-of-Contents/issues/19
21
- *
22
- * @type {number}
23
- */
24
- var affixOffset = 30;
25
-
26
- // check offset setting
27
- if ( typeof ezTOC.scroll_offset != 'undefined' ) {
28
-
29
- affixOffset = parseInt( ezTOC.scroll_offset );
30
- }
31
-
32
- $( ezTOC.affixSelector ).stick_in_parent( {
33
- inner_scrolling: false,
34
- offset_top: affixOffset
35
- } )
36
- }
37
-
38
- $.fn.shrinkTOCWidth = function() {
39
-
40
- $( this ).css( {
41
- width: 'auto',
42
- display: 'table'
43
- });
44
-
45
- if ( /MSIE 7\./.test( navigator.userAgent ) )
46
- $( this ).css( 'width', '' );
47
- };
48
-
49
- var smoothScroll = parseInt( ezTOC.smooth_scroll );
50
-
51
- if ( 1 === smoothScroll ) {
52
-
53
- $( 'a.ez-toc-link' ).on( 'click', function() {
54
-
55
- var self = $( this );
56
-
57
- var target = '';
58
- var hostname = self.prop( 'hostname' );
59
- var pathname = self.prop( 'pathname' );
60
- var qs = self.prop( 'search' );
61
- var hash = self.prop( 'hash' );
62
-
63
- // ie strips out the preceding / from pathname
64
- if ( pathname.length > 0 ) {
65
- if ( pathname.charAt( 0 ) !== '/' ) {
66
- pathname = '/' + pathname;
67
- }
68
- }
69
-
70
- if ( ( window.location.hostname === hostname ) &&
71
- ( window.location.pathname === pathname ) &&
72
- ( window.location.search === qs ) &&
73
- ( hash !== '' )
74
- ) {
75
-
76
- // var id = decodeURIComponent( hash.replace( '#', '' ) );
77
- target = '[id="' + hash.replace( '#', '' ) + '"]';
78
-
79
- // verify it exists
80
- if ( $( target ).length === 0 ) {
81
- console.log( 'ezTOC scrollTarget Not Found: ' + target );
82
- target = '';
83
- }
84
-
85
- // check offset setting
86
- if ( typeof ezTOC.scroll_offset != 'undefined' ) {
87
-
88
- var offset = -1 * ezTOC.scroll_offset;
89
-
90
- } else {
91
-
92
- var adminbar = $( '#wpadminbar' );
93
-
94
- if ( adminbar.length > 0 ) {
95
-
96
- if ( adminbar.is( ':visible' ) )
97
- offset = -30; // admin bar exists, give it the default
98
- else
99
- offset = 0; // there is an admin bar but it's hidden, so no offset!
100
-
101
- } else {
102
-
103
- // no admin bar, so no offset!
104
- offset = 0;
105
- }
106
- }
107
-
108
- if ( target ) {
109
- $.smoothScroll( {
110
- scrollTarget: target,
111
- offset: offset,
112
- beforeScroll: deactivateSetActiveEzTocListElement,
113
- afterScroll: function() { setActiveEzTocListElement(); activateSetActiveEzTocListElement(); }
114
- } );
115
-
116
- }
117
- }
118
- } );
119
- }
120
-
121
- if ( typeof ezTOC.visibility_hide_by_default != 'undefined' ) {
122
-
123
- var toc = $( 'ul.ez-toc-list' );
124
- var toggle = $( 'a.ez-toc-toggle' );
125
- var invert = ezTOC.visibility_hide_by_default;
126
-
127
- toggle.css( 'display', 'inline' );
128
-
129
- if ( Cookies ) {
130
-
131
- Cookies.get( 'ezTOC_hidetoc' ) == 1 ? toggle.data( 'visible', false ) : toggle.data( 'visible', true );
132
-
133
- } else {
134
-
135
- toggle.data( 'visible', true );
136
- }
137
-
138
- if ( invert ) {
139
-
140
- toggle.data( 'visible', false )
141
- }
142
-
143
- if ( ! toggle.data( 'visible' ) ) {
144
-
145
- toc.hide();
146
- }
147
-
148
- toggle.on( 'click', function( event ) {
149
-
150
- event.preventDefault();
151
-
152
- if ( $( this ).data( 'visible' ) ) {
153
-
154
- $( this ).data( 'visible', false );
155
-
156
- if ( Cookies ) {
157
-
158
- if ( invert )
159
- Cookies.set( 'ezTOC_hidetoc', null, { path: '/' } );
160
- else
161
- Cookies.set( 'ezTOC_hidetoc', '1', { expires: 30, path: '/' } );
162
- }
163
-
164
- toc.hide( 'fast' );
165
-
166
- } else {
167
-
168
- $( this ).data( 'visible', true );
169
-
170
- if ( Cookies ) {
171
-
172
- if ( invert )
173
- Cookies.set( 'ezTOC_hidetoc', '1', { expires: 30, path: '/' } );
174
- else
175
- Cookies.set( 'ezTOC_hidetoc', null, { path: '/' } );
176
- }
177
-
178
- toc.show( 'fast' );
179
-
180
- }
181
-
182
- } );
183
- }
184
-
185
-
186
- // ======================================
187
- // Set active heading in ez-toc-widget list
188
- // ======================================
189
-
190
- var headings = $( 'span.ez-toc-section' ).toArray();
191
- var headingToListElementLinkMap = getHeadingToListElementLinkMap( headings );
192
- var listElementLinks = $.map( headingToListElementLinkMap, function ( value, key ) {
193
- return value
194
- } );
195
- var scrollOffset = getScrollOffset();
196
-
197
- activateSetActiveEzTocListElement();
198
-
199
- function setActiveEzTocListElement() {
200
- var activeHeading = getActiveHeading( scrollOffset, headings );
201
- if ( activeHeading ) {
202
- var activeListElementLink = headingToListElementLinkMap[ activeHeading.id ];
203
- removeStyleFromNonActiveListElement( activeListElementLink, listElementLinks );
204
- setStyleForActiveListElementElement( activeListElementLink );
205
- }
206
- }
207
-
208
- function activateSetActiveEzTocListElement() {
209
- if ( headings.length > 0 && $('.ez-toc-widget-container').length) {
210
- $( window ).on( 'load resize scroll', setActiveEzTocListElement );
211
- }
212
- }
213
-
214
- function deactivateSetActiveEzTocListElement() {
215
- $( window ).off( 'load resize scroll', setActiveEzTocListElement );
216
- }
217
-
218
- function getEzTocListElementLinkByHeading( heading ) {
219
- return $( '.ez-toc-widget-container .ez-toc-list a[href="#' + $( heading ).attr( 'id' ) + '"]' );
220
- }
221
-
222
- function getHeadingToListElementLinkMap( headings ) {
223
- return headings.reduce( function ( map, heading ) {
224
- map[ heading.id ] = getEzTocListElementLinkByHeading( heading );
225
- return map;
226
- }, {} );
227
- }
228
-
229
- function getScrollOffset() {
230
- var scrollOffset = 5; // so if smooth offset is off, the correct title is set as active
231
- if ( typeof ezTOC.smooth_scroll != 'undefined' && parseInt( ezTOC.smooth_scroll ) === 1 ) {
232
- scrollOffset = ( typeof ezTOC.scroll_offset != 'undefined' ) ? parseInt( ezTOC.scroll_offset ) : 30;
233
- }
234
-
235
- var adminbar = $( '#wpadminbar' );
236
-
237
- if ( adminbar.length ) {
238
- scrollOffset += adminbar.height();
239
- }
240
- return scrollOffset;
241
- }
242
-
243
- function getActiveHeading( topOffset, headings ) {
244
- var scrollTop = $( window ).scrollTop();
245
- var relevantOffset = scrollTop + topOffset + 1;
246
- var activeHeading = headings[ 0 ];
247
- var closestHeadingAboveOffset = relevantOffset - $( activeHeading ).offset().top;
248
- headings.forEach( function ( section ) {
249
- var topOffset = relevantOffset - $( section ).offset().top;
250
- if ( topOffset > 0 && topOffset < closestHeadingAboveOffset ) {
251
- closestHeadingAboveOffset = topOffset;
252
- activeHeading = section;
253
- }
254
- } );
255
- return activeHeading;
256
- }
257
-
258
- function removeStyleFromNonActiveListElement( activeListElementLink, listElementLinks ) {
259
- listElementLinks.forEach( function ( listElementLink ) {
260
- if ( activeListElementLink !== listElementLink && listElementLink.parent().hasClass( 'active' ) ) {
261
- listElementLink.parent().removeClass( 'active' );
262
- }
263
- } );
264
- }
265
-
266
- function correctActiveListElementBackgroundColorHeight( activeListElement ) {
267
- var listElementHeight = getListElementHeightWithoutUlChildren( activeListElement );
268
- addListElementBackgroundColorHeightStyleToHead( listElementHeight );
269
- }
270
-
271
- function getListElementHeightWithoutUlChildren( listElement ) {
272
- var $listElement = $( listElement );
273
- var content = $listElement.html();
274
- // Adding list item with class '.active' to get the real height.
275
- // When adding a class to an existing element and using jQuery(..).height() directly afterwards,
276
- // the height is the 'old' height. The height might change due to text-wraps when setting the text-weight bold for example
277
- // When adding a new item, the height is calculated correctly.
278
- // But only when it might be visible (so display:none; is not possible...)
279
- // But because it get's directly removed afterwards it never will be rendered by the browser
280
- // (at least in my tests in FF, Chrome, IE11 and Edge)
281
- $listElement.parent().append( '<li id="ez-toc-height-test" class="active">' + content + '</li>' );
282
- var listItem = $( '#ez-toc-height-test' );
283
- var height = listItem.height();
284
- listItem.remove();
285
- return height - $listElement.children( 'ul' ).first().height();
286
- }
287
-
288
- function addListElementBackgroundColorHeightStyleToHead( listElementHeight ) {
289
- // Remove existing
290
- $( '#ez-toc-active-height' ).remove();
291
- // jQuery(..).css(..) doesn't work, because ::before is a pseudo element and not part of the DOM
292
- // Workaround is to add it to head
293
- $( '<style id="ez-toc-active-height">' +
294
- '.ez-toc-widget-container ul.ez-toc-list li.active::before {' +
295
- // 'line-heigh:' + listElementHeight + 'px; ' +
296
- 'height:' + listElementHeight + 'px;' +
297
- '} </style>' )
298
- .appendTo( 'head' );
299
- }
300
-
301
- function setStyleForActiveListElementElement( activeListElementLink ) {
302
- var activeListElement = activeListElementLink.parent();
303
- if ( !activeListElement.hasClass( 'active' ) ) {
304
- activeListElement.addClass( 'active' );
305
- }
306
- correctActiveListElementBackgroundColorHeight( activeListElement );
307
- }
308
- }
309
- } );
1
+ jQuery( function( $ ) {
2
+
3
+ /**
4
+ * @typedef ezTOC
5
+ * @type {Object} ezTOC
6
+ * @property {string} affixSelector
7
+ * @property {string} scroll_offset
8
+ * @property {string} smooth_scroll
9
+ * @property {string} visibility_hide_by_default
10
+ */
11
+
12
+ if ( typeof ezTOC != 'undefined' ) {
13
+
14
+ var affix = $( '.ez-toc-widget-container.ez-toc-affix' );
15
+
16
+ if ( 0 !== affix.length ) {
17
+
18
+ /**
19
+ * The smooth scroll offset needs to be taken into account when defining the offset_top property.
20
+ * @link https://github.com/shazahm1/Easy-Table-of-Contents/issues/19
21
+ *
22
+ * @type {number}
23
+ */
24
+ var affixOffset = 30;
25
+
26
+ // check offset setting
27
+ if ( typeof ezTOC.scroll_offset != 'undefined' ) {
28
+
29
+ affixOffset = parseInt( ezTOC.scroll_offset );
30
+ }
31
+
32
+ $( ezTOC.affixSelector ).stick_in_parent( {
33
+ inner_scrolling: false,
34
+ offset_top: affixOffset
35
+ } )
36
+ }
37
+
38
+ $.fn.shrinkTOCWidth = function() {
39
+
40
+ $( this ).css( {
41
+ width: 'auto',
42
+ display: 'table'
43
+ });
44
+
45
+ if ( /MSIE 7\./.test( navigator.userAgent ) )
46
+ $( this ).css( 'width', '' );
47
+ };
48
+
49
+ var smoothScroll = parseInt( ezTOC.smooth_scroll );
50
+
51
+ if ( 1 === smoothScroll ) {
52
+
53
+ $( 'a.ez-toc-link' ).on( 'click', function() {
54
+
55
+ var self = $( this );
56
+
57
+ var target = '';
58
+ var hostname = self.prop( 'hostname' );
59
+ var pathname = self.prop( 'pathname' );
60
+ var qs = self.prop( 'search' );
61
+ var hash = self.prop( 'hash' );
62
+
63
+ // ie strips out the preceding / from pathname
64
+ if ( pathname.length > 0 ) {
65
+ if ( pathname.charAt( 0 ) !== '/' ) {
66
+ pathname = '/' + pathname;
67
+ }
68
+ }
69
+
70
+ if ( ( window.location.hostname === hostname ) &&
71
+ ( window.location.pathname === pathname ) &&
72
+ ( window.location.search === qs ) &&
73
+ ( hash !== '' )
74
+ ) {
75
+
76
+ // var id = decodeURIComponent( hash.replace( '#', '' ) );
77
+ target = '[id="' + hash.replace( '#', '' ) + '"]';
78
+
79
+ // verify it exists
80
+ if ( $( target ).length === 0 ) {
81
+ console.log( 'ezTOC scrollTarget Not Found: ' + target );
82
+ target = '';
83
+ }
84
+
85
+ // check offset setting
86
+ if ( typeof ezTOC.scroll_offset != 'undefined' ) {
87
+
88
+ var offset = -1 * ezTOC.scroll_offset;
89
+
90
+ } else {
91
+
92
+ var adminbar = $( '#wpadminbar' );
93
+
94
+ if ( adminbar.length > 0 ) {
95
+
96
+ if ( adminbar.is( ':visible' ) )
97
+ offset = -30; // admin bar exists, give it the default
98
+ else
99
+ offset = 0; // there is an admin bar but it's hidden, so no offset!
100
+
101
+ } else {
102
+
103
+ // no admin bar, so no offset!
104
+ offset = 0;
105
+ }
106
+ }
107
+
108
+ if ( target ) {
109
+ $.smoothScroll( {
110
+ scrollTarget: target,
111
+ offset: offset,
112
+ beforeScroll: deactivateSetActiveEzTocListElement,
113
+ afterScroll: function() { setActiveEzTocListElement(); activateSetActiveEzTocListElement(); }
114
+ } );
115
+
116
+ }
117
+ }
118
+ } );
119
+ }
120
+
121
+ if ( typeof ezTOC.visibility_hide_by_default != 'undefined' ) {
122
+
123
+ var toc = $( 'ul.ez-toc-list' );
124
+ var toggle = $( 'a.ez-toc-toggle' );
125
+ var invert = ezTOC.visibility_hide_by_default;
126
+
127
+ toggle.css( 'display', 'inline' );
128
+
129
+ if ( Cookies ) {
130
+
131
+ Cookies.get( 'ezTOC_hidetoc' ) == 1 ? toggle.data( 'visible', false ) : toggle.data( 'visible', true );
132
+
133
+ } else {
134
+
135
+ toggle.data( 'visible', true );
136
+ }
137
+
138
+ if ( invert ) {
139
+
140
+ toggle.data( 'visible', false )
141
+ }
142
+
143
+ if ( ! toggle.data( 'visible' ) ) {
144
+
145
+ toc.hide();
146
+ }
147
+
148
+ toggle.on( 'click', function( event ) {
149
+
150
+ event.preventDefault();
151
+
152
+ if ( $( this ).data( 'visible' ) ) {
153
+
154
+ $( this ).data( 'visible', false );
155
+
156
+ if ( Cookies ) {
157
+
158
+ if ( invert )
159
+ Cookies.set( 'ezTOC_hidetoc', null, { path: '/' } );
160
+ else
161
+ Cookies.set( 'ezTOC_hidetoc', '1', { expires: 30, path: '/' } );
162
+ }
163
+
164
+ toc.hide( 'fast' );
165
+
166
+ } else {
167
+
168
+ $( this ).data( 'visible', true );
169
+
170
+ if ( Cookies ) {
171
+
172
+ if ( invert )
173
+ Cookies.set( 'ezTOC_hidetoc', '1', { expires: 30, path: '/' } );
174
+ else
175
+ Cookies.set( 'ezTOC_hidetoc', null, { path: '/' } );
176
+ }
177
+
178
+ toc.show( 'fast' );
179
+
180
+ }
181
+
182
+ } );
183
+ }
184
+
185
+
186
+ // ======================================
187
+ // Set active heading in ez-toc-widget list
188
+ // ======================================
189
+
190
+ var headings = $( 'span.ez-toc-section' ).toArray();
191
+ var headingToListElementLinkMap = getHeadingToListElementLinkMap( headings );
192
+ var listElementLinks = $.map( headingToListElementLinkMap, function ( value, key ) {
193
+ return value
194
+ } );
195
+ var scrollOffset = getScrollOffset();
196
+
197
+ activateSetActiveEzTocListElement();
198
+
199
+ function setActiveEzTocListElement() {
200
+ var activeHeading = getActiveHeading( scrollOffset, headings );
201
+ if ( activeHeading ) {
202
+ var activeListElementLink = headingToListElementLinkMap[ activeHeading.id ];
203
+ removeStyleFromNonActiveListElement( activeListElementLink, listElementLinks );
204
+ setStyleForActiveListElementElement( activeListElementLink );
205
+ }
206
+ }
207
+
208
+ function activateSetActiveEzTocListElement() {
209
+ if ( headings.length > 0 && $('.ez-toc-widget-container').length) {
210
+ $( window ).on( 'load resize scroll', setActiveEzTocListElement );
211
+ }
212
+ }
213
+
214
+ function deactivateSetActiveEzTocListElement() {
215
+ $( window ).off( 'load resize scroll', setActiveEzTocListElement );
216
+ }
217
+
218
+ function getEzTocListElementLinkByHeading( heading ) {
219
+ return $( '.ez-toc-widget-container .ez-toc-list a[href="#' + $( heading ).attr( 'id' ) + '"]' );
220
+ }
221
+
222
+ function getHeadingToListElementLinkMap( headings ) {
223
+ return headings.reduce( function ( map, heading ) {
224
+ map[ heading.id ] = getEzTocListElementLinkByHeading( heading );
225
+ return map;
226
+ }, {} );
227
+ }
228
+
229
+ function getScrollOffset() {
230
+ var scrollOffset = 5; // so if smooth offset is off, the correct title is set as active
231
+ if ( typeof ezTOC.smooth_scroll != 'undefined' && parseInt( ezTOC.smooth_scroll ) === 1 ) {
232
+ scrollOffset = ( typeof ezTOC.scroll_offset != 'undefined' ) ? parseInt( ezTOC.scroll_offset ) : 30;
233
+ }
234
+
235
+ var adminbar = $( '#wpadminbar' );
236
+
237
+ if ( adminbar.length ) {
238
+ scrollOffset += adminbar.height();
239
+ }
240
+ return scrollOffset;
241
+ }
242
+
243
+ function getActiveHeading( topOffset, headings ) {
244
+ var scrollTop = $( window ).scrollTop();
245
+ var relevantOffset = scrollTop + topOffset + 1;
246
+ var activeHeading = headings[ 0 ];
247
+ var closestHeadingAboveOffset = relevantOffset - $( activeHeading ).offset().top;
248
+ headings.forEach( function ( section ) {
249
+ var topOffset = relevantOffset - $( section ).offset().top;
250
+ if ( topOffset > 0 && topOffset < closestHeadingAboveOffset ) {
251
+ closestHeadingAboveOffset = topOffset;
252
+ activeHeading = section;
253
+ }
254
+ } );
255
+ return activeHeading;
256
+ }
257
+
258
+ function removeStyleFromNonActiveListElement( activeListElementLink, listElementLinks ) {
259
+ listElementLinks.forEach( function ( listElementLink ) {
260
+ if ( activeListElementLink !== listElementLink && listElementLink.parent().hasClass( 'active' ) ) {
261
+ listElementLink.parent().removeClass( 'active' );
262
+ }
263
+ } );
264
+ }
265
+
266
+ function correctActiveListElementBackgroundColorHeight( activeListElement ) {
267
+ var listElementHeight = getListElementHeightWithoutUlChildren( activeListElement );
268
+ addListElementBackgroundColorHeightStyleToHead( listElementHeight );
269
+ }
270
+
271
+ function getListElementHeightWithoutUlChildren( listElement ) {
272
+ var $listElement = $( listElement );
273
+ var content = $listElement.html();
274
+ // Adding list item with class '.active' to get the real height.
275
+ // When adding a class to an existing element and using jQuery(..).height() directly afterwards,
276
+ // the height is the 'old' height. The height might change due to text-wraps when setting the text-weight bold for example
277
+ // When adding a new item, the height is calculated correctly.
278
+ // But only when it might be visible (so display:none; is not possible...)
279
+ // But because it get's directly removed afterwards it never will be rendered by the browser
280
+ // (at least in my tests in FF, Chrome, IE11 and Edge)
281
+ $listElement.parent().append( '<li id="ez-toc-height-test" class="active">' + content + '</li>' );
282
+ var listItem = $( '#ez-toc-height-test' );
283
+ var height = listItem.height();
284
+ listItem.remove();
285
+ return height - $listElement.children( 'ul' ).first().height();
286
+ }
287
+
288
+ function addListElementBackgroundColorHeightStyleToHead( listElementHeight ) {
289
+ // Remove existing
290
+ $( '#ez-toc-active-height' ).remove();
291
+ // jQuery(..).css(..) doesn't work, because ::before is a pseudo element and not part of the DOM
292
+ // Workaround is to add it to head
293
+ $( '<style id="ez-toc-active-height">' +
294
+ '.ez-toc-widget-container ul.ez-toc-list li.active::before {' +
295
+ // 'line-heigh:' + listElementHeight + 'px; ' +
296
+ 'height:' + listElementHeight + 'px;' +
297
+ '} </style>' )
298
+ .appendTo( 'head' );
299
+ }
300
+
301
+ function setStyleForActiveListElementElement( activeListElementLink ) {
302
+ var activeListElement = activeListElementLink.parent();
303
+ if ( !activeListElement.hasClass( 'active' ) ) {
304
+ activeListElement.addClass( 'active' );
305
+ }
306
+ correctActiveListElementBackgroundColorHeight( activeListElement );
307
+ }
308
+ }
309
+ } );
easy-table-of-contents.php CHANGED
@@ -1,738 +1,738 @@
1
- <?php
2
- /**
3
- * Plugin Name: Easy Table of Contents
4
- * Plugin URI: https://magazine3.company/
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.19
7
- * Author: Magazine3
8
- * Author URI: https://magazine3.company/
9
- * Text Domain: easy-table-of-contents
10
- * Domain Path: /languages
11
- *
12
- * Copyright 2022 Magazine3 ( email : support@magazine3.in )
13
- *
14
- * Easy Table of Contents is free software; you can redistribute it and/or modify
15
- * it under the terms of the GNU General Public License, version 2, as
16
- * published by the Free Software Foundation.
17
- *
18
- * This program is distributed in the hope that it will be useful,
19
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
- * GNU General Public License for more details.
22
- *
23
- * You should have received a copy of the GNU General Public License
24
- * along with Easy Table of Contents; if not, see <http://www.gnu.org/licenses/>.
25
- *
26
- * @package Easy Table of Contents
27
- * @category Plugin
28
- * @author Magazine3
29
- * @version 2.0.19
30
- */
31
-
32
- use Easy_Plugins\Table_Of_Contents\Debug;
33
- use function Easy_Plugins\Table_Of_Contents\String\mb_find_replace;
34
-
35
- // Exit if accessed directly
36
- if ( ! defined( 'ABSPATH' ) ) exit;
37
-
38
- if ( ! class_exists( 'ezTOC' ) ) {
39
-
40
- /**
41
- * Class ezTOC
42
- */
43
- final class ezTOC {
44
-
45
- /**
46
- * Current version.
47
- *
48
- * @since 1.0
49
- * @var string
50
- */
51
- const VERSION = '2.0.19';
52
-
53
- /**
54
- * Stores the instance of this class.
55
- *
56
- * @access private
57
- * @since 1.0
58
- * @static
59
- *
60
- * @var ezTOC
61
- */
62
- private static $instance;
63
-
64
- /**
65
- * @since 2.0
66
- * @var array
67
- */
68
- private static $store = array();
69
-
70
- /**
71
- * A dummy constructor to prevent the class from being loaded more than once.
72
- *
73
- * @access public
74
- * @since 1.0
75
- */
76
- public function __construct() { /* Do nothing here */ }
77
-
78
- /**
79
- * @access private
80
- * @since 1.0
81
- * @static
82
- *
83
- * @return ezTOC
84
- */
85
- public static function instance() {
86
-
87
- if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) {
88
-
89
- self::$instance = new self;
90
-
91
- self::defineConstants();
92
- self::includes();
93
- self::hooks();
94
-
95
- self::loadTextdomain();
96
- }
97
-
98
- return self::$instance;
99
- }
100
-
101
- /**
102
- * Define the plugin constants.
103
- *
104
- * @access private
105
- * @since 1.0
106
- * @static
107
- */
108
- private static function defineConstants() {
109
-
110
- define( 'EZ_TOC_DIR_NAME', plugin_basename( dirname( __FILE__ ) ) );
111
- define( 'EZ_TOC_BASE_NAME', plugin_basename( __FILE__ ) );
112
- define( 'EZ_TOC_PATH', dirname( __FILE__ ) );
113
- define( 'EZ_TOC_URL', plugin_dir_url( __FILE__ ) );
114
- }
115
-
116
- /**
117
- * Includes the plugin dependency files.
118
- *
119
- * @access private
120
- * @since 1.0
121
- * @static
122
- */
123
- private static function includes() {
124
-
125
- require_once( EZ_TOC_PATH . '/includes/class.options.php' );
126
-
127
- if ( is_admin() ) {
128
-
129
- // This must be included after `class.options.php` because it depends on it methods.
130
- require_once( EZ_TOC_PATH . '/includes/class.admin.php' );
131
- }
132
-
133
- require_once( EZ_TOC_PATH . '/includes/class.post.php' );
134
- require_once( EZ_TOC_PATH . '/includes/class.widget-toc.php' );
135
- require_once( EZ_TOC_PATH . '/includes/Debug.php' );
136
- require_once( EZ_TOC_PATH . '/includes/inc.functions.php' );
137
- require_once( EZ_TOC_PATH . '/includes/inc.string-functions.php' );
138
-
139
- require_once( EZ_TOC_PATH . '/includes/inc.plugin-compatibility.php' );
140
- }
141
-
142
- /**
143
- * Add the core action filter hook.
144
- *
145
- * @access private
146
- * @since 1.0
147
- * @static
148
- */
149
- private static function hooks() {
150
-
151
- //add_action( 'plugins_loaded', array( __CLASS__, 'loadTextdomain' ) );
152
- add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueueScripts' ) );
153
-
154
- // Run after shortcodes are interpreted (priority 10).
155
- add_filter( 'the_content', array( __CLASS__, 'the_content' ), 100 );
156
- add_shortcode( 'ez-toc', array( __CLASS__, 'shortcode' ) );
157
- add_shortcode( apply_filters( 'ez_toc_shortcode', 'toc' ), array( __CLASS__, 'shortcode' ) );
158
- }
159
-
160
- /**
161
- * Load the plugin translation.
162
- *
163
- * Credit: Adapted from Ninja Forms / Easy Digital Downloads.
164
- *
165
- * @access private
166
- * @since 1.0
167
- * @static
168
- *
169
- * @uses apply_filters()
170
- * @uses get_locale()
171
- * @uses load_textdomain()
172
- * @uses load_plugin_textdomain()
173
- *
174
- * @return void
175
- */
176
- public static function loadTextdomain() {
177
-
178
- // Plugin textdomain. This should match the one set in the plugin header.
179
- $domain = 'easy-table-of-contents';
180
-
181
- // Set filter for plugin's languages directory
182
- $languagesDirectory = apply_filters( "ez_{$domain}_languages_directory", EZ_TOC_DIR_NAME . '/languages/' );
183
-
184
- // Traditional WordPress plugin locale filter
185
- $locale = apply_filters( 'plugin_locale', get_locale(), $domain );
186
- $fileName = sprintf( '%1$s-%2$s.mo', $domain, $locale );
187
-
188
- // Setup paths to current locale file
189
- $local = $languagesDirectory . $fileName;
190
- $global = WP_LANG_DIR . "/{$domain}/" . $fileName;
191
-
192
- if ( file_exists( $global ) ) {
193
-
194
- // Look in global `../wp-content/languages/{$domain}/` folder.
195
- load_textdomain( $domain, $global );
196
-
197
- } elseif ( file_exists( $local ) ) {
198
-
199
- // Look in local `../wp-content/plugins/{plugin-directory}/languages/` folder.
200
- load_textdomain( $domain, $local );
201
-
202
- } else {
203
-
204
- // Load the default language files
205
- load_plugin_textdomain( $domain, false, $languagesDirectory );
206
- }
207
- }
208
-
209
- /**
210
- * Call back for the `wp_enqueue_scripts` action.
211
- *
212
- * Register and enqueue CSS and javascript files for frontend.
213
- *
214
- * @access private
215
- * @since 1.0
216
- * @static
217
- */
218
- public static function enqueueScripts() {
219
-
220
- // If SCRIPT_DEBUG is set and TRUE load the non-minified JS files, otherwise, load the minified files.
221
- $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
222
-
223
- $js_vars = array();
224
-
225
- $isEligible = self::is_eligible( get_post() );
226
-
227
- if ( ! $isEligible && ! is_active_widget( false, false, 'ezw_tco' ) ) {
228
- return false;
229
- }
230
-
231
- wp_register_style( 'ez-icomoon', EZ_TOC_URL . "vendor/icomoon/style$min.css", array(), ezTOC::VERSION );
232
- wp_register_style( 'ez-toc', EZ_TOC_URL . "assets/css/screen$min.css", array( 'ez-icomoon' ), ezTOC::VERSION );
233
-
234
- wp_register_script( 'js-cookie', EZ_TOC_URL . "vendor/js-cookie/js.cookie$min.js", array(), '2.2.1', TRUE );
235
- wp_register_script( 'jquery-smooth-scroll', EZ_TOC_URL . "vendor/smooth-scroll/jquery.smooth-scroll$min.js", array( 'jquery' ), '2.2.0', TRUE );
236
- wp_register_script( 'jquery-sticky-kit', EZ_TOC_URL . "vendor/sticky-kit/jquery.sticky-kit$min.js", array( 'jquery' ), '1.9.2', TRUE );
237
-
238
- wp_register_script(
239
- 'ez-toc-js',
240
- EZ_TOC_URL . "assets/js/front{$min}.js",
241
- array( 'jquery-smooth-scroll', 'js-cookie', 'jquery-sticky-kit' ),
242
- ezTOC::VERSION . '-' . filemtime( EZ_TOC_PATH . "/assets/js/front{$min}.js" ),
243
- true
244
- );
245
-
246
- if ( ! ezTOC_Option::get( 'exclude_css' ) ) {
247
-
248
- wp_enqueue_style( 'ez-toc' );
249
- self::inlineCSS();
250
- }
251
-
252
- if ( ezTOC_Option::get( 'smooth_scroll' ) ) {
253
-
254
- $js_vars['smooth_scroll'] = true;
255
- }
256
-
257
- //wp_enqueue_script( 'ez-toc-js' );
258
-
259
- if ( ezTOC_Option::get( 'show_heading_text' ) && ezTOC_Option::get( 'visibility' ) ) {
260
-
261
- $width = ezTOC_Option::get( 'width' ) !== 'custom' ? ezTOC_Option::get( 'width' ) : ezTOC_Option::get( 'width_custom' ) . ezTOC_Option::get( 'width_custom_units' );
262
-
263
- $js_vars['visibility_hide_by_default'] = ezTOC_Option::get( 'visibility_hide_by_default' ) ? true : false;
264
-
265
- $js_vars['width'] = esc_js( $width );
266
- }
267
-
268
- $offset = wp_is_mobile() ? ezTOC_Option::get( 'mobile_smooth_scroll_offset', 0 ) : ezTOC_Option::get( 'smooth_scroll_offset', 30 );
269
-
270
- $js_vars['scroll_offset'] = esc_js( $offset );
271
-
272
- if ( ezTOC_Option::get( 'widget_affix_selector' ) ) {
273
-
274
- $js_vars['affixSelector'] = ezTOC_Option::get( 'widget_affix_selector' );
275
- }
276
-
277
- if ( 0 < count( $js_vars ) ) {
278
-
279
- wp_localize_script( 'ez-toc-js', 'ezTOC', $js_vars );
280
- }
281
- }
282
-
283
- /**
284
- * Prints out inline CSS after the core CSS file to allow overriding core styles via options.
285
- *
286
- * @access private
287
- * @since 1.0
288
- * @static
289
- */
290
- public static function inlineCSS() {
291
-
292
- $css = '';
293
-
294
- if ( ! ezTOC_Option::get( 'exclude_css' ) ) {
295
-
296
- $css .= 'div#ez-toc-container p.ez-toc-title {font-size: ' . ezTOC_Option::get( 'title_font_size', 120 ) . ezTOC_Option::get( 'title_font_size_units', '%' ) . ';}';
297
- $css .= 'div#ez-toc-container p.ez-toc-title {font-weight: ' . ezTOC_Option::get( 'title_font_weight', 500 ) . ';}';
298
- $css .= 'div#ez-toc-container ul li {font-size: ' . ezTOC_Option::get( 'font_size' ) . ezTOC_Option::get( 'font_size_units' ) . ';}';
299
-
300
- if ( ezTOC_Option::get( 'theme' ) === 'custom' || ezTOC_Option::get( 'width' ) != 'auto' ) {
301
-
302
- $css .= 'div#ez-toc-container {';
303
-
304
- if ( ezTOC_Option::get( 'theme' ) === 'custom' ) {
305
-
306
- $css .= 'background: ' . ezTOC_Option::get( 'custom_background_colour' ) . ';border: 1px solid ' . ezTOC_Option::get( 'custom_border_colour' ) . ';';
307
- }
308
-
309
- if ( 'auto' !== ezTOC_Option::get( 'width' ) ) {
310
-
311
- $css .= 'width: ';
312
-
313
- if ( 'custom' !== ezTOC_Option::get( 'width' ) ) {
314
-
315
- $css .= ezTOC_Option::get( 'width' );
316
-
317
- } else {
318
-
319
- $css .= ezTOC_Option::get( 'width_custom' ) . ezTOC_Option::get( 'width_custom_units' );
320
- }
321
-
322
- $css .= ';';
323
- }
324
-
325
- $css .= '}';
326
- }
327
-
328
- if ( 'custom' === ezTOC_Option::get( 'theme' ) ) {
329
-
330
- $css .= 'div#ez-toc-container p.ez-toc-title {color: ' . ezTOC_Option::get( 'custom_title_colour' ) . ';}';
331
- //$css .= 'div#ez-toc-container p.ez-toc-title a,div#ez-toc-container ul.ez-toc-list a {color: ' . ezTOC_Option::get( 'custom_link_colour' ) . ';}';
332
- $css .= 'div#ez-toc-container ul.ez-toc-list a {color: ' . ezTOC_Option::get( 'custom_link_colour' ) . ';}';
333
- $css .= 'div#ez-toc-container ul.ez-toc-list a:hover {color: ' . ezTOC_Option::get( 'custom_link_hover_colour' ) . ';}';
334
- $css .= 'div#ez-toc-container ul.ez-toc-list a:visited {color: ' . ezTOC_Option::get( 'custom_link_visited_colour' ) . ';}';
335
- }
336
- }
337
-
338
- if ( $css ) {
339
-
340
- wp_add_inline_style( 'ez-toc', $css );
341
- }
342
- }
343
-
344
- /**
345
- * Array search deep.
346
- *
347
- * Search an array recursively for a value.
348
- *
349
- * @link https://stackoverflow.com/a/5427665/5351316
350
- *
351
- * @param $search
352
- * @param array $array
353
- * @param string $mode
354
- *
355
- * @return bool
356
- */
357
- public static function array_search_deep( $search, array $array, $mode = 'value' ) {
358
-
359
- foreach ( new RecursiveIteratorIterator( new RecursiveArrayIterator( $array ) ) as $key => $value ) {
360
-
361
- if ( $search === ${${"mode"}} ) {
362
- return true;
363
- }
364
- }
365
-
366
- return false;
367
- }
368
-
369
- /**
370
- * Returns true if the table of contents is eligible to be printed, false otherwise.
371
- *
372
- * NOTE: Must bve use only within the loop.
373
- *
374
- * @access public
375
- * @since 1.0
376
- * @static
377
- *
378
- * @param WP_Post $post
379
- *
380
- * @return bool
381
- */
382
- public static function is_eligible( $post ) {
383
-
384
- //global $wp_current_filter;
385
-
386
- if ( empty( $post ) || ! $post instanceof WP_Post ) {
387
-
388
- Debug::log( 'not_instance_of_post', 'Not an instance if `WP_Post`.', $post );
389
- return false;
390
- }
391
-
392
- // This can likely be removed since it is checked in maybeApplyTheContentFilter().
393
- // Do not execute if root filter is one of those in the array.
394
- //if ( in_array( $wp_current_filter[0], array( 'get_the_excerpt', 'wp_head' ), true ) ) {
395
- //
396
- // return false;
397
- //}
398
-
399
- if ( has_shortcode( $post->post_content, apply_filters( 'ez_toc_shortcode', 'toc' ) ) ||
400
- has_shortcode( $post->post_content, 'ez-toc' ) ) {
401
-
402
- Debug::log( 'has_ez_toc_shortcode', 'Has instance of shortcode.', true );
403
- return true;
404
- }
405
-
406
- if ( is_front_page() && ! ezTOC_Option::get( 'include_homepage' ) ) {
407
-
408
- Debug::log( 'is_front_page', 'Is frontpage, TOC is not enabled.', false );
409
- return false;
410
- }
411
-
412
- $type = get_post_type( $post->ID );
413
-
414
- Debug::log( 'current_post_type', 'Post type is.', $type );
415
-
416
- $enabled = in_array( $type, ezTOC_Option::get( 'enabled_post_types', array() ), true );
417
- $insert = in_array( $type, ezTOC_Option::get( 'auto_insert_post_types', array() ), true );
418
-
419
- Debug::log( 'is_supported_post_type', 'Is supported post type?', $enabled );
420
- Debug::log( 'is_auto_insert_post_type', 'Is auto insert for post types?', $insert );
421
-
422
- if ( $insert || $enabled ) {
423
-
424
- if ( ezTOC_Option::get( 'restrict_path' ) ) {
425
-
426
- /**
427
- * @link https://wordpress.org/support/topic/restrict-path-logic-does-not-work-correctly?
428
- */
429
- if ( false !== strpos( ezTOC_Option::get( 'restrict_path' ), $_SERVER['REQUEST_URI'] ) ) {
430
-
431
- Debug::log( 'is_restricted_path', 'In restricted path, post not eligible.', ezTOC_Option::get( 'restrict_path' ) );
432
- return false;
433
-
434
- } else {
435
-
436
- Debug::log( 'is_not_restricted_path', 'Not in restricted path, post is eligible.', ezTOC_Option::get( 'restrict_path' ) );
437
- return true;
438
- }
439
-
440
- } else {
441
-
442
- if ( $insert && 1 === (int) get_post_meta( $post->ID, '_ez-toc-disabled', true ) ) {
443
-
444
- Debug::log( 'is_auto_insert_disable_post_meta', 'Auto insert enabled and disable TOC is enabled in post meta.', false );
445
- return false;
446
-
447
- } elseif ( $insert && 0 === (int) get_post_meta( $post->ID, '_ez-toc-disabled', true ) ) {
448
-
449
- Debug::log( 'is_auto_insert_enabled_post_meta', 'Auto insert enabled and disable TOC is not enabled in post meta.', true );
450
- return true;
451
-
452
- } elseif ( $enabled && 1 === (int) get_post_meta( $post->ID, '_ez-toc-insert', true ) ) {
453
-
454
- Debug::log( 'is_supported_post_type_disable_insert_post_meta', 'Supported post type and insert TOC is enabled in post meta.', true );
455
- return true;
456
-
457
- } elseif ( $enabled && $insert ) {
458
-
459
- Debug::log( 'supported_post_type_and_auto_insert', 'Supported post type and auto insert TOC is enabled.', true );
460
- return true;
461
- }
462
-
463
- Debug::log( 'not_auto_insert_or_not_supported_post_type', 'Not supported post type or insert TOC is disabled.', false );
464
- return false;
465
- }
466
-
467
- } else {
468
-
469
- Debug::log( 'not_auto_insert_and_not_supported post_type', 'Not supported post type and do not auto insert TOC.', false );
470
- return false;
471
- }
472
- }
473
-
474
- /**
475
- * Get TOC from store and if not in store process post and add it to the store.
476
- *
477
- * @since 2.0
478
- *
479
- * @param int $id
480
- *
481
- * @return ezTOC_Post|null
482
- */
483
- public static function get( $id ) {
484
-
485
- $post = null;
486
-
487
- if ( isset( self::$store[ $id ] ) && self::$store[ $id ] instanceof ezTOC_Post ) {
488
-
489
- $post = self::$store[ $id ];
490
-
491
- } else {
492
-
493
- $post = ezTOC_Post::get( get_the_ID() );
494
-
495
- if ( $post instanceof ezTOC_Post ) {
496
-
497
- self::$store[ $id ] = $post;
498
- }
499
- }
500
-
501
- return $post;
502
- }
503
-
504
- /**
505
- * Callback for the registered shortcode `[ez-toc]`
506
- *
507
- * NOTE: Shortcode is run before the callback @see ezTOC::the_content() for the `the_content` filter
508
- *
509
- * @access private
510
- * @since 1.3
511
- *
512
- * @param array|string $atts Shortcode attributes array or empty string.
513
- * @param string $content The enclosed content (if the shortcode is used in its enclosing form)
514
- * @param string $tag Shortcode name.
515
- *
516
- * @return string
517
- */
518
- public static function shortcode( $atts, $content, $tag ) {
519
-
520
- static $run = true;
521
- $html = '';
522
-
523
- if ( $run ) {
524
-
525
- $post = self::get( get_the_ID() );
526
-
527
- if ( ! $post instanceof ezTOC_Post ) {
528
-
529
- Debug::log( 'not_instance_of_post', 'Not an instance if `WP_Post`.', get_the_ID() );
530
-
531
- return Debug::log()->appendTo( $content );
532
- }
533
-
534
- $html = $post->getTOC();
535
- $run = false;
536
- }
537
-
538
- return $html;
539
- }
540
-
541
- /**
542
- * Whether or not apply `the_content` filter.
543
- *
544
- * @since 2.0
545
- *
546
- * @return bool
547
- */
548
- private static function maybeApplyTheContentFilter() {
549
-
550
- $apply = true;
551
-
552
- global $wp_current_filter;
553
-
554
- // Do not execute if root current filter is one of those in the array.
555
- if ( in_array( $wp_current_filter[0], array( 'get_the_excerpt', 'init', 'wp_head' ), true ) ) {
556
-
557
- $apply = false;
558
- }
559
-
560
- // bail if feed, search or archive
561
- if ( is_feed() || is_search() || is_archive() ) {
562
-
563
- $apply = false;
564
- }
565
-
566
- /**
567
- * Whether or not to apply `the_content` filter callback.
568
- *
569
- * @see ezTOC::the_content()
570
- *
571
- * @since 2.0
572
- *
573
- * @param bool $apply
574
- */
575
- return apply_filters( 'ez_toc_maybe_apply_the_content_filter', $apply );
576
- }
577
-
578
- /**
579
- * Callback for the `the_content` filter.
580
- *
581
- * This will add the inline table of contents page anchors to the post content. It will also insert the
582
- * table of contents inline with the post content as defined by the user defined preference.
583
- *
584
- * @since 1.0
585
- *
586
- * @param string $content
587
- *
588
- * @return string
589
- */
590
- public static function the_content( $content ) {
591
-
592
- $maybeApplyFilter = self::maybeApplyTheContentFilter();
593
-
594
- Debug::log( 'the_content_filter', 'The `the_content` filter applied.', $maybeApplyFilter );
595
-
596
- if ( ! $maybeApplyFilter ) {
597
-
598
- return Debug::log()->appendTo( $content );
599
- }
600
-
601
- // Bail if post not eligible and widget is not active.
602
- $isEligible = self::is_eligible( get_post() );
603
-
604
- Debug::log( 'post_eligible', 'Post eligible.', $isEligible );
605
-
606
- if ( ! $isEligible && ! is_active_widget( false, false, 'ezw_tco' ) ) {
607
-
608
- return Debug::log()->appendTo( $content );
609
- }
610
-
611
- $post = self::get( get_the_ID() );
612
-
613
- if ( ! $post instanceof ezTOC_Post ) {
614
-
615
- Debug::log( 'not_instance_of_post', 'Not an instance if `WP_Post`.', get_the_ID() );
616
-
617
- return Debug::log()->appendTo( $content );
618
- }
619
-
620
- // Bail if no headings found.
621
- if ( ! $post->hasTOCItems() ) {
622
-
623
- return Debug::log()->appendTo( $content );
624
- }
625
-
626
- $find = $post->getHeadings();
627
- $replace = $post->getHeadingsWithAnchors();
628
- $toc = $post->getTOC();
629
-
630
- $headings = implode( PHP_EOL, $find );
631
- $anchors = implode( PHP_EOL, $replace );
632
-
633
- $headingRows = count( $find ) + 1;
634
- $anchorRows = count( $replace ) + 1;
635
-
636
- $style = "background-image: linear-gradient(#F1F1F1 50%, #F9F9F9 50%); background-size: 100% 4em; border: 1px solid #CCC; font-family: monospace; font-size: 1em; line-height: 2em; margin: 0 auto; overflow: auto; padding: 0 8px 4px; white-space: nowrap; width: 100%;";
637
-
638
- Debug::log(
639
- 'found_post_headings',
640
- 'Found headings:',
641
- "<textarea rows='{$headingRows}' style='{$style}' wrap='soft'>{$headings}</textarea>"
642
- );
643
-
644
- Debug::log(
645
- 'replace_post_headings',
646
- 'Replace found headings with:',
647
- "<textarea rows='{$anchorRows}' style='{$style}' wrap='soft'>{$anchors}</textarea>"
648
- );
649
-
650
- // If shortcode used or post not eligible, return content with anchored headings.
651
- if ( strpos( $content, 'ez-toc-container' ) || ! $isEligible ) {
652
-
653
- Debug::log( 'shortcode_found', 'Shortcode found, add links to content.', true );
654
-
655
- return mb_find_replace( $find, $replace, $content );
656
- }
657
-
658
- $position = ezTOC_Option::get( 'position' );
659
-
660
- Debug::log( 'toc_insert_position', 'Insert TOC at position', $position );
661
-
662
- // else also add toc to content
663
- switch ( $position ) {
664
-
665
- case 'top':
666
- $content = $toc . mb_find_replace( $find, $replace, $content );
667
- break;
668
-
669
- case 'bottom':
670
- $content = mb_find_replace( $find, $replace, $content ) . $toc;
671
- break;
672
-
673
- case 'after':
674
- $replace[0] = $replace[0] . $toc;
675
- $content = mb_find_replace( $find, $replace, $content );
676
- break;
677
-
678
- case 'before':
679
- default:
680
- //$replace[0] = $html . $replace[0];
681
- $content = mb_find_replace( $find, $replace, $content );
682
-
683
- /**
684
- * @link https://wordpress.org/support/topic/php-notice-undefined-offset-8/
685
- */
686
- if ( ! array_key_exists( 0, $replace ) ) {
687
- break;
688
- }
689
-
690
- $pattern = '`<h[1-6]{1}[^>]*' . preg_quote( $replace[0], '`' ) . '`msuU';
691
- $result = preg_match( $pattern, $content, $matches );
692
-
693
- /*
694
- * Try to place TOC before the first heading found in eligible heading, failing that,
695
- * insert TOC at top of content.
696
- */
697
- if ( 1 === $result ) {
698
-
699
- Debug::log( 'toc_insert_position_found', 'Insert TOC before first eligible heading.', $result );
700
-
701
- $start = strpos( $content, $matches[0] );
702
- $content = substr_replace( $content, $toc, $start, 0 );
703
-
704
- } else {
705
-
706
- Debug::log( 'toc_insert_position_not_found', 'Insert TOC before first eligible heading not found.', $result );
707
-
708
- // Somehow, there are scenarios where the processing get this far and
709
- // the TOC is being added to pages where it should not. Disable for now.
710
- //$content = $html . $content;
711
- }
712
- }
713
-
714
- return Debug::log()->appendTo( $content );
715
- }
716
-
717
- }
718
-
719
- /**
720
- * The main function responsible for returning the Easy Table of Contents instance to functions everywhere.
721
- *
722
- * Use this function like you would a global variable, except without needing to declare the global.
723
- *
724
- * Example: <?php $instance = ezTOC(); ?>
725
- *
726
- * @access public
727
- * @since 1.0
728
- *
729
- * @return ezTOC
730
- */
731
- function ezTOC() {
732
-
733
- return ezTOC::instance();
734
- }
735
-
736
- // Start Easy Table of Contents.
737
- add_action( 'plugins_loaded', 'ezTOC' );
738
- }
1
+ <?php
2
+ /**
3
+ * Plugin Name: Easy Table of Contents
4
+ * Plugin URI: https://magazine3.company/
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.22
7
+ * Author: Magazine3
8
+ * Author URI: https://magazine3.company/
9
+ * Text Domain: easy-table-of-contents
10
+ * Domain Path: /languages
11
+ *
12
+ * Copyright 2022 Magazine3 ( email : team@magazine3.in )
13
+ *
14
+ * Easy Table of Contents is free software; you can redistribute it and/or modify
15
+ * it under the terms of the GNU General Public License, version 2, as
16
+ * published by the Free Software Foundation.
17
+ *
18
+ * This program is distributed in the hope that it will be useful,
19
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
+ * GNU General Public License for more details.
22
+ *
23
+ * You should have received a copy of the GNU General Public License
24
+ * along with Easy Table of Contents; if not, see <http://www.gnu.org/licenses/>.
25
+ *
26
+ * @package Easy Table of Contents
27
+ * @category Plugin
28
+ * @author Magazine3
29
+ * @version 2.0.22
30
+ */
31
+
32
+ use Easy_Plugins\Table_Of_Contents\Debug;
33
+ use function Easy_Plugins\Table_Of_Contents\String\mb_find_replace;
34
+
35
+ // Exit if accessed directly
36
+ if ( ! defined( 'ABSPATH' ) ) exit;
37
+
38
+ if ( ! class_exists( 'ezTOC' ) ) {
39
+
40
+ /**
41
+ * Class ezTOC
42
+ */
43
+ final class ezTOC {
44
+
45
+ /**
46
+ * Current version.
47
+ *
48
+ * @since 1.0
49
+ * @var string
50
+ */
51
+ const VERSION = '2.0.22';
52
+
53
+ /**
54
+ * Stores the instance of this class.
55
+ *
56
+ * @access private
57
+ * @since 1.0
58
+ * @static
59
+ *
60
+ * @var ezTOC
61
+ */
62
+ private static $instance;
63
+
64
+ /**
65
+ * @since 2.0
66
+ * @var array
67
+ */
68
+ private static $store = array();
69
+
70
+ /**
71
+ * A dummy constructor to prevent the class from being loaded more than once.
72
+ *
73
+ * @access public
74
+ * @since 1.0
75
+ */
76
+ public function __construct() { /* Do nothing here */ }
77
+
78
+ /**
79
+ * @access private
80
+ * @since 1.0
81
+ * @static
82
+ *
83
+ * @return ezTOC
84
+ */
85
+ public static function instance() {
86
+
87
+ if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) {
88
+
89
+ self::$instance = new self;
90
+
91
+ self::defineConstants();
92
+ self::includes();
93
+ self::hooks();
94
+
95
+ self::loadTextdomain();
96
+ }
97
+
98
+ return self::$instance;
99
+ }
100
+
101
+ /**
102
+ * Define the plugin constants.
103
+ *
104
+ * @access private
105
+ * @since 1.0
106
+ * @static
107
+ */
108
+ private static function defineConstants() {
109
+
110
+ define( 'EZ_TOC_DIR_NAME', plugin_basename( dirname( __FILE__ ) ) );
111
+ define( 'EZ_TOC_BASE_NAME', plugin_basename( __FILE__ ) );
112
+ define( 'EZ_TOC_PATH', dirname( __FILE__ ) );
113
+ define( 'EZ_TOC_URL', plugin_dir_url( __FILE__ ) );
114
+ }
115
+
116
+ /**
117
+ * Includes the plugin dependency files.
118
+ *
119
+ * @access private
120
+ * @since 1.0
121
+ * @static
122
+ */
123
+ private static function includes() {
124
+
125
+ require_once( EZ_TOC_PATH . '/includes/class.options.php' );
126
+
127
+ if ( is_admin() ) {
128
+
129
+ // This must be included after `class.options.php` because it depends on it methods.
130
+ require_once( EZ_TOC_PATH . '/includes/class.admin.php' );
131
+ }
132
+
133
+ require_once( EZ_TOC_PATH . '/includes/class.post.php' );
134
+ require_once( EZ_TOC_PATH . '/includes/class.widget-toc.php' );
135
+ require_once( EZ_TOC_PATH . '/includes/Debug.php' );
136
+ require_once( EZ_TOC_PATH . '/includes/inc.functions.php' );
137
+ require_once( EZ_TOC_PATH . '/includes/inc.string-functions.php' );
138
+
139
+ require_once( EZ_TOC_PATH . '/includes/inc.plugin-compatibility.php' );
140
+ }
141
+
142
+ /**
143
+ * Add the core action filter hook.
144
+ *
145
+ * @access private
146
+ * @since 1.0
147
+ * @static
148
+ */
149
+ private static function hooks() {
150
+
151
+ //add_action( 'plugins_loaded', array( __CLASS__, 'loadTextdomain' ) );
152
+ add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueueScripts' ) );
153
+
154
+ // Run after shortcodes are interpreted (priority 10).
155
+ add_filter( 'the_content', array( __CLASS__, 'the_content' ), 100 );
156
+ add_shortcode( 'ez-toc', array( __CLASS__, 'shortcode' ) );
157
+ add_shortcode( apply_filters( 'ez_toc_shortcode', 'toc' ), array( __CLASS__, 'shortcode' ) );
158
+ }
159
+
160
+ /**
161
+ * Load the plugin translation.
162
+ *
163
+ * Credit: Adapted from Ninja Forms / Easy Digital Downloads.
164
+ *
165
+ * @access private
166
+ * @since 1.0
167
+ * @static
168
+ *
169
+ * @uses apply_filters()
170
+ * @uses get_locale()
171
+ * @uses load_textdomain()
172
+ * @uses load_plugin_textdomain()
173
+ *
174
+ * @return void
175
+ */
176
+ public static function loadTextdomain() {
177
+
178
+ // Plugin textdomain. This should match the one set in the plugin header.
179
+ $domain = 'easy-table-of-contents';
180
+
181
+ // Set filter for plugin's languages directory
182
+ $languagesDirectory = apply_filters( "ez_{$domain}_languages_directory", EZ_TOC_DIR_NAME . '/languages/' );
183
+
184
+ // Traditional WordPress plugin locale filter
185
+ $locale = apply_filters( 'plugin_locale', get_locale(), $domain );
186
+ $fileName = sprintf( '%1$s-%2$s.mo', $domain, $locale );
187
+
188
+ // Setup paths to current locale file
189
+ $local = $languagesDirectory . $fileName;
190
+ $global = WP_LANG_DIR . "/{$domain}/" . $fileName;
191
+
192
+ if ( file_exists( $global ) ) {
193
+
194
+ // Look in global `../wp-content/languages/{$domain}/` folder.
195
+ load_textdomain( $domain, $global );
196
+
197
+ } elseif ( file_exists( $local ) ) {
198
+
199
+ // Look in local `../wp-content/plugins/{plugin-directory}/languages/` folder.
200
+ load_textdomain( $domain, $local );
201
+
202
+ } else {
203
+
204
+ // Load the default language files
205
+ load_plugin_textdomain( $domain, false, $languagesDirectory );
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Call back for the `wp_enqueue_scripts` action.
211
+ *
212
+ * Register and enqueue CSS and javascript files for frontend.
213
+ *
214
+ * @access private
215
+ * @since 1.0
216
+ * @static
217
+ */
218
+ public static function enqueueScripts() {
219
+
220
+ // If SCRIPT_DEBUG is set and TRUE load the non-minified JS files, otherwise, load the minified files.
221
+ $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
222
+
223
+ $js_vars = array();
224
+
225
+ $isEligible = self::is_eligible( get_post() );
226
+
227
+ if ( ! $isEligible && ! is_active_widget( false, false, 'ezw_tco' ) ) {
228
+ return false;
229
+ }
230
+
231
+ wp_register_style( 'ez-icomoon', EZ_TOC_URL . "vendor/icomoon/style$min.css", array(), ezTOC::VERSION );
232
+ wp_register_style( 'ez-toc', EZ_TOC_URL . "assets/css/screen$min.css", array( 'ez-icomoon' ), ezTOC::VERSION );
233
+
234
+ wp_register_script( 'js-cookie', EZ_TOC_URL . "vendor/js-cookie/js.cookie$min.js", array(), '2.2.1', TRUE );
235
+ wp_register_script( 'jquery-smooth-scroll', EZ_TOC_URL . "vendor/smooth-scroll/jquery.smooth-scroll$min.js", array( 'jquery' ), '2.2.0', TRUE );
236
+ wp_register_script( 'jquery-sticky-kit', EZ_TOC_URL . "vendor/sticky-kit/jquery.sticky-kit$min.js", array( 'jquery' ), '1.9.2', TRUE );
237
+
238
+ if (ezTOC_Option::get( 'toc_loading' ) != 'css') {
239
+ wp_register_script(
240
+ 'ez-toc-js',
241
+ EZ_TOC_URL . "assets/js/front{$min}.js",
242
+ array( 'jquery-smooth-scroll', 'js-cookie', 'jquery-sticky-kit' ),
243
+ ezTOC::VERSION . '-' . filemtime( EZ_TOC_PATH . "/assets/js/front{$min}.js" ),
244
+ true
245
+ );
246
+ }
247
+
248
+ if ( ! ezTOC_Option::get( 'exclude_css' ) ) {
249
+
250
+ wp_enqueue_style( 'ez-toc' );
251
+ self::inlineCSS();
252
+ }
253
+
254
+ if ( ezTOC_Option::get( 'smooth_scroll' ) ) {
255
+
256
+ $js_vars['smooth_scroll'] = true;
257
+ }
258
+
259
+ //wp_enqueue_script( 'ez-toc-js' );
260
+
261
+ if ( ezTOC_Option::get( 'show_heading_text' ) && ezTOC_Option::get( 'visibility' ) ) {
262
+
263
+ $width = ezTOC_Option::get( 'width' ) !== 'custom' ? ezTOC_Option::get( 'width' ) : ezTOC_Option::get( 'width_custom' ) . ezTOC_Option::get( 'width_custom_units' );
264
+
265
+ $js_vars['visibility_hide_by_default'] = ezTOC_Option::get( 'visibility_hide_by_default' ) ? true : false;
266
+
267
+ $js_vars['width'] = esc_js( $width );
268
+ }
269
+
270
+ $offset = wp_is_mobile() ? ezTOC_Option::get( 'mobile_smooth_scroll_offset', 0 ) : ezTOC_Option::get( 'smooth_scroll_offset', 30 );
271
+
272
+ $js_vars['scroll_offset'] = esc_js( $offset );
273
+
274
+ if ( ezTOC_Option::get( 'widget_affix_selector' ) ) {
275
+
276
+ $js_vars['affixSelector'] = ezTOC_Option::get( 'widget_affix_selector' );
277
+ }
278
+
279
+ if ( 0 < count( $js_vars ) ) {
280
+
281
+ wp_localize_script( 'ez-toc-js', 'ezTOC', $js_vars );
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Prints out inline CSS after the core CSS file to allow overriding core styles via options.
287
+ *
288
+ * @access private
289
+ * @since 1.0
290
+ * @static
291
+ */
292
+ public static function inlineCSS() {
293
+
294
+ $css = '';
295
+
296
+ if ( ! ezTOC_Option::get( 'exclude_css' ) ) {
297
+
298
+ $css .= 'div#ez-toc-container p.ez-toc-title {font-size: ' . ezTOC_Option::get( 'title_font_size', 120 ) . ezTOC_Option::get( 'title_font_size_units', '%' ) . ';}';
299
+ $css .= 'div#ez-toc-container p.ez-toc-title {font-weight: ' . ezTOC_Option::get( 'title_font_weight', 500 ) . ';}';
300
+ $css .= 'div#ez-toc-container ul li {font-size: ' . ezTOC_Option::get( 'font_size' ) . ezTOC_Option::get( 'font_size_units' ) . ';}';
301
+
302
+ if ( ezTOC_Option::get( 'theme' ) === 'custom' || ezTOC_Option::get( 'width' ) != 'auto' ) {
303
+
304
+ $css .= 'div#ez-toc-container {';
305
+
306
+ if ( ezTOC_Option::get( 'theme' ) === 'custom' ) {
307
+
308
+ $css .= 'background: ' . ezTOC_Option::get( 'custom_background_colour' ) . ';border: 1px solid ' . ezTOC_Option::get( 'custom_border_colour' ) . ';';
309
+ }
310
+
311
+ if ( 'auto' !== ezTOC_Option::get( 'width' ) ) {
312
+
313
+ $css .= 'width: ';
314
+
315
+ if ( 'custom' !== ezTOC_Option::get( 'width' ) ) {
316
+
317
+ $css .= ezTOC_Option::get( 'width' );
318
+
319
+ } else {
320
+
321
+ $css .= ezTOC_Option::get( 'width_custom' ) . ezTOC_Option::get( 'width_custom_units' );
322
+ }
323
+
324
+ $css .= ';';
325
+ }
326
+
327
+ $css .= '}';
328
+ }
329
+
330
+ if ( 'custom' === ezTOC_Option::get( 'theme' ) ) {
331
+
332
+ $css .= 'div#ez-toc-container p.ez-toc-title {color: ' . ezTOC_Option::get( 'custom_title_colour' ) . ';}';
333
+ //$css .= 'div#ez-toc-container p.ez-toc-title a,div#ez-toc-container ul.ez-toc-list a {color: ' . ezTOC_Option::get( 'custom_link_colour' ) . ';}';
334
+ $css .= 'div#ez-toc-container ul.ez-toc-list a {color: ' . ezTOC_Option::get( 'custom_link_colour' ) . ';}';
335
+ $css .= 'div#ez-toc-container ul.ez-toc-list a:hover {color: ' . ezTOC_Option::get( 'custom_link_hover_colour' ) . ';}';
336
+ $css .= 'div#ez-toc-container ul.ez-toc-list a:visited {color: ' . ezTOC_Option::get( 'custom_link_visited_colour' ) . ';}';
337
+ }
338
+ }
339
+
340
+ if ( $css ) {
341
+
342
+ wp_add_inline_style( 'ez-toc', $css );
343
+ }
344
+ }
345
+
346
+ /**
347
+ * Array search deep.
348
+ *
349
+ * Search an array recursively for a value.
350
+ *
351
+ * @link https://stackoverflow.com/a/5427665/5351316
352
+ *
353
+ * @param $search
354
+ * @param array $array
355
+ * @param string $mode
356
+ *
357
+ * @return bool
358
+ */
359
+ public static function array_search_deep( $search, array $array, $mode = 'value' ) {
360
+
361
+ foreach ( new RecursiveIteratorIterator( new RecursiveArrayIterator( $array ) ) as $key => $value ) {
362
+
363
+ if ( $search === ${${"mode"}} ) {
364
+ return true;
365
+ }
366
+ }
367
+
368
+ return false;
369
+ }
370
+
371
+ /**
372
+ * Returns true if the table of contents is eligible to be printed, false otherwise.
373
+ *
374
+ * NOTE: Must bve use only within the loop.
375
+ *
376
+ * @access public
377
+ * @since 1.0
378
+ * @static
379
+ *
380
+ * @param WP_Post $post
381
+ *
382
+ * @return bool
383
+ */
384
+ public static function is_eligible( $post ) {
385
+
386
+ //global $wp_current_filter;
387
+
388
+ if ( empty( $post ) || ! $post instanceof WP_Post ) {
389
+
390
+ Debug::log( 'not_instance_of_post', 'Not an instance if `WP_Post`.', $post );
391
+ return false;
392
+ }
393
+
394
+ // This can likely be removed since it is checked in maybeApplyTheContentFilter().
395
+ // Do not execute if root filter is one of those in the array.
396
+ //if ( in_array( $wp_current_filter[0], array( 'get_the_excerpt', 'wp_head' ), true ) ) {
397
+ //
398
+ // return false;
399
+ //}
400
+
401
+ if ( has_shortcode( $post->post_content, apply_filters( 'ez_toc_shortcode', 'toc' ) ) ||
402
+ has_shortcode( $post->post_content, 'ez-toc' ) ) {
403
+
404
+ Debug::log( 'has_ez_toc_shortcode', 'Has instance of shortcode.', true );
405
+ return true;
406
+ }
407
+
408
+ if ( is_front_page() && ! ezTOC_Option::get( 'include_homepage' ) ) {
409
+
410
+ Debug::log( 'is_front_page', 'Is frontpage, TOC is not enabled.', false );
411
+ return false;
412
+ }
413
+
414
+ $type = get_post_type( $post->ID );
415
+
416
+ Debug::log( 'current_post_type', 'Post type is.', $type );
417
+
418
+ $enabled = in_array( $type, ezTOC_Option::get( 'enabled_post_types', array() ), true );
419
+ $insert = in_array( $type, ezTOC_Option::get( 'auto_insert_post_types', array() ), true );
420
+
421
+ Debug::log( 'is_supported_post_type', 'Is supported post type?', $enabled );
422
+ Debug::log( 'is_auto_insert_post_type', 'Is auto insert for post types?', $insert );
423
+
424
+ if ( $insert || $enabled ) {
425
+
426
+ if ( ezTOC_Option::get( 'restrict_path' ) ) {
427
+
428
+ /**
429
+ * @link https://wordpress.org/support/topic/restrict-path-logic-does-not-work-correctly?
430
+ */
431
+ if ( false !== strpos( ezTOC_Option::get( 'restrict_path' ), $_SERVER['REQUEST_URI'] ) ) {
432
+
433
+ Debug::log( 'is_restricted_path', 'In restricted path, post not eligible.', ezTOC_Option::get( 'restrict_path' ) );
434
+ return false;
435
+
436
+ } else {
437
+
438
+ Debug::log( 'is_not_restricted_path', 'Not in restricted path, post is eligible.', ezTOC_Option::get( 'restrict_path' ) );
439
+ return true;
440
+ }
441
+
442
+ } else {
443
+
444
+ if ( $insert && 1 === (int) get_post_meta( $post->ID, '_ez-toc-disabled', true ) ) {
445
+
446
+ Debug::log( 'is_auto_insert_disable_post_meta', 'Auto insert enabled and disable TOC is enabled in post meta.', false );
447
+ return false;
448
+
449
+ } elseif ( $insert && 0 === (int) get_post_meta( $post->ID, '_ez-toc-disabled', true ) ) {
450
+
451
+ Debug::log( 'is_auto_insert_enabled_post_meta', 'Auto insert enabled and disable TOC is not enabled in post meta.', true );
452
+ return true;
453
+
454
+ } elseif ( $enabled && 1 === (int) get_post_meta( $post->ID, '_ez-toc-insert', true ) ) {
455
+
456
+ Debug::log( 'is_supported_post_type_disable_insert_post_meta', 'Supported post type and insert TOC is enabled in post meta.', true );
457
+ return true;
458
+
459
+ } elseif ( $enabled && $insert ) {
460
+
461
+ Debug::log( 'supported_post_type_and_auto_insert', 'Supported post type and auto insert TOC is enabled.', true );
462
+ return true;
463
+ }
464
+
465
+ Debug::log( 'not_auto_insert_or_not_supported_post_type', 'Not supported post type or insert TOC is disabled.', false );
466
+ return false;
467
+ }
468
+
469
+ } else {
470
+
471
+ Debug::log( 'not_auto_insert_and_not_supported post_type', 'Not supported post type and do not auto insert TOC.', false );
472
+ return false;
473
+ }
474
+ }
475
+
476
+ /**
477
+ * Get TOC from store and if not in store process post and add it to the store.
478
+ *
479
+ * @since 2.0
480
+ *
481
+ * @param int $id
482
+ *
483
+ * @return ezTOC_Post|null
484
+ */
485
+ public static function get( $id ) {
486
+
487
+ $post = null;
488
+
489
+ if ( isset( self::$store[ $id ] ) && self::$store[ $id ] instanceof ezTOC_Post ) {
490
+
491
+ $post = self::$store[ $id ];
492
+
493
+ } else {
494
+
495
+ $post = ezTOC_Post::get( get_the_ID() );
496
+
497
+ if ( $post instanceof ezTOC_Post ) {
498
+
499
+ self::$store[ $id ] = $post;
500
+ }
501
+ }
502
+
503
+ return $post;
504
+ }
505
+
506
+ /**
507
+ * Callback for the registered shortcode `[ez-toc]`
508
+ *
509
+ * NOTE: Shortcode is run before the callback @see ezTOC::the_content() for the `the_content` filter
510
+ *
511
+ * @access private
512
+ * @since 1.3
513
+ *
514
+ * @param array|string $atts Shortcode attributes array or empty string.
515
+ * @param string $content The enclosed content (if the shortcode is used in its enclosing form)
516
+ * @param string $tag Shortcode name.
517
+ *
518
+ * @return string
519
+ */
520
+ public static function shortcode( $atts, $content, $tag ) {
521
+
522
+ static $run = true;
523
+ $html = '';
524
+
525
+ if ( $run ) {
526
+
527
+ $post = self::get( get_the_ID() );
528
+
529
+ if ( ! $post instanceof ezTOC_Post ) {
530
+
531
+ Debug::log( 'not_instance_of_post', 'Not an instance if `WP_Post`.', get_the_ID() );
532
+
533
+ return Debug::log()->appendTo( $content );
534
+ }
535
+
536
+ $html = $post->getTOC();
537
+ $run = false;
538
+ }
539
+
540
+ return $html;
541
+ }
542
+
543
+ /**
544
+ * Whether or not apply `the_content` filter.
545
+ *
546
+ * @since 2.0
547
+ *
548
+ * @return bool
549
+ */
550
+ private static function maybeApplyTheContentFilter() {
551
+
552
+ $apply = true;
553
+
554
+ global $wp_current_filter;
555
+
556
+ // Do not execute if root current filter is one of those in the array.
557
+ if ( in_array( $wp_current_filter[0], array( 'get_the_excerpt', 'init', 'wp_head' ), true ) ) {
558
+
559
+ $apply = false;
560
+ }
561
+
562
+ // bail if feed, search or archive
563
+ if ( is_feed() || is_search() || is_archive() ) {
564
+
565
+ $apply = false;
566
+ }
567
+
568
+ /**
569
+ * Whether or not to apply `the_content` filter callback.
570
+ *
571
+ * @see ezTOC::the_content()
572
+ *
573
+ * @since 2.0
574
+ *
575
+ * @param bool $apply
576
+ */
577
+ return apply_filters( 'ez_toc_maybe_apply_the_content_filter', $apply );
578
+ }
579
+
580
+ /**
581
+ * Callback for the `the_content` filter.
582
+ *
583
+ * This will add the inline table of contents page anchors to the post content. It will also insert the
584
+ * table of contents inline with the post content as defined by the user defined preference.
585
+ *
586
+ * @since 1.0
587
+ *
588
+ * @param string $content
589
+ *
590
+ * @return string
591
+ */
592
+ public static function the_content( $content ) {
593
+ $maybeApplyFilter = self::maybeApplyTheContentFilter();
594
+
595
+ Debug::log( 'the_content_filter', 'The `the_content` filter applied.', $maybeApplyFilter );
596
+
597
+ if ( ! $maybeApplyFilter ) {
598
+
599
+ return Debug::log()->appendTo( $content );
600
+ }
601
+
602
+ // Bail if post not eligible and widget is not active.
603
+ $isEligible = self::is_eligible( get_post() );
604
+
605
+ Debug::log( 'post_eligible', 'Post eligible.', $isEligible );
606
+
607
+ if ( ! $isEligible && ! is_active_widget( false, false, 'ezw_tco' ) ) {
608
+
609
+ return Debug::log()->appendTo( $content );
610
+ }
611
+
612
+ $post = self::get( get_the_ID() );
613
+
614
+ if ( ! $post instanceof ezTOC_Post ) {
615
+
616
+ Debug::log( 'not_instance_of_post', 'Not an instance if `WP_Post`.', get_the_ID() );
617
+
618
+ return Debug::log()->appendTo( $content );
619
+ }
620
+
621
+ // Bail if no headings found.
622
+ if ( ! $post->hasTOCItems() ) {
623
+
624
+ return Debug::log()->appendTo( $content );
625
+ }
626
+
627
+ $find = $post->getHeadings();
628
+ $replace = $post->getHeadingsWithAnchors();
629
+ $toc = $post->getTOC();
630
+ $headings = implode( PHP_EOL, $find );
631
+ $anchors = implode( PHP_EOL, $replace );
632
+
633
+ $headingRows = count( $find ) + 1;
634
+ $anchorRows = count( $replace ) + 1;
635
+
636
+ $style = "background-image: linear-gradient(#F1F1F1 50%, #F9F9F9 50%); background-size: 100% 4em; border: 1px solid #CCC; font-family: monospace; font-size: 1em; line-height: 2em; margin: 0 auto; overflow: auto; padding: 0 8px 4px; white-space: nowrap; width: 100%;";
637
+
638
+ Debug::log(
639
+ 'found_post_headings',
640
+ 'Found headings:',
641
+ "<textarea rows='{$headingRows}' style='{$style}' wrap='soft'>{$headings}</textarea>"
642
+ );
643
+
644
+ Debug::log(
645
+ 'replace_post_headings',
646
+ 'Replace found headings with:',
647
+ "<textarea rows='{$anchorRows}' style='{$style}' wrap='soft'>{$anchors}</textarea>"
648
+ );
649
+
650
+ // If shortcode used or post not eligible, return content with anchored headings.
651
+ if ( strpos( $content, 'ez-toc-container' ) || ! $isEligible ) {
652
+
653
+ Debug::log( 'shortcode_found', 'Shortcode found, add links to content.', true );
654
+
655
+ return mb_find_replace( $find, $replace, $content );
656
+ }
657
+
658
+ $position = ezTOC_Option::get( 'position' );
659
+
660
+ Debug::log( 'toc_insert_position', 'Insert TOC at position', $position );
661
+
662
+ // else also add toc to content
663
+ switch ( $position ) {
664
+
665
+ case 'top':
666
+ $content = $toc . mb_find_replace( $find, $replace, $content );
667
+ break;
668
+
669
+ case 'bottom':
670
+ $content = mb_find_replace( $find, $replace, $content ) . $toc;
671
+ break;
672
+
673
+ case 'after':
674
+ $replace[0] = $replace[0] . $toc;
675
+ $content = mb_find_replace( $find, $replace, $content );
676
+ break;
677
+
678
+ case 'before':
679
+ default:
680
+ //$replace[0] = $html . $replace[0];
681
+ $content = mb_find_replace( $find, $replace, $content );
682
+
683
+ /**
684
+ * @link https://wordpress.org/support/topic/php-notice-undefined-offset-8/
685
+ */
686
+ if ( ! array_key_exists( 0, $replace ) ) {
687
+ break;
688
+ }
689
+
690
+ $pattern = '`<h[1-6]{1}[^>]*' . preg_quote( $replace[0], '`' ) . '`msuU';
691
+ $result = preg_match( $pattern, $content, $matches );
692
+
693
+ /*
694
+ * Try to place TOC before the first heading found in eligible heading, failing that,
695
+ * insert TOC at top of content.
696
+ */
697
+ if ( 1 === $result ) {
698
+
699
+ Debug::log( 'toc_insert_position_found', 'Insert TOC before first eligible heading.', $result );
700
+
701
+ $start = strpos( $content, $matches[0] );
702
+ $content = substr_replace( $content, $toc, $start, 0 );
703
+
704
+ } else {
705
+
706
+ Debug::log( 'toc_insert_position_not_found', 'Insert TOC before first eligible heading not found.', $result );
707
+
708
+ // Somehow, there are scenarios where the processing get this far and
709
+ // the TOC is being added to pages where it should not. Disable for now.
710
+ //$content = $html . $content;
711
+ }
712
+ }
713
+
714
+ return Debug::log()->appendTo( $content );
715
+ }
716
+
717
+ }
718
+
719
+ /**
720
+ * The main function responsible for returning the Easy Table of Contents instance to functions everywhere.
721
+ *
722
+ * Use this function like you would a global variable, except without needing to declare the global.
723
+ *
724
+ * Example: <?php $instance = ezTOC(); ?>
725
+ *
726
+ * @access public
727
+ * @since 1.0
728
+ *
729
+ * @return ezTOC
730
+ */
731
+ function ezTOC() {
732
+
733
+ return ezTOC::instance();
734
+ }
735
+
736
+ // Start Easy Table of Contents.
737
+ add_action( 'plugins_loaded', 'ezTOC' );
738
+ }
includes/Debug.php CHANGED
@@ -1,180 +1,180 @@
1
- <?php
2
-
3
- namespace Easy_Plugins\Table_Of_Contents;
4
-
5
- use WP_Error;
6
-
7
- /**
8
- * Class Debug
9
- *
10
- * @package Easy_Plugins\Table_Of_Contents
11
- */
12
- final class Debug extends WP_Error {
13
-
14
- /**
15
- * @since 2.0.13
16
- * @var bool
17
- */
18
- protected $display = false;
19
-
20
- /**
21
- * @since 2.0.13
22
- * @var bool
23
- */
24
- protected $enabled = false;
25
-
26
- /**
27
- * @var self
28
- */
29
- private static $instance;
30
-
31
- /**
32
- * Debug constructor.
33
- *
34
- * @since 2.0.13
35
- *
36
- * @param string $code
37
- * @param string $message
38
- * @param string $data
39
- */
40
- public function __construct( $code = '', $message = '', $data = '' ) {
41
-
42
- parent::__construct( $code, $message, $data );
43
- }
44
-
45
- /**
46
- * @since 2.0.14
47
- *
48
- * @param string $code
49
- * @param string $message
50
- * @param string $data
51
- *
52
- * @return Debug
53
- */
54
- public static function log( $code = '', $message = '', $data = '' ) {
55
-
56
- if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) {
57
-
58
- self::$instance = new self( $code, $message, $data );
59
-
60
- self::$instance->display = apply_filters(
61
- 'Easy_Plugins/Table_Of_Contents/Debug/Display',
62
- defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY
63
- );
64
-
65
- self::$instance->enabled = apply_filters(
66
- 'Easy_Plugins/Table_Of_Contents/Debug/Enabled',
67
- ( defined( 'WP_DEBUG' ) && WP_DEBUG ) && current_user_can( 'manage_options' )
68
- );
69
-
70
- } else {
71
-
72
- if ( ! empty( $code ) && ! empty( $message ) ) {
73
-
74
- self::$instance->add( $code, $message, $data );
75
- }
76
- }
77
-
78
- return self::$instance;
79
- }
80
-
81
- /**
82
- * Adds an error or appends an additional message to an existing error.
83
- *
84
- * NOTE: Overrides WP_Error::add() to allow support of passing `false` as `$data`.
85
- *
86
- * @since 2.0.14
87
- *
88
- * @param string|int $code Error code.
89
- * @param string $message Error message.
90
- * @param mixed $data Optional. Error data.
91
- */
92
- public function add( $code, $message, $data = null ) {
93
- $this->errors[ $code ][] = $message;
94
-
95
- if ( ! is_null( $data ) ) {
96
- $this->add_data( $data, $code );
97
- }
98
-
99
- /**
100
- * Fires when an error is added to a WP_Error object.
101
- *
102
- * @since 5.6.0
103
- *
104
- * @param string|int $code Error code.
105
- * @param string $message Error message.
106
- * @param mixed $data Error data. Might be empty.
107
- * @param WP_Error $wp_error The WP_Error object.
108
- */
109
- do_action( 'wp_error_added', $code, $message, $data, $this );
110
- }
111
-
112
- /**
113
- * @since 2.0.13
114
- *
115
- * @param string $content
116
- *
117
- * @return string
118
- */
119
- public function appendTo( $content = '' ) {
120
-
121
- return $content . $this;
122
- }
123
-
124
- /**
125
- * @since 2.0.13
126
- *
127
- * @return string
128
- */
129
- public function dump() {
130
-
131
- $dump = array();
132
-
133
- foreach ( (array) $this->errors as $code => $messages ) {
134
-
135
- $data = $this->get_error_data( $code );
136
- $data = is_string( $data ) ? $data : '<code>' . var_export( $data, true ) . '</code>';
137
- $data = "\t\t<li class=\"ez-toc-debug-message-data\">{$data}</li>" . PHP_EOL;
138
-
139
- array_push(
140
- $dump,
141
- PHP_EOL . "\t<ul class=\"ez-toc-debug-message-{$code}\">" . PHP_EOL . "\t\t<li class=\"ez-toc-debug-message\">" . implode( '</li>' . PHP_EOL . '<li>' . PHP_EOL, $messages ) . '</li>' . PHP_EOL . "{$data}\t</ul>" . PHP_EOL
142
- );
143
- }
144
-
145
- return '<div class="ez-toc-debug-message">' . implode( '</div>' . PHP_EOL . '<div class="ez-toc-debug-message">', $dump ) . '</div>' . PHP_EOL;
146
- }
147
-
148
- /**
149
- * @since 2.0.13
150
- *
151
- * @return string
152
- */
153
- public function __toString() {
154
-
155
- if ( false === $this->enabled ) {
156
-
157
- return '';
158
- }
159
-
160
- if ( false === $this->display ) {
161
-
162
- return '';
163
- }
164
-
165
- if ( ! $this->has_errors() ) {
166
-
167
- return '';
168
- }
169
-
170
- $intro = sprintf(
171
- 'You see the following because <a href="%1$s"><code>WP_DEBUG</code></a> and <a href="%1$s"><code>WP_DEBUG_DISPLAY</code></a> are enabled on this site. Please disabled these to prevent the display of these developers\' debug messages.',
172
- 'https://codex.wordpress.org/WP_DEBUG'
173
- );
174
-
175
- $intro = PHP_EOL . "<p>{$intro}</p>" .PHP_EOL;
176
- $dump = $this->dump();
177
-
178
- return PHP_EOL . "<div class='ez-toc-debug-messages'>{$intro}{$dump}</div>" . PHP_EOL;
179
- }
180
- }
1
+ <?php
2
+
3
+ namespace Easy_Plugins\Table_Of_Contents;
4
+
5
+ use WP_Error;
6
+
7
+ /**
8
+ * Class Debug
9
+ *
10
+ * @package Easy_Plugins\Table_Of_Contents
11
+ */
12
+ final class Debug extends WP_Error {
13
+
14
+ /**
15
+ * @since 2.0.13
16
+ * @var bool
17
+ */
18
+ protected $display = false;
19
+
20
+ /**
21
+ * @since 2.0.13
22
+ * @var bool
23
+ */
24
+ protected $enabled = false;
25
+
26
+ /**
27
+ * @var self
28
+ */
29
+ private static $instance;
30
+
31
+ /**
32
+ * Debug constructor.
33
+ *
34
+ * @since 2.0.13
35
+ *
36
+ * @param string $code
37
+ * @param string $message
38
+ * @param string $data
39
+ */
40
+ public function __construct( $code = '', $message = '', $data = '' ) {
41
+
42
+ parent::__construct( $code, $message, $data );
43
+ }
44
+
45
+ /**
46
+ * @since 2.0.14
47
+ *
48
+ * @param string $code
49
+ * @param string $message
50
+ * @param string $data
51
+ *
52
+ * @return Debug
53
+ */
54
+ public static function log( $code = '', $message = '', $data = '' ) {
55
+
56
+ if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) {
57
+
58
+ self::$instance = new self( $code, $message, $data );
59
+
60
+ self::$instance->display = apply_filters(
61
+ 'Easy_Plugins/Table_Of_Contents/Debug/Display',
62
+ defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY
63
+ );
64
+
65
+ self::$instance->enabled = apply_filters(
66
+ 'Easy_Plugins/Table_Of_Contents/Debug/Enabled',
67
+ ( defined( 'WP_DEBUG' ) && WP_DEBUG ) && current_user_can( 'manage_options' )
68
+ );
69
+
70
+ } else {
71
+
72
+ if ( ! empty( $code ) && ! empty( $message ) ) {
73
+
74
+ self::$instance->add( $code, $message, $data );
75
+ }
76
+ }
77
+
78
+ return self::$instance;
79
+ }
80
+
81
+ /**
82
+ * Adds an error or appends an additional message to an existing error.
83
+ *
84
+ * NOTE: Overrides WP_Error::add() to allow support of passing `false` as `$data`.
85
+ *
86
+ * @since 2.0.14
87
+ *
88
+ * @param string|int $code Error code.
89
+ * @param string $message Error message.
90
+ * @param mixed $data Optional. Error data.
91
+ */
92
+ public function add( $code, $message, $data = null ) {
93
+ $this->errors[ $code ][] = $message;
94
+
95
+ if ( ! is_null( $data ) ) {
96
+ $this->add_data( $data, $code );
97
+ }
98
+
99
+ /**
100
+ * Fires when an error is added to a WP_Error object.
101
+ *
102
+ * @since 5.6.0
103
+ *
104
+ * @param string|int $code Error code.
105
+ * @param string $message Error message.
106
+ * @param mixed $data Error data. Might be empty.
107
+ * @param WP_Error $wp_error The WP_Error object.
108
+ */
109
+ do_action( 'wp_error_added', $code, $message, $data, $this );
110
+ }
111
+
112
+ /**
113
+ * @since 2.0.13
114
+ *
115
+ * @param string $content
116
+ *
117
+ * @return string
118
+ */
119
+ public function appendTo( $content = '' ) {
120
+
121
+ return $content . $this;
122
+ }
123
+
124
+ /**
125
+ * @since 2.0.13
126
+ *
127
+ * @return string
128
+ */
129
+ public function dump() {
130
+
131
+ $dump = array();
132
+
133
+ foreach ( (array) $this->errors as $code => $messages ) {
134
+
135
+ $data = $this->get_error_data( $code );
136
+ $data = is_string( $data ) ? $data : '<code>' . var_export( $data, true ) . '</code>';
137
+ $data = "\t\t<li class=\"ez-toc-debug-message-data\">{$data}</li>" . PHP_EOL;
138
+
139
+ array_push(
140
+ $dump,
141
+ PHP_EOL . "\t<ul class=\"ez-toc-debug-message-{$code}\">" . PHP_EOL . "\t\t<li class=\"ez-toc-debug-message\">" . implode( '</li>' . PHP_EOL . '<li>' . PHP_EOL, $messages ) . '</li>' . PHP_EOL . "{$data}\t</ul>" . PHP_EOL
142
+ );
143
+ }
144
+
145
+ return '<div class="ez-toc-debug-message">' . implode( '</div>' . PHP_EOL . '<div class="ez-toc-debug-message">', $dump ) . '</div>' . PHP_EOL;
146
+ }
147
+
148
+ /**
149
+ * @since 2.0.13
150
+ *
151
+ * @return string
152
+ */
153
+ public function __toString() {
154
+
155
+ if ( false === $this->enabled ) {
156
+
157
+ return '';
158
+ }
159
+
160
+ if ( false === $this->display ) {
161
+
162
+ return '';
163
+ }
164
+
165
+ if ( ! $this->has_errors() ) {
166
+
167
+ return '';
168
+ }
169
+
170
+ $intro = sprintf(
171
+ 'You see the following because <a href="%1$s"><code>WP_DEBUG</code></a> and <a href="%1$s"><code>WP_DEBUG_DISPLAY</code></a> are enabled on this site. Please disabled these to prevent the display of these developers\' debug messages.',
172
+ 'https://codex.wordpress.org/WP_DEBUG'
173
+ );
174
+
175
+ $intro = PHP_EOL . "<p>{$intro}</p>" .PHP_EOL;
176
+ $dump = $this->dump();
177
+
178
+ return PHP_EOL . "<div class='ez-toc-debug-messages'>{$intro}{$dump}</div>" . PHP_EOL;
179
+ }
180
+ }
includes/class.admin.php CHANGED
@@ -1,569 +1,569 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if ( ! defined( 'ABSPATH' ) ) exit;
5
-
6
- if ( ! class_exists( 'ezTOC_Admin' ) ) {
7
-
8
- /**
9
- * Class ezTOC_Admin
10
- */
11
- final class ezTOC_Admin {
12
-
13
- /**
14
- * Setup plugin for admin use.
15
- *
16
- * @access private
17
- * @since 1.0
18
- * @static
19
- */
20
- public function __construct() {
21
-
22
- $this->hooks();
23
- //$this->registerMetaboxes();
24
- }
25
-
26
- /**
27
- * Add the core admin hooks.
28
- *
29
- * @access private
30
- * @since 1.0
31
- * @static
32
- */
33
- private function hooks() {
34
-
35
- add_action( 'admin_init', array( $this, 'registerScripts' ) );
36
- add_action( 'admin_menu', array( $this, 'menu' ) );
37
- add_action( 'init', array( $this, 'registerMetaboxes' ), 99 );
38
- add_filter( 'plugin_action_links_' . EZ_TOC_BASE_NAME, array( $this, 'pluginActionLinks' ), 10, 2 );
39
- add_action( 'admin_enqueue_scripts', array( $this, 'load_scripts' ) );
40
- add_action('wp_ajax_eztoc_send_query_message', array( $this, 'eztoc_send_query_message'));
41
- }
42
-
43
- /**
44
- * Callback to add the Settings link to the plugin action links.
45
- *
46
- * @access private
47
- * @since 1.0
48
- * @static
49
- *
50
- * @param $links
51
- * @param $file
52
- *
53
- * @return array
54
- */
55
- public function pluginActionLinks( $links, $file ) {
56
-
57
- $action = array();
58
-
59
- $action[] = sprintf(
60
- '<a href="%1$s">%2$s</a>',
61
- esc_url( add_query_arg( 'page', 'table-of-contents', self_admin_url( 'options-general.php' ) ) ),
62
- esc_html__( 'Settings', 'easy-table-of-contents' )
63
- );
64
-
65
- return array_merge( $action, $links );
66
- }
67
-
68
- /**
69
- * Register the scripts used in the admin.
70
- *
71
- * @access private
72
- * @since 1.0
73
- * @static
74
- */
75
- public function registerScripts() {
76
-
77
- wp_register_script( 'cn_toc_admin_script', EZ_TOC_URL . 'assets/js/admin.js', array( 'jquery', 'wp-color-picker' ), ezTOC::VERSION, true );
78
- wp_register_style( 'cn_toc_admin_style', EZ_TOC_URL . 'assets/css/admin.css', array( 'wp-color-picker' ), ezTOC::VERSION );
79
- }
80
-
81
- /**
82
- * Callback to add plugin as a submenu page of the Options page.
83
- *
84
- * This also adds the action to enqueue the scripts to be loaded on plugin's admin pages only.
85
- *
86
- * @access private
87
- * @since 1.0
88
- * @static
89
- */
90
- public function menu() {
91
-
92
- $page = add_submenu_page(
93
- 'options-general.php',
94
- esc_html__( 'Table of Contents', 'easy-table-of-contents' ),
95
- esc_html__( 'Table of Contents', 'easy-table-of-contents' ),
96
- 'manage_options',
97
- 'table-of-contents',
98
- array( $this, 'page' )
99
- );
100
-
101
- add_action( 'admin_print_styles-' . $page, array( $this, 'enqueueScripts' ) );
102
- }
103
-
104
- /**
105
- * Enqueue the scripts.
106
- *
107
- * @access private
108
- * @since 1.0
109
- * @static
110
- */
111
- public function enqueueScripts() {
112
-
113
- wp_enqueue_script( 'cn_toc_admin_script' );
114
- wp_enqueue_style( 'cn_toc_admin_style' );
115
- }
116
-
117
- /**
118
- * Callback to add the action which will register the table of contents post metaboxes.
119
- *
120
- * Metaboxes will only be registered for the post types per user preferences.
121
- *
122
- * @access private
123
- * @since 1.0
124
- * @static
125
- */
126
- public function registerMetaboxes() {
127
-
128
- foreach ( get_post_types() as $type ) {
129
-
130
- if ( in_array( $type, ezTOC_Option::get( 'enabled_post_types', array() ) ) ) {
131
-
132
- add_action( "add_meta_boxes_$type", array( $this, 'metabox' ) );
133
- add_action( "save_post_$type", array( $this, 'save' ), 10, 3 );
134
- }
135
- }
136
- }
137
-
138
- /**
139
- * Callback to register the table of contents metaboxes.
140
- *
141
- * @access private
142
- * @since 1.0
143
- * @static
144
- */
145
- public function metabox() {
146
-
147
- add_meta_box( 'ez-toc', esc_html__( 'Table of Contents', 'ez-toc' ), array( $this, 'displayMetabox' ) );
148
- }
149
-
150
- /**
151
- * Callback to render the content of the table of contents metaboxes.
152
- *
153
- * @access private
154
- * @since 1.0
155
- * @static
156
- *
157
- * @param object $post The post object.
158
- * @param $atts
159
- */
160
- public function displayMetabox( $post, $atts ) {
161
-
162
- // Add an nonce field so we can check for it on save.
163
- wp_nonce_field( 'ez_toc_save', '_ez_toc_nonce' );
164
-
165
- $suppress = get_post_meta( $post->ID, '_ez-toc-disabled', true ) == 1 ? true : false;
166
- $insert = get_post_meta( $post->ID, '_ez-toc-insert', true ) == 1 ? true : false;
167
- $headings = get_post_meta( $post->ID, '_ez-toc-heading-levels', true );
168
- $exclude = get_post_meta( $post->ID, '_ez-toc-exclude', true );
169
- $altText = get_post_meta( $post->ID, '_ez-toc-alttext', true );
170
-
171
- if ( ! is_array( $headings ) ) {
172
-
173
- $headings = array();
174
- }
175
- ?>
176
-
177
- <table class="form-table">
178
-
179
- <tbody>
180
-
181
- <tr>
182
- <th scope="row"></th>
183
- <td>
184
-
185
- <?php if ( in_array( get_post_type( $post ), ezTOC_Option::get( 'auto_insert_post_types', array() ) ) ) :
186
-
187
- ezTOC_Option::checkbox(
188
- array(
189
- 'id' => 'disabled-toc',
190
- 'desc' => esc_html__( 'Disable the automatic insertion of the table of contents.', 'easy-table-of-contents' ),
191
- 'default' => $suppress,
192
- ),
193
- $suppress
194
- );
195
-
196
- elseif( in_array( get_post_type( $post ), ezTOC_Option::get( 'enabled_post_types', array() ) ) ):
197
-
198
- ezTOC_Option::checkbox(
199
- array(
200
- 'id' => 'insert-toc',
201
- 'desc' => esc_html__( 'Insert table of contents.', 'easy-table-of-contents' ),
202
- 'default' => $insert,
203
- ),
204
- $insert
205
- );
206
-
207
- endif; ?>
208
-
209
- </td>
210
- </tr>
211
-
212
- <tr>
213
- <th scope="row"><?php esc_html_e( 'Advanced:', 'easy-table-of-contents' ); ?></th>
214
- <td>
215
- <?php
216
- ezTOC_Option::descriptive_text(
217
- array(
218
- 'id' => 'exclude-desc',
219
- 'name' => '',
220
- 'desc' => '<p><strong>' . esc_html__( 'NOTE:', 'easy-table-of-contents' ) . '</strong></p>' .
221
- '<ul>' .
222
- '<li>' . esc_html__( 'Using the advanced options below will override the global advanced settings.', 'easy-table-of-contents' ) . '</li>' .
223
- '</ul>',
224
- )
225
- );
226
- ?>
227
- </td>
228
- </tr>
229
-
230
- <tr>
231
- <th scope="row"><?php esc_html_e( 'Headings:', 'easy-table-of-contents' ); ?></th>
232
- <td>
233
- <?php
234
- ezTOC_Option::checkboxgroup(
235
- array(
236
- 'id' => 'heading-levels',
237
- 'desc' => esc_html__( 'Select the heading to consider when generating the table of contents. Deselecting a heading will exclude it.', 'easy-table-of-contents' ),
238
- 'options' => array(
239
- '1' => __( 'Heading 1 (h1)', 'easy-table-of-contents' ),
240
- '2' => __( 'Heading 2 (h2)', 'easy-table-of-contents' ),
241
- '3' => __( 'Heading 3 (h3)', 'easy-table-of-contents' ),
242
- '4' => __( 'Heading 4 (h4)', 'easy-table-of-contents' ),
243
- '5' => __( 'Heading 5 (h5)', 'easy-table-of-contents' ),
244
- '6' => __( 'Heading 6 (h6)', 'easy-table-of-contents' ),
245
- ),
246
- 'default' => array(),
247
- ),
248
- array_map( 'absint', $headings )
249
- );
250
- ?>
251
- </td>
252
- </tr>
253
- <tr>
254
- <th scope="row"><?php esc_html_e( 'Alternate Headings', 'easy-table-of-contents' ); ?></th>
255
- <td>
256
- <?php
257
- ezTOC_Option::textarea(
258
- array(
259
- 'id' => 'alttext',
260
- 'desc' => __( 'Specify alternate table of contents header string. Add the header to be replaced and the alternate header on a single line separated with a pipe <code>|</code>. Put each additional original and alternate header on its own line.', 'easy-table-of-contents' ),
261
- 'size' => 'large',
262
- 'default' => '',
263
- ),
264
- $altText
265
- );
266
- ?>
267
- </td>
268
- </tr>
269
- <tr>
270
- <th scope="row"></th>
271
- <td>
272
- <?php
273
- ezTOC_Option::descriptive_text(
274
- array(
275
- 'id' => 'alttext-desc',
276
- 'name' => '',
277
- 'desc' => '<p><strong>' . esc_html__( 'Examples:', 'easy-table-of-contents' ) . '</strong></p>' .
278
- '<ul>' .
279
- '<li>' . __( '<code>Level [1.1]|Alternate TOC Header</code> Replaces Level [1.1] in the table of contents with Alternate TOC Header.', 'easy-table-of-contents' ) . '</li>' .
280
- '</ul>' .
281
- '<p>' . __( '<strong>Note:</strong> This is case sensitive.', 'easy-table-of-contents' ) . '</p>',
282
- )
283
- );
284
- ?>
285
- </td>
286
- </tr>
287
- <tr>
288
- <th scope="row"><?php esc_html_e( 'Exclude Headings', 'easy-table-of-contents' ); ?></th>
289
- <td>
290
- <?php
291
- ezTOC_Option::text(
292
- array(
293
- 'id' => 'exclude',
294
- 'desc' => __( 'Specify headings to be excluded from appearing in the table of contents. Separate multiple headings with a pipe <code>|</code>. Use an asterisk <code>*</code> as a wildcard to match other text.', 'easy-table-of-contents' ),
295
- 'size' => 'large',
296
- 'default' => '',
297
- ),
298
- $exclude
299
- );
300
- ?>
301
- </td>
302
- </tr>
303
- <tr>
304
- <th scope="row"></th>
305
- <td>
306
- <?php
307
- ezTOC_Option::descriptive_text(
308
- array(
309
- 'id' => 'exclude-desc',
310
- 'name' => '',
311
- 'desc' => '<p><strong>' . esc_html__( 'Examples:', 'easy-table-of-contents' ) . '</strong></p>' .
312
- '<ul>' .
313
- '<li>' . __( '<code>Fruit*</code> Ignore headings starting with "Fruit".', 'easy-table-of-contents' ) . '</li>' .
314
- '<li>' . __( '<code>*Fruit Diet*</code> Ignore headings with "Fruit Diet" somewhere in the heading.', 'easy-table-of-contents' ) . '</li>' .
315
- '<li>' . __( '<code>Apple Tree|Oranges|Yellow Bananas</code> Ignore headings that are exactly "Apple Tree", "Oranges" or "Yellow Bananas".', 'easy-table-of-contents' ) . '</li>' .
316
- '</ul>' .
317
- '<p>' . __( '<strong>Note:</strong> This is not case sensitive.', 'easy-table-of-contents' ) . '</p>',
318
- )
319
- );
320
- ?>
321
- </td>
322
- </tr>
323
- </tbody>
324
- </table>
325
-
326
- <?php
327
- }
328
-
329
- /**
330
- * Callback which saves the user preferences from the table of contents metaboxes.
331
- *
332
- * @access private
333
- * @since 1.0
334
- * @static
335
- *
336
- * @param int $post_id The post ID.
337
- * @param object $post The post object.
338
- * @param bool $update Whether this is an existing post being updated or not.
339
- */
340
- public function save( $post_id, $post, $update ) {
341
-
342
- if ( current_user_can( 'edit_post', $post_id ) &&
343
- isset( $_REQUEST['_ez_toc_nonce'] ) &&
344
- wp_verify_nonce( $_REQUEST['_ez_toc_nonce'], 'ez_toc_save' )
345
- ) {
346
-
347
- // Checkboxes are present if checked, absent if not.
348
- if ( isset( $_REQUEST['ez-toc-settings']['disabled-toc'] ) ) {
349
-
350
- update_post_meta( $post_id, '_ez-toc-disabled', true );
351
-
352
- } else {
353
-
354
- update_post_meta( $post_id, '_ez-toc-disabled', false );
355
-
356
- }
357
-
358
- if ( isset( $_REQUEST['ez-toc-settings']['insert-toc'] ) ) {
359
-
360
- update_post_meta( $post_id, '_ez-toc-insert', true );
361
-
362
- } else {
363
-
364
- update_post_meta( $post_id, '_ez-toc-insert', false );
365
- }
366
-
367
- if ( isset( $_REQUEST['ez-toc-settings']['heading-levels'] ) && ! empty( $_REQUEST['ez-toc-settings']['heading-levels'] ) ) {
368
-
369
- if ( is_array( $_REQUEST['ez-toc-settings']['heading-levels'] ) ) {
370
-
371
- $headings = array_map( 'absint', $_REQUEST['ez-toc-settings']['heading-levels'] );
372
-
373
- } else {
374
-
375
- $headings = array();
376
- }
377
-
378
- update_post_meta( $post_id, '_ez-toc-heading-levels', $headings );
379
-
380
- } else {
381
-
382
- update_post_meta( $post_id, '_ez-toc-heading-levels', array() );
383
- }
384
-
385
- if ( isset( $_REQUEST['ez-toc-settings']['alttext'] ) && ! empty( $_REQUEST['ez-toc-settings']['alttext'] ) ) {
386
-
387
- if ( is_string( $_REQUEST['ez-toc-settings']['alttext'] ) ) {
388
-
389
- $alttext = trim( $_REQUEST['ez-toc-settings']['alttext'] );
390
-
391
- } else {
392
-
393
- $alttext = '';
394
- }
395
-
396
- /*
397
- * This is basically `esc_html()` but does not encode quotes.
398
- * This is to allow angle brackets and such which `wp_kses_post` would strip as "evil" scripts.
399
- */
400
- $alttext = wp_check_invalid_utf8( $alttext );
401
- $alttext = _wp_specialchars( $alttext, ENT_NOQUOTES );
402
-
403
- update_post_meta( $post_id, '_ez-toc-alttext', wp_kses_post( $alttext ) );
404
-
405
- } else {
406
-
407
- update_post_meta( $post_id, '_ez-toc-alttext', '' );
408
- }
409
-
410
- if ( isset( $_REQUEST['ez-toc-settings']['exclude'] ) && ! empty( $_REQUEST['ez-toc-settings']['exclude'] ) ) {
411
-
412
- if ( is_string( $_REQUEST['ez-toc-settings']['exclude'] ) ) {
413
-
414
- $exclude = trim( $_REQUEST['ez-toc-settings']['exclude'] );
415
-
416
- } else {
417
-
418
- $exclude = '';
419
- }
420
-
421
- /*
422
- * This is basically `esc_html()` but does not encode quotes.
423
- * This is to allow angle brackets and such which `wp_kses_post` would strip as "evil" scripts.
424
- */
425
- $exclude = wp_check_invalid_utf8( $exclude );
426
- $exclude = _wp_specialchars( $exclude, ENT_NOQUOTES );
427
-
428
- update_post_meta( $post_id, '_ez-toc-exclude', wp_kses_post( $exclude ) );
429
-
430
- } else {
431
-
432
- update_post_meta( $post_id, '_ez-toc-exclude', '' );
433
- }
434
-
435
- }
436
-
437
- }
438
-
439
-
440
- /**
441
- * Enqueue Admin js scripts
442
- *
443
- */
444
- public function load_scripts($pagenow){
445
-
446
- if (isset($pagenow) && $pagenow != 'settings_page_table-of-contents' && strpos($pagenow, 'table-of-contents') == false) {
447
-
448
- return false;
449
- }
450
-
451
- wp_enqueue_script( 'eztoc-admin-js', EZ_TOC_URL . 'assets/js/eztoc-admin.js',array('jquery'), ezTOC::VERSION,true );
452
-
453
- $data = array(
454
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
455
- 'eztoc_security_nonce' => wp_create_nonce('eztoc_ajax_check_nonce'),
456
- );
457
-
458
- $data = apply_filters('eztoc_localize_filter',$data,'eztoc_admin_data');
459
-
460
- wp_localize_script( 'eztoc-admin-js', 'eztoc_admin_data', $data );
461
- }
462
-
463
- /**
464
- * This is a ajax handler function for sending email from user admin panel to us.
465
- * @return type json string
466
- */
467
-
468
- public function eztoc_send_query_message(){
469
-
470
- if ( ! isset( $_POST['eztoc_security_nonce'] ) ){
471
- return;
472
- }
473
- if ( !wp_verify_nonce( $_POST['eztoc_security_nonce'], 'eztoc_ajax_check_nonce' ) ){
474
- return;
475
- }
476
- $message = $this->eztoc_sanitize_textarea_field($_POST['message']);
477
- $email = $this->eztoc_sanitize_textarea_field($_POST['email']);
478
-
479
- if(function_exists('wp_get_current_user')){
480
-
481
- $user = wp_get_current_user();
482
-
483
-
484
- $message = '<p>'.$message.'</p><br><br>'.'Query from Easy Table of Content plugin support tab';
485
-
486
- $user_data = $user->data;
487
- $user_email = $user_data->user_email;
488
-
489
- if($email){
490
- $user_email = $email;
491
- }
492
- //php mailer variables
493
- $sendto = 'support@magazine3.in';
494
- $subject = "Easy Table of Content Query";
495
-
496
- $headers[] = 'Content-Type: text/html; charset=UTF-8';
497
- $headers[] = 'From: '. esc_attr($user_email);
498
- $headers[] = 'Reply-To: ' . esc_attr($user_email);
499
- // Load WP components, no themes.
500
-
501
- $sent = wp_mail($sendto, $subject, $message, $headers);
502
-
503
- if($sent){
504
-
505
- echo json_encode(array('status'=>'t'));
506
-
507
- }else{
508
-
509
- echo json_encode(array('status'=>'f'));
510
-
511
- }
512
-
513
- }
514
-
515
- wp_die();
516
- }
517
-
518
- public function eztoc_sanitize_textarea_field( $str ) {
519
-
520
- if ( is_object( $str ) || is_array( $str ) ) {
521
- return '';
522
- }
523
-
524
- $str = (string) $str;
525
-
526
- $filtered = wp_check_invalid_utf8( $str );
527
-
528
- if ( strpos( $filtered, '<' ) !== false ) {
529
- $filtered = wp_pre_kses_less_than( $filtered );
530
- // This will strip extra whitespace for us.
531
- $filtered = wp_strip_all_tags( $filtered, false );
532
-
533
- // Use HTML entities in a special case to make sure no later
534
- // newline stripping stage could lead to a functional tag.
535
- $filtered = str_replace( "<\n", "&lt;\n", $filtered );
536
- }
537
-
538
- $filtered = trim( $filtered );
539
-
540
- $found = false;
541
- while ( preg_match( '/%[a-f0-9]{2}/i', $filtered, $match ) ) {
542
- $filtered = str_replace( $match[0], '', $filtered );
543
- $found = true;
544
- }
545
-
546
- if ( $found ) {
547
- // Strip out the whitespace that may now exist after removing the octets.
548
- $filtered = trim( preg_replace( '/ +/', ' ', $filtered ) );
549
- }
550
-
551
- return $filtered;
552
- }
553
-
554
- /**
555
- * Callback used to render the admin options page.
556
- *
557
- * @access private
558
- * @since 1.0
559
- * @static
560
- */
561
- public function page() {
562
-
563
- include EZ_TOC_PATH . '/includes/inc.admin-options-page.php';
564
- }
565
- }
566
-
567
- new ezTOC_Admin();
568
-
569
- }
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ if ( ! class_exists( 'ezTOC_Admin' ) ) {
7
+
8
+ /**
9
+ * Class ezTOC_Admin
10
+ */
11
+ final class ezTOC_Admin {
12
+
13
+ /**
14
+ * Setup plugin for admin use.
15
+ *
16
+ * @access private
17
+ * @since 1.0
18
+ * @static
19
+ */
20
+ public function __construct() {
21
+
22
+ $this->hooks();
23
+ //$this->registerMetaboxes();
24
+ }
25
+
26
+ /**
27
+ * Add the core admin hooks.
28
+ *
29
+ * @access private
30
+ * @since 1.0
31
+ * @static
32
+ */
33
+ private function hooks() {
34
+
35
+ add_action( 'admin_init', array( $this, 'registerScripts' ) );
36
+ add_action( 'admin_menu', array( $this, 'menu' ) );
37
+ add_action( 'init', array( $this, 'registerMetaboxes' ), 99 );
38
+ add_filter( 'plugin_action_links_' . EZ_TOC_BASE_NAME, array( $this, 'pluginActionLinks' ), 10, 2 );
39
+ add_action( 'admin_enqueue_scripts', array( $this, 'load_scripts' ) );
40
+ add_action('wp_ajax_eztoc_send_query_message', array( $this, 'eztoc_send_query_message'));
41
+ }
42
+
43
+ /**
44
+ * Callback to add the Settings link to the plugin action links.
45
+ *
46
+ * @access private
47
+ * @since 1.0
48
+ * @static
49
+ *
50
+ * @param $links
51
+ * @param $file
52
+ *
53
+ * @return array
54
+ */
55
+ public function pluginActionLinks( $links, $file ) {
56
+
57
+ $action = array();
58
+
59
+ $action[] = sprintf(
60
+ '<a href="%1$s">%2$s</a>',
61
+ esc_url( add_query_arg( 'page', 'table-of-contents', self_admin_url( 'options-general.php' ) ) ),
62
+ esc_html__( 'Settings', 'easy-table-of-contents' )
63
+ );
64
+
65
+ return array_merge( $action, $links );
66
+ }
67
+
68
+ /**
69
+ * Register the scripts used in the admin.
70
+ *
71
+ * @access private
72
+ * @since 1.0
73
+ * @static
74
+ */
75
+ public function registerScripts() {
76
+
77
+ wp_register_script( 'cn_toc_admin_script', EZ_TOC_URL . 'assets/js/admin.js', array( 'jquery', 'wp-color-picker' ), ezTOC::VERSION, true );
78
+ wp_register_style( 'cn_toc_admin_style', EZ_TOC_URL . 'assets/css/admin.css', array( 'wp-color-picker' ), ezTOC::VERSION );
79
+ }
80
+
81
+ /**
82
+ * Callback to add plugin as a submenu page of the Options page.
83
+ *
84
+ * This also adds the action to enqueue the scripts to be loaded on plugin's admin pages only.
85
+ *
86
+ * @access private
87
+ * @since 1.0
88
+ * @static
89
+ */
90
+ public function menu() {
91
+
92
+ $page = add_submenu_page(
93
+ 'options-general.php',
94
+ esc_html__( 'Table of Contents', 'easy-table-of-contents' ),
95
+ esc_html__( 'Table of Contents', 'easy-table-of-contents' ),
96
+ 'manage_options',
97
+ 'table-of-contents',
98
+ array( $this, 'page' )
99
+ );
100
+
101
+ add_action( 'admin_print_styles-' . $page, array( $this, 'enqueueScripts' ) );
102
+ }
103
+
104
+ /**
105
+ * Enqueue the scripts.
106
+ *
107
+ * @access private
108
+ * @since 1.0
109
+ * @static
110
+ */
111
+ public function enqueueScripts() {
112
+
113
+ wp_enqueue_script( 'cn_toc_admin_script' );
114
+ wp_enqueue_style( 'cn_toc_admin_style' );
115
+ }
116
+
117
+ /**
118
+ * Callback to add the action which will register the table of contents post metaboxes.
119
+ *
120
+ * Metaboxes will only be registered for the post types per user preferences.
121
+ *
122
+ * @access private
123
+ * @since 1.0
124
+ * @static
125
+ */
126
+ public function registerMetaboxes() {
127
+
128
+ foreach ( get_post_types() as $type ) {
129
+
130
+ if ( in_array( $type, ezTOC_Option::get( 'enabled_post_types', array() ) ) ) {
131
+
132
+ add_action( "add_meta_boxes_$type", array( $this, 'metabox' ) );
133
+ add_action( "save_post_$type", array( $this, 'save' ), 10, 3 );
134
+ }
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Callback to register the table of contents metaboxes.
140
+ *
141
+ * @access private
142
+ * @since 1.0
143
+ * @static
144
+ */
145
+ public function metabox() {
146
+
147
+ add_meta_box( 'ez-toc', esc_html__( 'Table of Contents', 'ez-toc' ), array( $this, 'displayMetabox' ) );
148
+ }
149
+
150
+ /**
151
+ * Callback to render the content of the table of contents metaboxes.
152
+ *
153
+ * @access private
154
+ * @since 1.0
155
+ * @static
156
+ *
157
+ * @param object $post The post object.
158
+ * @param $atts
159
+ */
160
+ public function displayMetabox( $post, $atts ) {
161
+
162
+ // Add an nonce field so we can check for it on save.
163
+ wp_nonce_field( 'ez_toc_save', '_ez_toc_nonce' );
164
+
165
+ $suppress = get_post_meta( $post->ID, '_ez-toc-disabled', true ) == 1 ? true : false;
166
+ $insert = get_post_meta( $post->ID, '_ez-toc-insert', true ) == 1 ? true : false;
167
+ $headings = get_post_meta( $post->ID, '_ez-toc-heading-levels', true );
168
+ $exclude = get_post_meta( $post->ID, '_ez-toc-exclude', true );
169
+ $altText = get_post_meta( $post->ID, '_ez-toc-alttext', true );
170
+
171
+ if ( ! is_array( $headings ) ) {
172
+
173
+ $headings = array();
174
+ }
175
+ ?>
176
+
177
+ <table class="form-table">
178
+
179
+ <tbody>
180
+
181
+ <tr>
182
+ <th scope="row"></th>
183
+ <td>
184
+
185
+ <?php if ( in_array( get_post_type( $post ), ezTOC_Option::get( 'auto_insert_post_types', array() ) ) ) :
186
+
187
+ ezTOC_Option::checkbox(
188
+ array(
189
+ 'id' => 'disabled-toc',
190
+ 'desc' => esc_html__( 'Disable the automatic insertion of the table of contents.', 'easy-table-of-contents' ),
191
+ 'default' => $suppress,
192
+ ),
193
+ $suppress
194
+ );
195
+
196
+ elseif( in_array( get_post_type( $post ), ezTOC_Option::get( 'enabled_post_types', array() ) ) ):
197
+
198
+ ezTOC_Option::checkbox(
199
+ array(
200
+ 'id' => 'insert-toc',
201
+ 'desc' => esc_html__( 'Insert table of contents.', 'easy-table-of-contents' ),
202
+ 'default' => $insert,
203
+ ),
204
+ $insert
205
+ );
206
+
207
+ endif; ?>
208
+
209
+ </td>
210
+ </tr>
211
+
212
+ <tr>
213
+ <th scope="row"><?php esc_html_e( 'Advanced:', 'easy-table-of-contents' ); ?></th>
214
+ <td>
215
+ <?php
216
+ ezTOC_Option::descriptive_text(
217
+ array(
218
+ 'id' => 'exclude-desc',
219
+ 'name' => '',
220
+ 'desc' => '<p><strong>' . esc_html__( 'NOTE:', 'easy-table-of-contents' ) . '</strong></p>' .
221
+ '<ul>' .
222
+ '<li>' . esc_html__( 'Using the advanced options below will override the global advanced settings.', 'easy-table-of-contents' ) . '</li>' .
223
+ '</ul>',
224
+ )
225
+ );
226
+ ?>
227
+ </td>
228
+ </tr>
229
+
230
+ <tr>
231
+ <th scope="row"><?php esc_html_e( 'Headings:', 'easy-table-of-contents' ); ?></th>
232
+ <td>
233
+ <?php
234
+ ezTOC_Option::checkboxgroup(
235
+ array(
236
+ 'id' => 'heading-levels',
237
+ 'desc' => esc_html__( 'Select the heading to consider when generating the table of contents. Deselecting a heading will exclude it.', 'easy-table-of-contents' ),
238
+ 'options' => array(
239
+ '1' => __( 'Heading 1 (h1)', 'easy-table-of-contents' ),
240
+ '2' => __( 'Heading 2 (h2)', 'easy-table-of-contents' ),
241
+ '3' => __( 'Heading 3 (h3)', 'easy-table-of-contents' ),
242
+ '4' => __( 'Heading 4 (h4)', 'easy-table-of-contents' ),
243
+ '5' => __( 'Heading 5 (h5)', 'easy-table-of-contents' ),
244
+ '6' => __( 'Heading 6 (h6)', 'easy-table-of-contents' ),
245
+ ),
246
+ 'default' => array(),
247
+ ),
248
+ array_map( 'absint', $headings )
249
+ );
250
+ ?>
251
+ </td>
252
+ </tr>
253
+ <tr>
254
+ <th scope="row"><?php esc_html_e( 'Alternate Headings', 'easy-table-of-contents' ); ?></th>
255
+ <td>
256
+ <?php
257
+ ezTOC_Option::textarea(
258
+ array(
259
+ 'id' => 'alttext',
260
+ 'desc' => __( 'Specify alternate table of contents header string. Add the header to be replaced and the alternate header on a single line separated with a pipe <code>|</code>. Put each additional original and alternate header on its own line.', 'easy-table-of-contents' ),
261
+ 'size' => 'large',
262
+ 'default' => '',
263
+ ),
264
+ $altText
265
+ );
266
+ ?>
267
+ </td>
268
+ </tr>
269
+ <tr>
270
+ <th scope="row"></th>
271
+ <td>
272
+ <?php
273
+ ezTOC_Option::descriptive_text(
274
+ array(
275
+ 'id' => 'alttext-desc',
276
+ 'name' => '',
277
+ 'desc' => '<p><strong>' . esc_html__( 'Examples:', 'easy-table-of-contents' ) . '</strong></p>' .
278
+ '<ul>' .
279
+ '<li>' . __( '<code>Level [1.1]|Alternate TOC Header</code> Replaces Level [1.1] in the table of contents with Alternate TOC Header.', 'easy-table-of-contents' ) . '</li>' .
280
+ '</ul>' .
281
+ '<p>' . __( '<strong>Note:</strong> This is case sensitive.', 'easy-table-of-contents' ) . '</p>',
282
+ )
283
+ );
284
+ ?>
285
+ </td>
286
+ </tr>
287
+ <tr>
288
+ <th scope="row"><?php esc_html_e( 'Exclude Headings', 'easy-table-of-contents' ); ?></th>
289
+ <td>
290
+ <?php
291
+ ezTOC_Option::text(
292
+ array(
293
+ 'id' => 'exclude',
294
+ 'desc' => __( 'Specify headings to be excluded from appearing in the table of contents. Separate multiple headings with a pipe <code>|</code>. Use an asterisk <code>*</code> as a wildcard to match other text.', 'easy-table-of-contents' ),
295
+ 'size' => 'large',
296
+ 'default' => '',
297
+ ),
298
+ $exclude
299
+ );
300
+ ?>
301
+ </td>
302
+ </tr>
303
+ <tr>
304
+ <th scope="row"></th>
305
+ <td>
306
+ <?php
307
+ ezTOC_Option::descriptive_text(
308
+ array(
309
+ 'id' => 'exclude-desc',
310
+ 'name' => '',
311
+ 'desc' => '<p><strong>' . esc_html__( 'Examples:', 'easy-table-of-contents' ) . '</strong></p>' .
312
+ '<ul>' .
313
+ '<li>' . __( '<code>Fruit*</code> Ignore headings starting with "Fruit".', 'easy-table-of-contents' ) . '</li>' .
314
+ '<li>' . __( '<code>*Fruit Diet*</code> Ignore headings with "Fruit Diet" somewhere in the heading.', 'easy-table-of-contents' ) . '</li>' .
315
+ '<li>' . __( '<code>Apple Tree|Oranges|Yellow Bananas</code> Ignore headings that are exactly "Apple Tree", "Oranges" or "Yellow Bananas".', 'easy-table-of-contents' ) . '</li>' .
316
+ '</ul>' .
317
+ '<p>' . __( '<strong>Note:</strong> This is not case sensitive.', 'easy-table-of-contents' ) . '</p>',
318
+ )
319
+ );
320
+ ?>
321
+ </td>
322
+ </tr>
323
+ </tbody>
324
+ </table>
325
+
326
+ <?php
327
+ }
328
+
329
+ /**
330
+ * Callback which saves the user preferences from the table of contents metaboxes.
331
+ *
332
+ * @access private
333
+ * @since 1.0
334
+ * @static
335
+ *
336
+ * @param int $post_id The post ID.
337
+ * @param object $post The post object.
338
+ * @param bool $update Whether this is an existing post being updated or not.
339
+ */
340
+ public function save( $post_id, $post, $update ) {
341
+
342
+ if ( current_user_can( 'edit_post', $post_id ) &&
343
+ isset( $_REQUEST['_ez_toc_nonce'] ) &&
344
+ wp_verify_nonce( $_REQUEST['_ez_toc_nonce'], 'ez_toc_save' )
345
+ ) {
346
+
347
+ // Checkboxes are present if checked, absent if not.
348
+ if ( isset( $_REQUEST['ez-toc-settings']['disabled-toc'] ) ) {
349
+
350
+ update_post_meta( $post_id, '_ez-toc-disabled', true );
351
+
352
+ } else {
353
+
354
+ update_post_meta( $post_id, '_ez-toc-disabled', false );
355
+
356
+ }
357
+
358
+ if ( isset( $_REQUEST['ez-toc-settings']['insert-toc'] ) ) {
359
+
360
+ update_post_meta( $post_id, '_ez-toc-insert', true );
361
+
362
+ } else {
363
+
364
+ update_post_meta( $post_id, '_ez-toc-insert', false );
365
+ }
366
+
367
+ if ( isset( $_REQUEST['ez-toc-settings']['heading-levels'] ) && ! empty( $_REQUEST['ez-toc-settings']['heading-levels'] ) ) {
368
+
369
+ if ( is_array( $_REQUEST['ez-toc-settings']['heading-levels'] ) ) {
370
+
371
+ $headings = array_map( 'absint', $_REQUEST['ez-toc-settings']['heading-levels'] );
372
+
373
+ } else {
374
+
375
+ $headings = array();
376
+ }
377
+
378
+ update_post_meta( $post_id, '_ez-toc-heading-levels', $headings );
379
+
380
+ } else {
381
+
382
+ update_post_meta( $post_id, '_ez-toc-heading-levels', array() );
383
+ }
384
+
385
+ if ( isset( $_REQUEST['ez-toc-settings']['alttext'] ) && ! empty( $_REQUEST['ez-toc-settings']['alttext'] ) ) {
386
+
387
+ if ( is_string( $_REQUEST['ez-toc-settings']['alttext'] ) ) {
388
+
389
+ $alttext = trim( $_REQUEST['ez-toc-settings']['alttext'] );
390
+
391
+ } else {
392
+
393
+ $alttext = '';
394
+ }
395
+
396
+ /*
397
+ * This is basically `esc_html()` but does not encode quotes.
398
+ * This is to allow angle brackets and such which `wp_kses_post` would strip as "evil" scripts.
399
+ */
400
+ $alttext = wp_check_invalid_utf8( $alttext );
401
+ $alttext = _wp_specialchars( $alttext, ENT_NOQUOTES );
402
+
403
+ update_post_meta( $post_id, '_ez-toc-alttext', wp_kses_post( $alttext ) );
404
+
405
+ } else {
406
+
407
+ update_post_meta( $post_id, '_ez-toc-alttext', '' );
408
+ }
409
+
410
+ if ( isset( $_REQUEST['ez-toc-settings']['exclude'] ) && ! empty( $_REQUEST['ez-toc-settings']['exclude'] ) ) {
411
+
412
+ if ( is_string( $_REQUEST['ez-toc-settings']['exclude'] ) ) {
413
+
414
+ $exclude = trim( $_REQUEST['ez-toc-settings']['exclude'] );
415
+
416
+ } else {
417
+
418
+ $exclude = '';
419
+ }
420
+
421
+ /*
422
+ * This is basically `esc_html()` but does not encode quotes.
423
+ * This is to allow angle brackets and such which `wp_kses_post` would strip as "evil" scripts.
424
+ */
425
+ $exclude = wp_check_invalid_utf8( $exclude );
426
+ $exclude = _wp_specialchars( $exclude, ENT_NOQUOTES );
427
+
428
+ update_post_meta( $post_id, '_ez-toc-exclude', wp_kses_post( $exclude ) );
429
+
430
+ } else {
431
+
432
+ update_post_meta( $post_id, '_ez-toc-exclude', '' );
433
+ }
434
+
435
+ }
436
+
437
+ }
438
+
439
+
440
+ /**
441
+ * Enqueue Admin js scripts
442
+ *
443
+ */
444
+ public function load_scripts($pagenow){
445
+
446
+ if (isset($pagenow) && $pagenow != 'settings_page_table-of-contents' && strpos($pagenow, 'table-of-contents') == false) {
447
+
448
+ return false;
449
+ }
450
+
451
+ wp_enqueue_script( 'eztoc-admin-js', EZ_TOC_URL . 'assets/js/eztoc-admin.js',array('jquery'), ezTOC::VERSION,true );
452
+
453
+ $data = array(
454
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
455
+ 'eztoc_security_nonce' => wp_create_nonce('eztoc_ajax_check_nonce'),
456
+ );
457
+
458
+ $data = apply_filters('eztoc_localize_filter',$data,'eztoc_admin_data');
459
+
460
+ wp_localize_script( 'eztoc-admin-js', 'eztoc_admin_data', $data );
461
+ }
462
+
463
+ /**
464
+ * This is a ajax handler function for sending email from user admin panel to us.
465
+ * @return type json string
466
+ */
467
+
468
+ public function eztoc_send_query_message(){
469
+
470
+ if ( ! isset( $_POST['eztoc_security_nonce'] ) ){
471
+ return;
472
+ }
473
+ if ( !wp_verify_nonce( $_POST['eztoc_security_nonce'], 'eztoc_ajax_check_nonce' ) ){
474
+ return;
475
+ }
476
+ $message = $this->eztoc_sanitize_textarea_field($_POST['message']);
477
+ $email = $this->eztoc_sanitize_textarea_field($_POST['email']);
478
+
479
+ if(function_exists('wp_get_current_user')){
480
+
481
+ $user = wp_get_current_user();
482
+
483
+
484
+ $message = '<p>'.$message.'</p><br><br>'.'Query from Easy Table of Content plugin support tab';
485
+
486
+ $user_data = $user->data;
487
+ $user_email = $user_data->user_email;
488
+
489
+ if($email){
490
+ $user_email = $email;
491
+ }
492
+ //php mailer variables
493
+ $sendto = 'team@magazine3.in';
494
+ $subject = "Easy Table of Content Query";
495
+
496
+ $headers[] = 'Content-Type: text/html; charset=UTF-8';
497
+ $headers[] = 'From: '. esc_attr($user_email);
498
+ $headers[] = 'Reply-To: ' . esc_attr($user_email);
499
+ // Load WP components, no themes.
500
+
501
+ $sent = wp_mail($sendto, $subject, $message, $headers);
502
+
503
+ if($sent){
504
+
505
+ echo json_encode(array('status'=>'t'));
506
+
507
+ }else{
508
+
509
+ echo json_encode(array('status'=>'f'));
510
+
511
+ }
512
+
513
+ }
514
+
515
+ wp_die();
516
+ }
517
+
518
+ public function eztoc_sanitize_textarea_field( $str ) {
519
+
520
+ if ( is_object( $str ) || is_array( $str ) ) {
521
+ return '';
522
+ }
523
+
524
+ $str = (string) $str;
525
+
526
+ $filtered = wp_check_invalid_utf8( $str );
527
+
528
+ if ( strpos( $filtered, '<' ) !== false ) {
529
+ $filtered = wp_pre_kses_less_than( $filtered );
530
+ // This will strip extra whitespace for us.
531
+ $filtered = wp_strip_all_tags( $filtered, false );
532
+
533
+ // Use HTML entities in a special case to make sure no later
534
+ // newline stripping stage could lead to a functional tag.
535
+ $filtered = str_replace( "<\n", "&lt;\n", $filtered );
536
+ }
537
+
538
+ $filtered = trim( $filtered );
539
+
540
+ $found = false;
541
+ while ( preg_match( '/%[a-f0-9]{2}/i', $filtered, $match ) ) {
542
+ $filtered = str_replace( $match[0], '', $filtered );
543
+ $found = true;
544
+ }
545
+
546
+ if ( $found ) {
547
+ // Strip out the whitespace that may now exist after removing the octets.
548
+ $filtered = trim( preg_replace( '/ +/', ' ', $filtered ) );
549
+ }
550
+
551
+ return $filtered;
552
+ }
553
+
554
+ /**
555
+ * Callback used to render the admin options page.
556
+ *
557
+ * @access private
558
+ * @since 1.0
559
+ * @static
560
+ */
561
+ public function page() {
562
+
563
+ include EZ_TOC_PATH . '/includes/inc.admin-options-page.php';
564
+ }
565
+ }
566
+
567
+ new ezTOC_Admin();
568
+
569
+ }
includes/class.options.php CHANGED
@@ -1,1281 +1,1294 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if ( ! defined( 'ABSPATH' ) ) exit;
5
-
6
- if ( ! class_exists( 'ezTOC_Option' ) ) {
7
-
8
- /**
9
- * Class ezTOC_Option
10
- *
11
- * Credit: Adapted from Easy Digital Downloads.
12
- */
13
- final class ezTOC_Option {
14
-
15
- /**
16
- * Register the plugins core settings and options.
17
- *
18
- * @access private
19
- * @since 1.0
20
- * @static
21
- */
22
- public static function register() {
23
-
24
- if ( false === get_option( 'ez-toc-settings' ) ) {
25
-
26
- add_option( 'ez-toc-settings', self::getDefaults() );
27
- }
28
-
29
- foreach ( self::getRegistered() as $section => $settings ) {
30
-
31
- add_settings_section(
32
- 'ez_toc_settings_' . $section,
33
- __return_null(),
34
- '__return_false',
35
- 'ez_toc_settings_' . $section
36
- );
37
-
38
- foreach ( $settings as $option ) {
39
-
40
- $name = isset( $option['name'] ) ? $option['name'] : '';
41
-
42
- add_settings_field(
43
- 'ez-toc-settings[' . $option['id'] . ']',
44
- $name,
45
- method_exists( __CLASS__, $option['type'] ) ? array( __CLASS__, $option['type'] ) : array( __CLASS__, 'missingCallback' ),
46
- 'ez_toc_settings_' . $section,
47
- 'ez_toc_settings_' . $section,
48
- array(
49
- 'section' => $section,
50
- 'id' => isset( $option['id'] ) ? $option['id'] : null,
51
- 'desc' => ! empty( $option['desc'] ) ? $option['desc'] : '',
52
- 'name' => isset( $option['name'] ) ? $option['name'] : null,
53
- 'size' => isset( $option['size'] ) ? $option['size'] : null,
54
- 'options' => isset( $option['options'] ) ? $option['options'] : '',
55
- 'default' => isset( $option['default'] ) ? $option['default'] : '',
56
- 'min' => isset( $option['min'] ) ? $option['min'] : null,
57
- 'max' => isset( $option['max'] ) ? $option['max'] : null,
58
- 'step' => isset( $option['step'] ) ? $option['step'] : null,
59
- 'chosen' => isset( $option['chosen'] ) ? $option['chosen'] : null,
60
- 'placeholder' => isset( $option['placeholder'] ) ? $option['placeholder'] : null,
61
- 'allow_blank' => isset( $option['allow_blank'] ) ? $option['allow_blank'] : true,
62
- 'readonly' => isset( $option['readonly'] ) ? $option['readonly'] : false,
63
- 'faux' => isset( $option['faux'] ) ? $option['faux'] : false,
64
- )
65
- );
66
- }
67
-
68
- }
69
-
70
- // Creates our settings in the options table
71
- register_setting( 'ez-toc-settings', 'ez-toc-settings', array( __CLASS__, 'sanitize' ) );
72
- }
73
-
74
- /**
75
- * Callback for settings sanitization.
76
- *
77
- * @access private
78
- * @since 1.0
79
- * @static
80
- *
81
- * @param array $input The value inputted in the field.
82
- *
83
- * @return string $input Sanitized value.
84
- */
85
- public static function sanitize( $input = array() ) {
86
-
87
- $options = self::getOptions();
88
-
89
- if ( empty( $_POST['_wp_http_referer'] ) ) {
90
-
91
- return $input;
92
- }
93
-
94
- $registered = self::getRegistered();
95
-
96
- foreach ( $registered as $sectionID => $sectionOptions ) {
97
-
98
- $input = $input ? $input : array();
99
- $input = apply_filters( 'ez_toc_settings_' . $sectionID . '_sanitize', $input );
100
-
101
- // Loop through each setting being saved and pass it through a sanitization filter
102
- foreach ( $input as $key => $value ) {
103
-
104
- // Get the setting type (checkbox, select, etc)
105
- $type = isset( $registered[ $sectionID ][ $key ]['type'] ) ? $registered[ $sectionID ][ $key ]['type'] : false;
106
-
107
- if ( $type ) {
108
-
109
- // Field type specific filter
110
- $input[ $key ] = apply_filters( 'ez_toc_settings_sanitize_' . $type, $value, $key );
111
- }
112
-
113
- // General filter
114
- $input[ $key ] = apply_filters( 'ez_toc_settings_sanitize', $input[ $key ], $key );
115
- }
116
-
117
- // Loop through the registered options.
118
- foreach ( $sectionOptions as $optionID => $optionProperties ) {
119
-
120
- // Unset any that are empty for the section being saved.
121
- if ( empty( $input[ $optionID ] ) ) {
122
-
123
- unset( $options[ $optionID ] );
124
- }
125
-
126
- // Check for the checkbox option type.
127
- if ( array_key_exists( 'type', $optionProperties ) && 'checkbox' == $optionProperties['type'] ) {
128
-
129
- // If it does not exist in the options values being saved, add the option ID and set its value to `0`.
130
- // This matches WP core behavior for saving checkbox option values.
131
- if ( ! array_key_exists( $optionID, $input ) ) {
132
-
133
- $input[ $optionID ] = '0';
134
- }
135
- }
136
- }
137
-
138
- }
139
-
140
- // Merge our new settings with the existing
141
- $output = array_merge( $options, $input );
142
-
143
- return $output;
144
- }
145
-
146
- /**
147
- * The core registered settings and options.
148
- *
149
- * @access private
150
- * @since 1.0
151
- * @static
152
- *
153
- * @return array
154
- */
155
- private static function getRegistered() {
156
-
157
- $options = array(
158
- 'general' => apply_filters(
159
- 'ez_toc_settings_general',
160
- array(
161
- 'enabled_post_types' => array(
162
- 'id' => 'enabled_post_types',
163
- 'name' => __( 'Enable Support', 'easy-table-of-contents' ),
164
- 'desc' => __( 'Select the post types to enable the support for table of contents.', 'easy-table-of-contents' ),
165
- 'type' => 'checkboxgroup',
166
- 'options' => self::getPostTypes(),
167
- 'default' => array(),
168
- ),
169
- 'auto_insert_post_types' => array(
170
- 'id' => 'auto_insert_post_types',
171
- 'name' => __( 'Auto Insert', 'easy-table-of-contents' ),
172
- 'desc' => __( 'Select the post types which will have the table of contents automatically inserted.', 'easy-table-of-contents' ) .
173
- '<br><span class="description">' . __( 'NOTE: The table of contents will only be automatically inserted on post types for which it has been enabled.', 'easy-table-of-contents' ) . '<span>',
174
- 'type' => 'checkboxgroup',
175
- 'options' => self::getPostTypes(),
176
- 'default' => array(),
177
- ),
178
- 'position' => array(
179
- 'id' => 'position',
180
- 'name' => __( 'Position', 'easy-table-of-contents' ),
181
- 'desc' => __( 'Choose where where you want to display the table of contents.', 'easy-table-of-contents' ),
182
- 'type' => 'select',
183
- 'options' => array(
184
- 'before' => __( 'Before first heading (default)', 'easy-table-of-contents' ),
185
- 'after' => __( 'After first heading', 'easy-table-of-contents' ),
186
- 'top' => __( 'Top', 'easy-table-of-contents' ),
187
- 'bottom' => __( 'Bottom', 'easy-table-of-contents' ),
188
- //'placeholder' => __( 'Replace [toc] placeholder. For backwards compatibility with Table of Content Plus.', 'easy-table-of-contents' ),
189
- ),
190
- 'default' => 1,
191
- ),
192
- 'start' => array(
193
- 'id' => 'start',
194
- 'name' => __( 'Show when', 'easy-table-of-contents' ),
195
- 'desc' => __( 'or more headings are present', 'easy-table-of-contents' ),
196
- 'type' => 'select',
197
- 'options' => array_combine( range( 1, 10 ), range( 1, 10 ) ),
198
- 'default' => 4,
199
- ),
200
- 'show_heading_text' => array(
201
- 'id' => 'show_heading_text',
202
- 'name' => __( 'Display Header Label', 'easy-table-of-contents' ),
203
- 'desc' => __( 'Show header text above the table of contents.', 'easy-table-of-contents' ),
204
- 'type' => 'checkbox',
205
- 'default' => true,
206
- ),
207
- 'heading_text' => array(
208
- 'id' => 'heading_text',
209
- 'name' => __( 'Header Label', 'easy-table-of-contents' ),
210
- 'desc' => __( 'Eg: Contents, Table of Contents, Page Contents', 'easy-table-of-contents' ),
211
- 'type' => 'text',
212
- 'default' => __( 'Contents', 'easy-table-of-contents' ),
213
- ),
214
- 'visibility' => array(
215
- 'id' => 'visibility',
216
- 'name' => __( 'Toggle View', 'easy-table-of-contents' ),
217
- 'desc' => __( 'Allow the user to toggle the visibility of the table of contents.', 'easy-table-of-contents' ),
218
- 'type' => 'checkbox',
219
- 'default' => true,
220
- ),
221
- //'visibility_show' => array(
222
- // 'id' => 'visibility_show',
223
- // 'name' => __( 'Show Label', 'easy-table-of-contents' ),
224
- // 'desc' => __( 'Eg: show', 'easy-table-of-contents' ),
225
- // 'type' => 'text',
226
- // 'default' => __( 'show', 'easy-table-of-contents' ),
227
- //),
228
- //'visibility_hide' => array(
229
- // 'id' => 'visibility_hide',
230
- // 'name' => __( 'Hide Label', 'easy-table-of-contents' ),
231
- // 'desc' => __( 'Eg: hide', 'easy-table-of-contents' ),
232
- // 'type' => 'text',
233
- // 'default' => __( 'hide', 'easy-table-of-contents' ),
234
- //),
235
- 'visibility_hide_by_default' => array(
236
- 'id' => 'visibility_hide_by_default',
237
- 'name' => __( 'Initial View', 'easy-table-of-contents' ),
238
- 'desc' => __( 'Initially hide the table of contents.', 'easy-table-of-contents' ),
239
- 'type' => 'checkbox',
240
- 'default' => false,
241
- ),
242
- 'show_hierarchy' => array(
243
- 'id' => 'show_hierarchy',
244
- 'name' => __( 'Show as Hierarchy', 'easy-table-of-contents' ),
245
- 'desc' => '',
246
- 'type' => 'checkbox',
247
- 'default' => true,
248
- ),
249
- 'counter' => array(
250
- 'id' => 'counter',
251
- 'name' => __( 'Counter', 'easy-table-of-contents' ),
252
- 'desc' => '',
253
- 'type' => 'select',
254
- 'options' => array(
255
- 'decimal' => __( 'Decimal (default)', 'easy-table-of-contents' ),
256
- 'numeric' => __( 'Numeric', 'easy-table-of-contents' ),
257
- 'roman' => __( 'Roman', 'easy-table-of-contents' ),
258
- 'none' => __( 'None', 'easy-table-of-contents' ),
259
- ),
260
- 'default' => 'decimal',
261
- ),
262
- 'smooth_scroll' => array(
263
- 'id' => 'smooth_scroll',
264
- 'name' => __( 'Smooth Scroll', 'easy-table-of-contents' ),
265
- 'desc' => '',
266
- 'type' => 'checkbox',
267
- 'default' => true,
268
- ),
269
- )
270
- ),
271
- 'appearance' => apply_filters(
272
- 'ez_toc_settings_appearance',
273
- array(
274
- 'width' => array(
275
- 'id' => 'width',
276
- 'name' => __( 'Width', 'easy-table-of-contents' ),
277
- 'desc' => '',
278
- 'type' => 'selectgroup',
279
- 'options' => array(
280
- 'fixed' => array(
281
- 'name' => __( 'Fixed', 'easy-table-of-contents' ),
282
- 'options' => array(
283
- '200px' => '200px',
284
- '225px' => '225px',
285
- '250px' => '250px',
286
- '275px' => '275px',
287
- '300px' => '300px',
288
- '325px' => '325px',
289
- '350px' => '350px',
290
- '375px' => '375px',
291
- '400px' => '400px',
292
- ),
293
- ),
294
- 'relative' => array(
295
- 'name' => __( 'Relative', 'easy-table-of-contents' ),
296
- 'options' => array(
297
- 'auto' => 'Auto',
298
- '25%' => '25%',
299
- '33%' => '33%',
300
- '50%' => '50%',
301
- '66%' => '66%',
302
- '75%' => '75%',
303
- '100%' => '100%',
304
- ),
305
- ),
306
- 'other' => array(
307
- 'name' => __( 'Custom', 'easy-table-of-contents' ),
308
- 'options' => array(
309
- 'custom' => __( 'User Defined', 'easy-table-of-contents' ),
310
- ),
311
- ),
312
- ),
313
- 'default' => 'auto',
314
- ),
315
- 'width_custom' => array(
316
- 'id' => 'width_custom',
317
- 'name' => __( 'Custom Width', 'easy-table-of-contents' ),
318
- 'desc' => __( 'Select the User Defined option from the Width option to utilitze the custom width.', 'easy-table-of-contents' ),
319
- 'type' => 'custom_width',
320
- 'default' => 275,
321
- ),
322
- 'wrapping' => array(
323
- 'id' => 'wrapping',
324
- 'name' => __( 'Float', 'easy-table-of-contents' ),
325
- 'desc' => '',
326
- 'type' => 'select',
327
- 'options' => array(
328
- 'none' => __( 'None (Default)', 'easy-table-of-contents' ),
329
- 'left' => __( 'Left', 'easy-table-of-contents' ),
330
- 'right' => __( 'Right', 'easy-table-of-contents' ),
331
- ),
332
- 'default' => 'none',
333
- ),
334
- 'title_font_size' => array(
335
- 'id' => 'title_font_size',
336
- 'name' => __( 'Title Font Size', 'easy-table-of-contents' ),
337
- 'desc' => '',
338
- 'type' => 'font_size',
339
- 'default' => 120,
340
- ),
341
- 'title_font_weight' => array(
342
- 'id' => 'title_font_weight',
343
- 'name' => __( 'Title Font Weight', 'easy-table-of-contents' ),
344
- 'desc' => '',
345
- 'type' => 'select',
346
- 'options' => array(
347
- '100' => __( 'Thin', 'easy-table-of-contents' ),
348
- '200' => __( 'Extra Light', 'easy-table-of-contents' ),
349
- '300' => __( 'Light', 'easy-table-of-contents' ),
350
- '400' => __( 'Normal', 'easy-table-of-contents' ),
351
- '500' => __( 'Medium', 'easy-table-of-contents' ),
352
- '600' => __( 'Semi Bold', 'easy-table-of-contents' ),
353
- '700' => __( 'Bold', 'easy-table-of-contents' ),
354
- '800' => __( 'Extra Bold', 'easy-table-of-contents' ),
355
- '900' => __( 'Heavy', 'easy-table-of-contents' ),
356
- ),
357
- 'default' => '500',
358
- ),
359
- 'font_size' => array(
360
- 'id' => 'font_size',
361
- 'name' => __( 'Font Size', 'easy-table-of-contents' ),
362
- 'desc' => '',
363
- 'type' => 'font_size',
364
- 'default' => 95,
365
- ),
366
- 'theme' => array(
367
- 'id' => 'theme',
368
- 'name' => __( 'Theme', 'easy-table-of-contents' ),
369
- 'desc' => __( 'The theme is only applied to the table of contents which is auto inserted into the post. The Table of Contents widget will inherit the theme widget styles.', 'easy-table-of-contents' ),
370
- 'type' => 'radio',
371
- 'options' => array(
372
- 'grey' => __( 'Grey', 'easy-table-of-contents' ),
373
- 'light-blue' => __( 'Light Blue', 'easy-table-of-contents' ),
374
- 'white' => __( 'White', 'easy-table-of-contents' ),
375
- 'black' => __( 'Black', 'easy-table-of-contents' ),
376
- 'transparent' => __( 'Transparent', 'easy-table-of-contents' ),
377
- 'custom' => __( 'Custom', 'easy-table-of-contents' ),
378
- ),
379
- 'default' => 'grey',
380
- ),
381
- 'custom_theme_header' => array(
382
- 'id' => 'custom_theme_header',
383
- 'name' => '<strong>' . __( 'Custom Theme', 'easy-table-of-contents' ) . '</strong>',
384
- 'desc' => __( 'For the following settings to apply, select the Custom Theme option.', 'easy-table-of-contents' ),
385
- 'type' => 'header',
386
- ),
387
- 'custom_background_colour' => array(
388
- 'id' => 'custom_background_colour',
389
- 'name' => __( 'Background Color', 'easy-table-of-contents' ),
390
- 'desc' => '',
391
- 'type' => 'color',
392
- 'default' => '#fff',
393
- ),
394
- 'custom_border_colour' => array(
395
- 'id' => 'custom_border_colour',
396
- 'name' => __( 'Border Color', 'easy-table-of-contents' ),
397
- 'desc' => '',
398
- 'type' => 'color',
399
- 'default' => '#ddd',
400
- ),
401
- 'custom_title_colour' => array(
402
- 'id' => 'custom_title_colour',
403
- 'name' => __( 'Title Color', 'easy-table-of-contents' ),
404
- 'desc' => '',
405
- 'type' => 'color',
406
- 'default' => '#999',
407
- ),
408
- 'custom_link_colour' => array(
409
- 'id' => 'custom_link_colour',
410
- 'name' => __( 'Link Color', 'easy-table-of-contents' ),
411
- 'desc' => '',
412
- 'type' => 'color',
413
- 'default' => '#428bca',
414
- ),
415
- 'custom_link_hover_colour' => array(
416
- 'id' => 'custom_link_hover_colour',
417
- 'name' => __( 'Link Hover Color', 'easy-table-of-contents' ),
418
- 'desc' => '',
419
- 'type' => 'color',
420
- 'default' => '#2a6496',
421
- ),
422
- 'custom_link_visited_colour' => array(
423
- 'id' => 'custom_link_visited_colour',
424
- 'name' => __( 'Link Visited Color', 'easy-table-of-contents' ),
425
- 'desc' => '',
426
- 'type' => 'color',
427
- 'default' => '#428bca',
428
- ),
429
- )
430
- ),
431
- 'advanced' => apply_filters(
432
- 'ez_toc_settings_advanced',
433
- array(
434
- 'lowercase' => array(
435
- 'id' => 'lowercase',
436
- 'name' => __( 'Lowercase', 'easy-table-of-contents' ),
437
- 'desc' => __( 'Ensure anchors are in lowercase.', 'easy-table-of-contents' ),
438
- 'type' => 'checkbox',
439
- 'default' => false,
440
- ),
441
- 'hyphenate' => array(
442
- 'id' => 'hyphenate',
443
- 'name' => __( 'Hyphenate', 'easy-table-of-contents' ),
444
- 'desc' => __( 'Use - rather than _ in anchors.', 'easy-table-of-contents' ),
445
- 'type' => 'checkbox',
446
- 'default' => false,
447
- ),
448
- 'include_homepage' => array(
449
- 'id' => 'include_homepage',
450
- 'name' => __( 'Homepage', 'easy-table-of-contents' ),
451
- 'desc' => __( 'Show the table of contents for qualifying items on the homepage.', 'easy-table-of-contents' ),
452
- 'type' => 'checkbox',
453
- 'default' => false,
454
- ),
455
- 'exclude_css' => array(
456
- 'id' => 'exclude_css',
457
- 'name' => __( 'CSS', 'easy-table-of-contents' ),
458
- 'desc' => __( "Prevent the loading the core CSS styles. When selected, the appearance options from above will be ignored.", 'easy-table-of-contents' ),
459
- 'type' => 'checkbox',
460
- 'default' => false,
461
- ),
462
- //'bullet_spacing' => array(
463
- // 'id' => 'bullet_spacing',
464
- // 'name' => __( 'Theme Bullets', 'easy-table-of-contents' ),
465
- // 'desc' => __( 'If your theme includes background images for unordered list elements, enable this option to support them.', 'easy-table-of-contents' ),
466
- // 'type' => 'checkbox',
467
- // 'default' => false,
468
- //),
469
- 'heading_levels' => array(
470
- 'id' => 'heading_levels',
471
- 'name' => __( 'Headings:', 'easy-table-of-contents' ),
472
- 'desc' => __( 'Select the heading to consider when generating the table of contents. Deselecting a heading will exclude it.', 'easy-table-of-contents' ),
473
- 'type' => 'checkboxgroup',
474
- 'options' => array(
475
- '1' => __( 'Heading 1 (h1)', 'easy-table-of-contents' ),
476
- '2' => __( 'Heading 2 (h2)', 'easy-table-of-contents' ),
477
- '3' => __( 'Heading 3 (h3)', 'easy-table-of-contents' ),
478
- '4' => __( 'Heading 4 (h4)', 'easy-table-of-contents' ),
479
- '5' => __( 'Heading 5 (h5)', 'easy-table-of-contents' ),
480
- '6' => __( 'Heading 6 (h6)', 'easy-table-of-contents' ),
481
- ),
482
- 'default' => array( '1', '2', '3', '4', '5', '6' ),
483
- ),
484
- 'exclude' => array(
485
- 'id' => 'exclude',
486
- 'name' => __( 'Exclude Headings', 'easy-table-of-contents' ),
487
- 'desc' => __( 'Specify headings to be excluded from appearing in the table of contents. Separate multiple headings with a pipe <code>|</code>. Use an asterisk <code>*</code> as a wildcard to match other text.', 'easy-table-of-contents' ),
488
- 'type' => 'text',
489
- 'size' => 'large',
490
- 'default' => '',
491
- ),
492
- 'exclude_desc' => array(
493
- 'id' => 'exclude_desc',
494
- 'name' => '',
495
- 'desc' => '<p><strong>' . __( 'Examples:', 'easy-table-of-contents' ) . '</strong></p>' .
496
- '<ul>' .
497
- '<li>' . __( '<code>Fruit*</code> Ignore headings starting with "Fruit".', 'easy-table-of-contents' ) . '</li>' .
498
- '<li>' . __( '<code>*Fruit Diet*</code> Ignore headings with "Fruit Diet" somewhere in the heading.', 'easy-table-of-contents' ) . '</li>' .
499
- '<li>' . __( '<code>Apple Tree|Oranges|Yellow Bananas</code> Ignore headings that are exactly "Apple Tree", "Oranges" or "Yellow Bananas".', 'easy-table-of-contents' ) . '</li>' .
500
- '</ul>' .
501
- '<p>' . __( '<strong>Note:</strong> This is not case sensitive.', 'easy-table-of-contents' ) . '</p>',
502
- 'type' => 'descriptive_text',
503
- ),
504
- 'smooth_scroll_offset' => array(
505
- 'id' => 'smooth_scroll_offset',
506
- 'name' => __( 'Smooth Scroll Offset', 'easy-table-of-contents' ),
507
- 'desc' => 'px<br/>' . __( 'If you have a consistent menu across the top of your site, you can adjust the top offset to stop the headings from appearing underneath the top menu. A setting of 30 accommodates the WordPress admin bar. This setting only has an effect after you have enabled Smooth Scroll option.', 'easy-table-of-contents' ),
508
- 'type' => 'number',
509
- 'size' => 'small',
510
- 'default' => 30
511
- ),
512
- 'mobile_smooth_scroll_offset' => array(
513
- 'id' => 'mobile_smooth_scroll_offset',
514
- 'name' => __( 'Mobile Smooth Scroll Offset', 'easy-table-of-contents' ),
515
- 'desc' => 'px<br/>' . __( 'This provides the same function as the Smooth Scroll Offset option above but applied when the user is visiting your site on a mobile device.', 'easy-table-of-contents' ),
516
- 'type' => 'number',
517
- 'size' => 'small',
518
- 'default' => 0
519
- ),
520
- 'restrict_path' => array(
521
- 'id' => 'restrict_path',
522
- 'name' => __( 'Limit Path', 'easy-table-of-contents' ),
523
- 'desc' => '<br/>' . __( 'Restrict generation of the table of contents to pages that match the required path. This path is from the root of your site and always begins with a forward slash.', 'easy-table-of-contents' ) .
524
- '<br/><span class="description">' . __( 'Eg: /wiki/, /corporate/annual-reports/', 'easy-table-of-contents' ) . '</span>',
525
- 'type' => 'text',
526
- ),
527
- 'fragment_prefix' => array(
528
- 'id' => 'fragment_prefix',
529
- 'name' => __( 'Default Anchor Prefix', 'easy-table-of-contents' ),
530
- 'desc' => '<br/>' . __( 'Anchor targets are restricted to alphanumeric characters as per HTML specification (see readme for more detail). The default anchor prefix will be used when no characters qualify. When left blank, a number will be used instead.', 'easy-table-of-contents' ) .
531
- '<br/>' . __( 'This option normally applies to content written in character sets other than ASCII.', 'easy-table-of-contents' ) .
532
- '<br/><span class="description">' . __( 'Eg: i, toc_index, index, _', 'easy-table-of-contents' ) . '</span>',
533
- 'type' => 'text',
534
- 'default' => 'i',
535
- ),
536
- 'widget_affix_selector' => array(
537
- 'id' => 'widget_affix_selector',
538
- 'name' => __( 'Widget Affix Selector', 'easy-table-of-contents' ),
539
- 'desc' => '<br/>' . __( 'To enable the option to affix or pin the Table of Contents widget enter the theme\'s sidebar class or id.', 'easy-table-of-contents' ) .
540
- '<br/>' . __( 'Since every theme is different, this can not be determined automatically. If you are unsure how to find the sidebar\'s class or id, please ask the theme\'s support persons.', 'easy-table-of-contents' ) .
541
- '<br/><span class="description">' . __( 'Eg: .widget-area or #sidebar', 'easy-table-of-contents' ) . '</span>',
542
- 'type' => 'text',
543
- 'default' => '',
544
- ),
545
- )
546
- ),
547
- );
548
-
549
- return apply_filters( 'ez_toc_registered_settings', $options );
550
- }
551
-
552
- /**
553
- * The default values for the registered settings and options.
554
- *
555
- * @access private
556
- * @since 1.0
557
- * @static
558
- *
559
- * @return array
560
- */
561
- private static function getDefaults() {
562
-
563
- $defaults = array(
564
- 'fragment_prefix' => 'i',
565
- 'position' => 'before',
566
- 'start' => 4,
567
- 'show_heading_text' => true,
568
- 'heading_text' => 'Table of Contents',
569
- 'enabled_post_types' => array( 'page' ),
570
- 'auto_insert_post_types' => array(),
571
- 'show_hierarchy' => true,
572
- 'counter' => 'decimal',
573
- 'smooth_scroll' => true,
574
- 'smooth_scroll_offset' => 30,
575
- 'mobile_smooth_scroll_offset' => 0,
576
- 'visibility' => true,
577
- //'visibility_show' => 'show',
578
- //'visibility_hide' => 'hide',
579
- 'visibility_hide_by_default' => false,
580
- 'width' => 'auto',
581
- 'width_custom' => 275,
582
- 'width_custom_units' => 'px',
583
- 'wrapping' => 'none',
584
- 'title_font_size' => 120,
585
- 'title_font_size_units' => '%',
586
- 'title_font_weight' => 500,
587
- 'font_size' => 95,
588
- 'font_size_units' => '%',
589
- 'theme' => 'grey',
590
- 'custom_background_colour' => '#fff',
591
- 'custom_border_colour' => '#ddd',
592
- 'custom_title_colour' => '#999',
593
- 'custom_link_colour' => '#428bca',
594
- 'custom_link_hover_colour' => '#2a6496',
595
- 'custom_link_visited_colour' => '#428bca',
596
- 'lowercase' => false,
597
- 'hyphenate' => false,
598
- //'bullet_spacing' => false,
599
- 'include_homepage' => false,
600
- 'exclude_css' => false,
601
- 'exclude' => '',
602
- 'heading_levels' => array( '1', '2', '3', '4', '5', '6' ),
603
- 'restrict_path' => '',
604
- 'css_container_class' => '',
605
- //'show_toc_in_widget_only' => false,
606
- //'show_toc_in_widget_only_post_types' => array(),
607
- 'widget_affix_selector' => '',
608
- );
609
-
610
- return apply_filters( 'ez_toc_get_default_options', $defaults );
611
- }
612
-
613
- /**
614
- * Get the default options array.
615
- *
616
- * @access private
617
- * @since 1.0
618
- * @static
619
- *
620
- * @return array
621
- */
622
- private static function getOptions() {
623
-
624
- $defaults = self::getDefaults();
625
- $options = get_option( 'ez-toc-settings', $defaults );
626
-
627
- //return apply_filters( 'ez_toc_get_options', wp_parse_args( $options, $defaults ) );
628
- return apply_filters( 'ez_toc_get_options', $options );
629
- }
630
-
631
- /**
632
- * Get option value by key name.
633
- *
634
- * @access public
635
- * @since 1.0
636
- * @static
637
- *
638
- * @param string $key
639
- * @param bool|false $default
640
- *
641
- * @return mixed
642
- */
643
- public static function get( $key, $default = false ) {
644
-
645
- $options = self::getOptions();
646
-
647
- $value = array_key_exists( $key, $options ) ? $options[ $key ] : $default;
648
- $value = apply_filters( 'ez_toc_get_option', $value, $key, $default );
649
-
650
- return apply_filters( 'ez_toc_get_option_' . $key, $value, $key, $default );
651
- }
652
-
653
- /**
654
- * Set an option value by key name.
655
- *
656
- * @access public
657
- * @since 1.0
658
- * @static
659
- *
660
- * @param string $key
661
- * @param bool|false $value
662
- *
663
- * @return bool
664
- */
665
- public static function set( $key, $value = false ) {
666
-
667
- if ( empty( $value ) ) {
668
-
669
- $remove_option = self::delete( $key );
670
-
671
- return $remove_option;
672
- }
673
-
674
- $options = self::getOptions();
675
-
676
- $options[ $key ] = apply_filters( 'ez_toc_update_option', $value, $key );
677
-
678
- return update_option( 'ez-toc-settings', $options );
679
- }
680
-
681
- /**
682
- * Delete an option from the options table by option key name.
683
- *
684
- * @access public
685
- * @since 1.0
686
- * @static
687
- *
688
- * @param string $key
689
- *
690
- * @return bool
691
- */
692
- public static function delete( $key ) {
693
-
694
- // First let's grab the current settings
695
- $options = get_option( 'ez-toc-settings' );
696
-
697
- // Next let's try to update the value
698
- if ( array_key_exists( $key, $options ) ) {
699
-
700
- unset( $options[ $key ] );
701
- }
702
-
703
- return update_option( 'ez-toc-settings', $options );
704
- }
705
-
706
- /**
707
- * Sanitize a hex color from user input.
708
- *
709
- * Tries to convert $string into a valid hex colour.
710
- * Returns $default if $string is not a hex value, otherwise returns verified hex.
711
- *
712
- * @access private
713
- * @since 1.0
714
- * @static
715
- *
716
- * @param string $string
717
- * @param string $default
718
- *
719
- * @return mixed|string
720
- */
721
- private static function hex_value( $string = '', $default = '#' ) {
722
-
723
- $return = $default;
724
-
725
- if ( $string ) {
726
- // strip out non hex chars
727
- $return = preg_replace( '/[^a-fA-F0-9]*/', '', $string );
728
-
729
- switch ( strlen( $return ) ) {
730
- case 3: // do next
731
- case 6:
732
- $return = '#' . $return;
733
- break;
734
-
735
- default:
736
- if ( strlen( $return ) > 6 ) {
737
- $return = '#' . substr( $return, 0, 6 );
738
- } // if > 6 chars, then take the first 6
739
- elseif ( strlen( $return ) > 3 && strlen( $return ) < 6 ) {
740
- $return = '#' . substr( $return, 0, 3 );
741
- } // if between 3 and 6, then take first 3
742
- else {
743
- $return = $default;
744
- } // not valid, return $default
745
- }
746
- }
747
-
748
- return $return;
749
- }
750
-
751
- /**
752
- * Get the registered post types minus excluded core types.
753
- *
754
- * @access public
755
- * @since 1.0
756
- * @static
757
- *
758
- * @return array
759
- */
760
- public static function getPostTypes() {
761
-
762
- $exclude = apply_filters( 'ez_toc_exclude_post_types', array( 'attachment', 'revision', 'nav_menu_item', 'safecss' ) );
763
- $registered = get_post_types( array(), 'objects' );
764
- $types = array();
765
-
766
- foreach ( $registered as $post ) {
767
-
768
- if ( in_array( $post->name, $exclude ) ) {
769
-
770
- continue;
771
- }
772
-
773
- $types[ $post->name ] = $post->label;
774
- }
775
-
776
- return $types;
777
- }
778
-
779
- /**
780
- * Missing Callback
781
- *
782
- * If a settings field type callback is not callable, alert the user.
783
- *
784
- * @access public
785
- * @since 1.0
786
- * @static
787
- *
788
- * @param array $args Arguments passed by the setting
789
- */
790
- public static function missingCallback( $args ) {
791
-
792
- printf(
793
- __( 'The callback function used for the <strong>%s</strong> setting is missing.', 'easy-table-of-contents' ),
794
- $args['id']
795
- );
796
- }
797
-
798
- /**
799
- * Text Callback
800
- *
801
- * Renders text fields.
802
- *
803
- * @access public
804
- * @since 1.0
805
- * @static
806
- *
807
- * @param array $args Arguments passed by the setting
808
- * @param null $value
809
- */
810
- public static function text( $args, $value = null ) {
811
-
812
- if ( is_null( $value ) ) {
813
-
814
- $value = self::get( $args['id'], $args['default'] );
815
- }
816
-
817
- if ( isset( $args['faux'] ) && true === $args['faux'] ) {
818
-
819
- $args['readonly'] = true;
820
- $value = isset( $args['default'] ) ? $args['default'] : '';
821
- $name = '';
822
-
823
- } else {
824
-
825
- $name = ' name="ez-toc-settings[' . $args['id'] . ']"';
826
- }
827
-
828
- $readonly = isset( $args['readonly'] ) && $args['readonly'] === true ? ' readonly="readonly"' : '';
829
- $size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
830
-
831
- $html = '<input type="text" class="' . $size . '-text" id="ez-toc-settings[' . $args['id'] . ']"' . $name . ' value="' . esc_attr( stripslashes( $value ) ) . '"' . $readonly . '/>';
832
-
833
- if ( 0 < strlen( $args['desc'] ) ) {
834
-
835
- $html .= '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
836
- }
837
-
838
- echo $html;
839
- }
840
-
841
- /**
842
- * Textarea Callback.
843
- *
844
- * Renders a textarea.
845
- *
846
- * @access public
847
- * @since 1.1
848
- * @static
849
- *
850
- * @param array $args Arguments passed by the setting
851
- * @param null $value
852
- */
853
- public static function textarea( $args, $value = null ) {
854
-
855
- $html = '';
856
-
857
- if ( is_null( $value ) ) {
858
-
859
- $value = self::get( $args['id'], $args['default'] );
860
- }
861
-
862
- if ( isset( $args['faux'] ) && true === $args['faux'] ) {
863
-
864
- $args['readonly'] = true;
865
- $value = isset( $args['default'] ) ? $args['default'] : '';
866
- $name = '';
867
-
868
- } else {
869
-
870
- $name = ' name="ez-toc-settings[' . $args['id'] . ']"';
871
- }
872
-
873
- $readonly = isset( $args['readonly'] ) && $args['readonly'] === true ? ' readonly="readonly"' : '';
874
- $size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
875
-
876
- if ( 0 < strlen( $args['desc'] ) ) {
877
-
878
- $html .= '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
879
- }
880
-
881
- $html .= '<textarea rows="10" cols="50" class="' . $size . '-text" id="ez-toc-settings[' . $args['id'] . ']"' . $name . $readonly . '/>' . esc_textarea( $value ) . '</textarea>';
882
-
883
- echo $html;
884
- }
885
-
886
- /**
887
- * Number Callback
888
- *
889
- * Renders number fields.
890
- *
891
- * @access public
892
- * @since 1.0
893
- * @static
894
- *
895
- * @param array $args Arguments passed by the setting
896
- */
897
- public static function number( $args ) {
898
-
899
- $value = self::get( $args['id'], $args['default'] );
900
-
901
- if ( isset( $args['faux'] ) && true === $args['faux'] ) {
902
-
903
- $args['readonly'] = true;
904
- $value = isset( $args['default'] ) ? $args['default'] : '';
905
- $name = '';
906
-
907
- } else {
908
-
909
- $name = ' name="ez-toc-settings[' . $args['id'] . ']"';
910
- }
911
-
912
- $readonly = isset( $args['readonly'] ) && $args['readonly'] === true ? ' readonly="readonly"' : '';
913
- $size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
914
-
915
- $html = '<input type="number" class="' . $size . '-text" id="ez-toc-settings[' . $args['id'] . ']"' . $name . ' value="' . esc_attr( stripslashes( $value ) ) . '"' . $readonly . '/>';
916
-
917
- if ( 0 < strlen( $args['desc'] ) ) {
918
-
919
- $html .= '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
920
- }
921
-
922
- echo $html;
923
- }
924
-
925
- /**
926
- * Checkbox Callback
927
- *
928
- * Renders checkboxes.
929
- *
930
- * @access public
931
- * @since 1.0
932
- * @static
933
- *
934
- * @param array $args Arguments passed by the setting
935
- * @param null $value
936
- */
937
- public static function checkbox( $args, $value = null ) {
938
-
939
- if ( is_null( $value ) ) {
940
-
941
- $value = self::get( $args['id'], $args['default'] );
942
- }
943
-
944
- if ( isset( $args['faux'] ) && true === $args['faux'] ) {
945
-
946
- $name = '';
947
-
948
- } else {
949
-
950
- $name = ' name="ez-toc-settings[' . $args['id'] . ']"';
951
- }
952
-
953
- $checked = $value ? checked( 1, $value, false ) : '';
954
-
955
- $html = '<input type="checkbox" id="ez-toc-settings[' . $args['id'] . ']"' . $name . ' value="1" ' . $checked . '/>';
956
-
957
- if ( 0 < strlen( $args['desc'] ) ) {
958
-
959
- $html .= '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
960
- }
961
-
962
- echo $html;
963
- }
964
-
965
- /**
966
- * Multicheck Callback
967
- *
968
- * Renders multiple checkboxes.
969
- *
970
- * @access public
971
- * @since 1.0
972
- * @static
973
- *
974
- * @param array $args Arguments passed by the setting
975
- * @param null $value
976
- */
977
- public static function checkboxgroup( $args, $value = null ) {
978
-
979
- if ( is_null( $value ) ) {
980
-
981
- $value = self::get( $args['id'], $args['default'] );
982
- }
983
-
984
- if ( ! empty( $args['options'] ) ) {
985
-
986
- foreach ( $args['options'] as $key => $option ):
987
-
988
- if ( in_array( $key, $value ) ) {
989
-
990
- $enabled = $option;
991
-
992
- } else {
993
-
994
- $enabled = null;
995
- }
996
-
997
- echo '<input name="ez-toc-settings[' . $args['id'] . '][' . $key . ']" id="ez-toc-settings[' . $args['id'] . '][' . $key . ']" type="checkbox" value="' . $key . '" ' . checked( $option, $enabled, false ) . '/>&nbsp;';
998
- echo '<label for="ez-toc-settings[' . $args['id'] . '][' . $key . ']">' . $option . '</label><br/>';
999
-
1000
- endforeach;
1001
-
1002
- if ( 0 < strlen( $args['desc'] ) ) {
1003
-
1004
- echo '<p class="description">' . $args['desc'] . '</p>';
1005
- }
1006
- }
1007
- }
1008
-
1009
- /**
1010
- * Radio Callback
1011
- *
1012
- * Renders radio groups.
1013
- *
1014
- * @access public
1015
- * @since 1.0
1016
- * @static
1017
- *
1018
- * @param array $args Arguments passed by the setting
1019
- */
1020
- public static function radio( $args ) {
1021
-
1022
- $value = self::get( $args['id'], $args['default'] );
1023
-
1024
- foreach ( $args['options'] as $key => $option ) {
1025
-
1026
- echo '<input name="ez-toc-settings[' . $args['id'] . ']"" id="ez-toc-settings[' . $args['id'] . '][' . $key . ']" type="radio" value="' . $key . '" ' . checked( $key, $value, false ) . '/>&nbsp;';
1027
- echo '<label for="ez-toc-settings[' . $args['id'] . '][' . $key . ']">' . $option . '</label><br/>';
1028
- }
1029
-
1030
- if ( 0 < strlen( $args['desc'] ) ) {
1031
-
1032
- echo '<p class="description">' . $args['desc'] . '</p>';
1033
- }
1034
- }
1035
-
1036
- /**
1037
- * Select Callback
1038
- *
1039
- * Renders select fields.
1040
- *
1041
- * @access public
1042
- * @since 1.0
1043
- * @static
1044
- *
1045
- * @param array $args Arguments passed by the setting.
1046
- */
1047
- public static function select( $args ) {
1048
-
1049
- $value = self::get( $args['id'], $args['default'] );
1050
-
1051
- if ( isset( $args['placeholder'] ) ) {
1052
- $placeholder = $args['placeholder'];
1053
- } else {
1054
- $placeholder = '';
1055
- }
1056
-
1057
- if ( isset( $args['chosen'] ) ) {
1058
- $chosen = 'class="enhanced"';
1059
- } else {
1060
- $chosen = '';
1061
- }
1062
-
1063
- $html = '<select id="ez-toc-settings[' . $args['id'] . ']" name="ez-toc-settings[' . $args['id'] . ']" ' . $chosen . 'data-placeholder="' . $placeholder . '" />';
1064
-
1065
- foreach ( $args['options'] as $option => $name ) {
1066
- $selected = selected( $option, $value, false );
1067
- $html .= '<option value="' . $option . '" ' . $selected . '>' . $name . '</option>';
1068
- }
1069
-
1070
- $html .= '</select>';
1071
-
1072
- if ( 0 < strlen( $args['desc'] ) ) {
1073
-
1074
- $html .= '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
1075
- }
1076
-
1077
- echo $html;
1078
- }
1079
-
1080
- /**
1081
- * Select Drop Down Callback
1082
- *
1083
- * Renders select with option group fields.
1084
- *
1085
- * @access public
1086
- * @since 1.0
1087
- * @static
1088
- *
1089
- * @param array $args Arguments passed by the setting.
1090
- */
1091
- public static function selectgroup( $args ) {
1092
-
1093
- $value = self::get( $args['id'], $args['default'] );
1094
-
1095
- if ( isset( $args['placeholder'] ) ) {
1096
- $placeholder = $args['placeholder'];
1097
- } else {
1098
- $placeholder = '';
1099
- }
1100
-
1101
- if ( isset( $args['chosen'] ) ) {
1102
- $chosen = 'class="enhanced"';
1103
- } else {
1104
- $chosen = '';
1105
- }
1106
-
1107
- $html = '<select id="ez-toc-settings[' . $args['id'] . ']" name="ez-toc-settings[' . $args['id'] . ']" ' . $chosen . 'data-placeholder="' . $placeholder . '" />';
1108
-
1109
- foreach ( $args['options'] as $group ) {
1110
-
1111
- $html .= sprintf( '<optgroup label="%1$s">', $group['name'] );
1112
-
1113
- foreach ( $group['options'] as $option => $name ) {
1114
-
1115
- $selected = selected( $option, $value, false );
1116
- $html .= '<option value="' . $option . '" ' . $selected . '>' . $name . '</option>';
1117
- }
1118
-
1119
- $html .= '</optgroup>';
1120
- }
1121
-
1122
- $html .= '</select>';
1123
-
1124
- if ( 0 < strlen( $args['desc'] ) ) {
1125
-
1126
- $html .= '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
1127
- }
1128
-
1129
- echo $html;
1130
- }
1131
-
1132
- /**
1133
- * Header Callback
1134
- *
1135
- * Renders the header.
1136
- *
1137
- * @access public
1138
- * @since 1.0
1139
- * @static
1140
- *
1141
- * @param array $args Arguments passed by the setting
1142
- */
1143
- public static function header( $args ) {
1144
-
1145
- echo '<hr/>';
1146
-
1147
- if ( 0 < strlen( $args['desc'] ) ) {
1148
-
1149
- echo '<p>' . wp_kses_post( $args['desc'] ) . '</p>';
1150
- }
1151
- }
1152
-
1153
- /**
1154
- * Descriptive text callback.
1155
- *
1156
- * Renders descriptive text onto the settings field.
1157
- *
1158
- * @access public
1159
- * @since 1.0
1160
- * @static
1161
- *
1162
- * @param array $args Arguments passed by the setting
1163
- */
1164
- public static function descriptive_text( $args ) {
1165
-
1166
- echo wp_kses_post( $args['desc'] );
1167
- }
1168
-
1169
- /**
1170
- * Color picker Callback
1171
- *
1172
- * Renders color picker fields.
1173
- *
1174
- * @access public
1175
- * @since 1.0
1176
- * @static
1177
- *
1178
- * @param array $args Arguments passed by the setting
1179
- */
1180
- public static function color( $args ) {
1181
-
1182
- $value = self::get( $args['id'], $args['default'] );
1183
-
1184
- $default = isset( $args['default'] ) ? $args['default'] : '';
1185
-
1186
- $html = '<input type="text" class="ez-toc-color-picker" id="ez-toc-settings[' . $args['id'] . ']" name="ez-toc-settings[' . $args['id'] . ']" value="' . esc_attr( $value ) . '" data-default-color="' . esc_attr( $default ) . '" />';
1187
-
1188
- if ( 0 < strlen( $args['desc'] ) ) {
1189
-
1190
- echo '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
1191
- }
1192
-
1193
- echo $html;
1194
- }
1195
-
1196
- /**
1197
- * Custom table of contents width.
1198
- *
1199
- * @access public
1200
- * @since 1.0
1201
- * @static
1202
- *
1203
- * @param array $args
1204
- */
1205
- public static function custom_width( $args ) {
1206
-
1207
- //$value = self::get( $args['id'], $args['default'] );
1208
-
1209
- self::text(
1210
- array(
1211
- 'id' => $args['id'],
1212
- 'desc' => '',
1213
- 'size' => 'small',
1214
- 'default' => $args['default'],
1215
- )
1216
- );
1217
-
1218
- self::select(
1219
- array(
1220
- 'id' => $args['id'] . '_units',
1221
- 'desc' => '',
1222
- 'options' => array(
1223
- 'px' => 'px',
1224
- '%' => '%',
1225
- 'em' => 'em',
1226
- ),
1227
- 'default' => 'px',
1228
- )
1229
- );
1230
-
1231
- if ( 0 < strlen( $args['desc'] ) ) {
1232
-
1233
- echo '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
1234
- }
1235
- }
1236
-
1237
- /**
1238
- * Custom font size callback.
1239
- *
1240
- * @access public
1241
- * @since 1.0
1242
- * @static
1243
- *
1244
- * @param array $args
1245
- */
1246
- public static function font_size( $args ) {
1247
-
1248
- //$value = self::get( $args['id'], $args['default'] );
1249
-
1250
- self::text(
1251
- array(
1252
- 'id' => $args['id'],
1253
- 'desc' => '',
1254
- 'size' => 'small',
1255
- 'default' => $args['default'],
1256
- )
1257
- );
1258
-
1259
- self::select(
1260
- array(
1261
- 'id' => $args['id'] . '_units',
1262
- 'desc' => '',
1263
- 'options' => array(
1264
- 'pt' => 'pt',
1265
- 'px' => 'px',
1266
- '%' => '%',
1267
- 'em' => 'em',
1268
- ),
1269
- 'default' => '%',
1270
- )
1271
- );
1272
-
1273
- if ( 0 < strlen( $args['desc'] ) ) {
1274
-
1275
- echo '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
1276
- }
1277
- }
1278
- }
1279
-
1280
- add_action( 'admin_init', array( 'ezTOC_Option', 'register' ) );
1281
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ if ( ! class_exists( 'ezTOC_Option' ) ) {
7
+
8
+ /**
9
+ * Class ezTOC_Option
10
+ *
11
+ * Credit: Adapted from Easy Digital Downloads.
12
+ */
13
+ final class ezTOC_Option {
14
+
15
+ /**
16
+ * Register the plugins core settings and options.
17
+ *
18
+ * @access private
19
+ * @since 1.0
20
+ * @static
21
+ */
22
+ public static function register() {
23
+
24
+ if ( false === get_option( 'ez-toc-settings' ) ) {
25
+
26
+ add_option( 'ez-toc-settings', self::getDefaults() );
27
+ }
28
+
29
+ foreach ( self::getRegistered() as $section => $settings ) {
30
+
31
+ add_settings_section(
32
+ 'ez_toc_settings_' . $section,
33
+ __return_null(),
34
+ '__return_false',
35
+ 'ez_toc_settings_' . $section
36
+ );
37
+
38
+ foreach ( $settings as $option ) {
39
+
40
+ $name = isset( $option['name'] ) ? $option['name'] : '';
41
+
42
+ add_settings_field(
43
+ 'ez-toc-settings[' . $option['id'] . ']',
44
+ $name,
45
+ method_exists( __CLASS__, $option['type'] ) ? array( __CLASS__, $option['type'] ) : array( __CLASS__, 'missingCallback' ),
46
+ 'ez_toc_settings_' . $section,
47
+ 'ez_toc_settings_' . $section,
48
+ array(
49
+ 'section' => $section,
50
+ 'id' => isset( $option['id'] ) ? $option['id'] : null,
51
+ 'desc' => ! empty( $option['desc'] ) ? $option['desc'] : '',
52
+ 'name' => isset( $option['name'] ) ? $option['name'] : null,
53
+ 'size' => isset( $option['size'] ) ? $option['size'] : null,
54
+ 'options' => isset( $option['options'] ) ? $option['options'] : '',
55
+ 'default' => isset( $option['default'] ) ? $option['default'] : '',
56
+ 'min' => isset( $option['min'] ) ? $option['min'] : null,
57
+ 'max' => isset( $option['max'] ) ? $option['max'] : null,
58
+ 'step' => isset( $option['step'] ) ? $option['step'] : null,
59
+ 'chosen' => isset( $option['chosen'] ) ? $option['chosen'] : null,
60
+ 'placeholder' => isset( $option['placeholder'] ) ? $option['placeholder'] : null,
61
+ 'allow_blank' => isset( $option['allow_blank'] ) ? $option['allow_blank'] : true,
62
+ 'readonly' => isset( $option['readonly'] ) ? $option['readonly'] : false,
63
+ 'faux' => isset( $option['faux'] ) ? $option['faux'] : false,
64
+ )
65
+ );
66
+ }
67
+
68
+ }
69
+
70
+ // Creates our settings in the options table
71
+ register_setting( 'ez-toc-settings', 'ez-toc-settings', array( __CLASS__, 'sanitize' ) );
72
+ }
73
+
74
+ /**
75
+ * Callback for settings sanitization.
76
+ *
77
+ * @access private
78
+ * @since 1.0
79
+ * @static
80
+ *
81
+ * @param array $input The value inputted in the field.
82
+ *
83
+ * @return string $input Sanitized value.
84
+ */
85
+ public static function sanitize( $input = array() ) {
86
+
87
+ $options = self::getOptions();
88
+
89
+ if ( empty( $_POST['_wp_http_referer'] ) ) {
90
+
91
+ return $input;
92
+ }
93
+
94
+ $registered = self::getRegistered();
95
+
96
+ foreach ( $registered as $sectionID => $sectionOptions ) {
97
+
98
+ $input = $input ? $input : array();
99
+ $input = apply_filters( 'ez_toc_settings_' . $sectionID . '_sanitize', $input );
100
+
101
+ // Loop through each setting being saved and pass it through a sanitization filter
102
+ foreach ( $input as $key => $value ) {
103
+
104
+ // Get the setting type (checkbox, select, etc)
105
+ $type = isset( $registered[ $sectionID ][ $key ]['type'] ) ? $registered[ $sectionID ][ $key ]['type'] : false;
106
+
107
+ if ( $type ) {
108
+
109
+ // Field type specific filter
110
+ $input[ $key ] = apply_filters( 'ez_toc_settings_sanitize_' . $type, $value, $key );
111
+ }
112
+
113
+ // General filter
114
+ $input[ $key ] = apply_filters( 'ez_toc_settings_sanitize', $input[ $key ], $key );
115
+ }
116
+
117
+ // Loop through the registered options.
118
+ foreach ( $sectionOptions as $optionID => $optionProperties ) {
119
+
120
+ // Unset any that are empty for the section being saved.
121
+ if ( empty( $input[ $optionID ] ) ) {
122
+
123
+ unset( $options[ $optionID ] );
124
+ }
125
+
126
+ // Check for the checkbox option type.
127
+ if ( array_key_exists( 'type', $optionProperties ) && 'checkbox' == $optionProperties['type'] ) {
128
+
129
+ // If it does not exist in the options values being saved, add the option ID and set its value to `0`.
130
+ // This matches WP core behavior for saving checkbox option values.
131
+ if ( ! array_key_exists( $optionID, $input ) ) {
132
+
133
+ $input[ $optionID ] = '0';
134
+ }
135
+ }
136
+ }
137
+
138
+ }
139
+
140
+ // Merge our new settings with the existing
141
+ $output = array_merge( $options, $input );
142
+
143
+ return $output;
144
+ }
145
+
146
+ /**
147
+ * The core registered settings and options.
148
+ *
149
+ * @access private
150
+ * @since 1.0
151
+ * @static
152
+ *
153
+ * @return array
154
+ */
155
+ private static function getRegistered() {
156
+
157
+ $options = array(
158
+ 'general' => apply_filters(
159
+ 'ez_toc_settings_general',
160
+ array(
161
+ 'enabled_post_types' => array(
162
+ 'id' => 'enabled_post_types',
163
+ 'name' => __( 'Enable Support', 'easy-table-of-contents' ),
164
+ 'desc' => __( 'Select the post types to enable the support for table of contents.', 'easy-table-of-contents' ),
165
+ 'type' => 'checkboxgroup',
166
+ 'options' => self::getPostTypes(),
167
+ 'default' => array(),
168
+ ),
169
+ 'auto_insert_post_types' => array(
170
+ 'id' => 'auto_insert_post_types',
171
+ 'name' => __( 'Auto Insert', 'easy-table-of-contents' ),
172
+ 'desc' => __( 'Select the post types which will have the table of contents automatically inserted.', 'easy-table-of-contents' ) .
173
+ '<br><span class="description">' . __( 'NOTE: The table of contents will only be automatically inserted on post types for which it has been enabled.', 'easy-table-of-contents' ) . '<span>',
174
+ 'type' => 'checkboxgroup',
175
+ 'options' => self::getPostTypes(),
176
+ 'default' => array(),
177
+ ),
178
+ 'position' => array(
179
+ 'id' => 'position',
180
+ 'name' => __( 'Position', 'easy-table-of-contents' ),
181
+ 'desc' => __( 'Choose where where you want to display the table of contents.', 'easy-table-of-contents' ),
182
+ 'type' => 'select',
183
+ 'options' => array(
184
+ 'before' => __( 'Before first heading (default)', 'easy-table-of-contents' ),
185
+ 'after' => __( 'After first heading', 'easy-table-of-contents' ),
186
+ 'top' => __( 'Top', 'easy-table-of-contents' ),
187
+ 'bottom' => __( 'Bottom', 'easy-table-of-contents' ),
188
+ //'placeholder' => __( 'Replace [toc] placeholder. For backwards compatibility with Table of Content Plus.', 'easy-table-of-contents' ),
189
+ ),
190
+ 'default' => 1,
191
+ ),
192
+ 'start' => array(
193
+ 'id' => 'start',
194
+ 'name' => __( 'Show when', 'easy-table-of-contents' ),
195
+ 'desc' => __( 'or more headings are present', 'easy-table-of-contents' ),
196
+ 'type' => 'select',
197
+ 'options' => array_combine( range( 1, 10 ), range( 1, 10 ) ),
198
+ 'default' => 4,
199
+ ),
200
+ 'show_heading_text' => array(
201
+ 'id' => 'show_heading_text',
202
+ 'name' => __( 'Display Header Label', 'easy-table-of-contents' ),
203
+ 'desc' => __( 'Show header text above the table of contents.', 'easy-table-of-contents' ),
204
+ 'type' => 'checkbox',
205
+ 'default' => true,
206
+ ),
207
+ 'heading_text' => array(
208
+ 'id' => 'heading_text',
209
+ 'name' => __( 'Header Label', 'easy-table-of-contents' ),
210
+ 'desc' => __( 'Eg: Contents, Table of Contents, Page Contents', 'easy-table-of-contents' ),
211
+ 'type' => 'text',
212
+ 'default' => __( 'Contents', 'easy-table-of-contents' ),
213
+ ),
214
+ 'visibility' => array(
215
+ 'id' => 'visibility',
216
+ 'name' => __( 'Toggle View', 'easy-table-of-contents' ),
217
+ 'desc' => __( 'Allow the user to toggle the visibility of the table of contents.', 'easy-table-of-contents' ),
218
+ 'type' => 'checkbox',
219
+ 'default' => true,
220
+ ),
221
+ //'visibility_show' => array(
222
+ // 'id' => 'visibility_show',
223
+ // 'name' => __( 'Show Label', 'easy-table-of-contents' ),
224
+ // 'desc' => __( 'Eg: show', 'easy-table-of-contents' ),
225
+ // 'type' => 'text',
226
+ // 'default' => __( 'show', 'easy-table-of-contents' ),
227
+ //),
228
+ //'visibility_hide' => array(
229
+ // 'id' => 'visibility_hide',
230
+ // 'name' => __( 'Hide Label', 'easy-table-of-contents' ),
231
+ // 'desc' => __( 'Eg: hide', 'easy-table-of-contents' ),
232
+ // 'type' => 'text',
233
+ // 'default' => __( 'hide', 'easy-table-of-contents' ),
234
+ //),
235
+ 'visibility_hide_by_default' => array(
236
+ 'id' => 'visibility_hide_by_default',
237
+ 'name' => __( 'Initial View', 'easy-table-of-contents' ),
238
+ 'desc' => __( 'Initially hide the table of contents.', 'easy-table-of-contents' ),
239
+ 'type' => 'checkbox',
240
+ 'default' => false,
241
+ ),
242
+ 'show_hierarchy' => array(
243
+ 'id' => 'show_hierarchy',
244
+ 'name' => __( 'Show as Hierarchy', 'easy-table-of-contents' ),
245
+ 'desc' => '',
246
+ 'type' => 'checkbox',
247
+ 'default' => true,
248
+ ),
249
+ 'counter' => array(
250
+ 'id' => 'counter',
251
+ 'name' => __( 'Counter', 'easy-table-of-contents' ),
252
+ 'desc' => '',
253
+ 'type' => 'select',
254
+ 'options' => array(
255
+ 'decimal' => __( 'Decimal (default)', 'easy-table-of-contents' ),
256
+ 'numeric' => __( 'Numeric', 'easy-table-of-contents' ),
257
+ 'roman' => __( 'Roman', 'easy-table-of-contents' ),
258
+ 'none' => __( 'None', 'easy-table-of-contents' ),
259
+ ),
260
+ 'default' => 'decimal',
261
+ ),
262
+ 'smooth_scroll' => array(
263
+ 'id' => 'smooth_scroll',
264
+ 'name' => __( 'Smooth Scroll', 'easy-table-of-contents' ),
265
+ 'desc' => '',
266
+ 'type' => 'checkbox',
267
+ 'default' => true,
268
+ ),
269
+ 'toc_loading' => array(
270
+ 'id' => 'toc_loading',
271
+ 'name' => __( 'TOC Loading Method', 'easy-table-of-contents' ),
272
+ 'desc' => '',
273
+ 'type' => 'select',
274
+ 'options' => array(
275
+ 'js' => __( 'JavaScript (default)', 'easy-table-of-contents' ),
276
+ 'css' => __( 'Pure CSS', 'easy-table-of-contents' ),
277
+
278
+ ),
279
+ 'default' => 'js',
280
+ ),
281
+ )
282
+ ),
283
+ 'appearance' => apply_filters(
284
+ 'ez_toc_settings_appearance',
285
+ array(
286
+ 'width' => array(
287
+ 'id' => 'width',
288
+ 'name' => __( 'Width', 'easy-table-of-contents' ),
289
+ 'desc' => '',
290
+ 'type' => 'selectgroup',
291
+ 'options' => array(
292
+ 'fixed' => array(
293
+ 'name' => __( 'Fixed', 'easy-table-of-contents' ),
294
+ 'options' => array(
295
+ '200px' => '200px',
296
+ '225px' => '225px',
297
+ '250px' => '250px',
298
+ '275px' => '275px',
299
+ '300px' => '300px',
300
+ '325px' => '325px',
301
+ '350px' => '350px',
302
+ '375px' => '375px',
303
+ '400px' => '400px',
304
+ ),
305
+ ),
306
+ 'relative' => array(
307
+ 'name' => __( 'Relative', 'easy-table-of-contents' ),
308
+ 'options' => array(
309
+ 'auto' => 'Auto',
310
+ '25%' => '25%',
311
+ '33%' => '33%',
312
+ '50%' => '50%',
313
+ '66%' => '66%',
314
+ '75%' => '75%',
315
+ '100%' => '100%',
316
+ ),
317
+ ),
318
+ 'other' => array(
319
+ 'name' => __( 'Custom', 'easy-table-of-contents' ),
320
+ 'options' => array(
321
+ 'custom' => __( 'User Defined', 'easy-table-of-contents' ),
322
+ ),
323
+ ),
324
+ ),
325
+ 'default' => 'auto',
326
+ ),
327
+ 'width_custom' => array(
328
+ 'id' => 'width_custom',
329
+ 'name' => __( 'Custom Width', 'easy-table-of-contents' ),
330
+ 'desc' => __( 'Select the User Defined option from the Width option to utilitze the custom width.', 'easy-table-of-contents' ),
331
+ 'type' => 'custom_width',
332
+ 'default' => 275,
333
+ ),
334
+ 'wrapping' => array(
335
+ 'id' => 'wrapping',
336
+ 'name' => __( 'Float', 'easy-table-of-contents' ),
337
+ 'desc' => '',
338
+ 'type' => 'select',
339
+ 'options' => array(
340
+ 'none' => __( 'None (Default)', 'easy-table-of-contents' ),
341
+ 'left' => __( 'Left', 'easy-table-of-contents' ),
342
+ 'right' => __( 'Right', 'easy-table-of-contents' ),
343
+ ),
344
+ 'default' => 'none',
345
+ ),
346
+ 'title_font_size' => array(
347
+ 'id' => 'title_font_size',
348
+ 'name' => __( 'Title Font Size', 'easy-table-of-contents' ),
349
+ 'desc' => '',
350
+ 'type' => 'font_size',
351
+ 'default' => 120,
352
+ ),
353
+ 'title_font_weight' => array(
354
+ 'id' => 'title_font_weight',
355
+ 'name' => __( 'Title Font Weight', 'easy-table-of-contents' ),
356
+ 'desc' => '',
357
+ 'type' => 'select',
358
+ 'options' => array(
359
+ '100' => __( 'Thin', 'easy-table-of-contents' ),
360
+ '200' => __( 'Extra Light', 'easy-table-of-contents' ),
361
+ '300' => __( 'Light', 'easy-table-of-contents' ),
362
+ '400' => __( 'Normal', 'easy-table-of-contents' ),
363
+ '500' => __( 'Medium', 'easy-table-of-contents' ),
364
+ '600' => __( 'Semi Bold', 'easy-table-of-contents' ),
365
+ '700' => __( 'Bold', 'easy-table-of-contents' ),
366
+ '800' => __( 'Extra Bold', 'easy-table-of-contents' ),
367
+ '900' => __( 'Heavy', 'easy-table-of-contents' ),
368
+ ),
369
+ 'default' => '500',
370
+ ),
371
+ 'font_size' => array(
372
+ 'id' => 'font_size',
373
+ 'name' => __( 'Font Size', 'easy-table-of-contents' ),
374
+ 'desc' => '',
375
+ 'type' => 'font_size',
376
+ 'default' => 95,
377
+ ),
378
+ 'theme' => array(
379
+ 'id' => 'theme',
380
+ 'name' => __( 'Theme', 'easy-table-of-contents' ),
381
+ 'desc' => __( 'The theme is only applied to the table of contents which is auto inserted into the post. The Table of Contents widget will inherit the theme widget styles.', 'easy-table-of-contents' ),
382
+ 'type' => 'radio',
383
+ 'options' => array(
384
+ 'grey' => __( 'Grey', 'easy-table-of-contents' ),
385
+ 'light-blue' => __( 'Light Blue', 'easy-table-of-contents' ),
386
+ 'white' => __( 'White', 'easy-table-of-contents' ),
387
+ 'black' => __( 'Black', 'easy-table-of-contents' ),
388
+ 'transparent' => __( 'Transparent', 'easy-table-of-contents' ),
389
+ 'custom' => __( 'Custom', 'easy-table-of-contents' ),
390
+ ),
391
+ 'default' => 'grey',
392
+ ),
393
+ 'custom_theme_header' => array(
394
+ 'id' => 'custom_theme_header',
395
+ 'name' => '<strong>' . __( 'Custom Theme', 'easy-table-of-contents' ) . '</strong>',
396
+ 'desc' => __( 'For the following settings to apply, select the Custom Theme option.', 'easy-table-of-contents' ),
397
+ 'type' => 'header',
398
+ ),
399
+ 'custom_background_colour' => array(
400
+ 'id' => 'custom_background_colour',
401
+ 'name' => __( 'Background Color', 'easy-table-of-contents' ),
402
+ 'desc' => '',
403
+ 'type' => 'color',
404
+ 'default' => '#fff',
405
+ ),
406
+ 'custom_border_colour' => array(
407
+ 'id' => 'custom_border_colour',
408
+ 'name' => __( 'Border Color', 'easy-table-of-contents' ),
409
+ 'desc' => '',
410
+ 'type' => 'color',
411
+ 'default' => '#ddd',
412
+ ),
413
+ 'custom_title_colour' => array(
414
+ 'id' => 'custom_title_colour',
415
+ 'name' => __( 'Title Color', 'easy-table-of-contents' ),
416
+ 'desc' => '',
417
+ 'type' => 'color',
418
+ 'default' => '#999',
419
+ ),
420
+ 'custom_link_colour' => array(
421
+ 'id' => 'custom_link_colour',
422
+ 'name' => __( 'Link Color', 'easy-table-of-contents' ),
423
+ 'desc' => '',
424
+ 'type' => 'color',
425
+ 'default' => '#428bca',
426
+ ),
427
+ 'custom_link_hover_colour' => array(
428
+ 'id' => 'custom_link_hover_colour',
429
+ 'name' => __( 'Link Hover Color', 'easy-table-of-contents' ),
430
+ 'desc' => '',
431
+ 'type' => 'color',
432
+ 'default' => '#2a6496',
433
+ ),
434
+ 'custom_link_visited_colour' => array(
435
+ 'id' => 'custom_link_visited_colour',
436
+ 'name' => __( 'Link Visited Color', 'easy-table-of-contents' ),
437
+ 'desc' => '',
438
+ 'type' => 'color',
439
+ 'default' => '#428bca',
440
+ ),
441
+ )
442
+ ),
443
+ 'advanced' => apply_filters(
444
+ 'ez_toc_settings_advanced',
445
+ array(
446
+ 'lowercase' => array(
447
+ 'id' => 'lowercase',
448
+ 'name' => __( 'Lowercase', 'easy-table-of-contents' ),
449
+ 'desc' => __( 'Ensure anchors are in lowercase.', 'easy-table-of-contents' ),
450
+ 'type' => 'checkbox',
451
+ 'default' => false,
452
+ ),
453
+ 'hyphenate' => array(
454
+ 'id' => 'hyphenate',
455
+ 'name' => __( 'Hyphenate', 'easy-table-of-contents' ),
456
+ 'desc' => __( 'Use - rather than _ in anchors.', 'easy-table-of-contents' ),
457
+ 'type' => 'checkbox',
458
+ 'default' => false,
459
+ ),
460
+ 'include_homepage' => array(
461
+ 'id' => 'include_homepage',
462
+ 'name' => __( 'Homepage', 'easy-table-of-contents' ),
463
+ 'desc' => __( 'Show the table of contents for qualifying items on the homepage.', 'easy-table-of-contents' ),
464
+ 'type' => 'checkbox',
465
+ 'default' => false,
466
+ ),
467
+ 'exclude_css' => array(
468
+ 'id' => 'exclude_css',
469
+ 'name' => __( 'CSS', 'easy-table-of-contents' ),
470
+ 'desc' => __( "Prevent the loading the core CSS styles. When selected, the appearance options from above will be ignored.", 'easy-table-of-contents' ),
471
+ 'type' => 'checkbox',
472
+ 'default' => false,
473
+ ),
474
+ //'bullet_spacing' => array(
475
+ // 'id' => 'bullet_spacing',
476
+ // 'name' => __( 'Theme Bullets', 'easy-table-of-contents' ),
477
+ // 'desc' => __( 'If your theme includes background images for unordered list elements, enable this option to support them.', 'easy-table-of-contents' ),
478
+ // 'type' => 'checkbox',
479
+ // 'default' => false,
480
+ //),
481
+ 'heading_levels' => array(
482
+ 'id' => 'heading_levels',
483
+ 'name' => __( 'Headings:', 'easy-table-of-contents' ),
484
+ 'desc' => __( 'Select the heading to consider when generating the table of contents. Deselecting a heading will exclude it.', 'easy-table-of-contents' ),
485
+ 'type' => 'checkboxgroup',
486
+ 'options' => array(
487
+ '1' => __( 'Heading 1 (h1)', 'easy-table-of-contents' ),
488
+ '2' => __( 'Heading 2 (h2)', 'easy-table-of-contents' ),
489
+ '3' => __( 'Heading 3 (h3)', 'easy-table-of-contents' ),
490
+ '4' => __( 'Heading 4 (h4)', 'easy-table-of-contents' ),
491
+ '5' => __( 'Heading 5 (h5)', 'easy-table-of-contents' ),
492
+ '6' => __( 'Heading 6 (h6)', 'easy-table-of-contents' ),
493
+ ),
494
+ 'default' => array( '1', '2', '3', '4', '5', '6' ),
495
+ ),
496
+ 'exclude' => array(
497
+ 'id' => 'exclude',
498
+ 'name' => __( 'Exclude Headings', 'easy-table-of-contents' ),
499
+ 'desc' => __( 'Specify headings to be excluded from appearing in the table of contents. Separate multiple headings with a pipe <code>|</code>. Use an asterisk <code>*</code> as a wildcard to match other text.', 'easy-table-of-contents' ),
500
+ 'type' => 'text',
501
+ 'size' => 'large',
502
+ 'default' => '',
503
+ ),
504
+ 'exclude_desc' => array(
505
+ 'id' => 'exclude_desc',
506
+ 'name' => '',
507
+ 'desc' => '<p><strong>' . __( 'Examples:', 'easy-table-of-contents' ) . '</strong></p>' .
508
+ '<ul>' .
509
+ '<li>' . __( '<code>Fruit*</code> Ignore headings starting with "Fruit".', 'easy-table-of-contents' ) . '</li>' .
510
+ '<li>' . __( '<code>*Fruit Diet*</code> Ignore headings with "Fruit Diet" somewhere in the heading.', 'easy-table-of-contents' ) . '</li>' .
511
+ '<li>' . __( '<code>Apple Tree|Oranges|Yellow Bananas</code> Ignore headings that are exactly "Apple Tree", "Oranges" or "Yellow Bananas".', 'easy-table-of-contents' ) . '</li>' .
512
+ '</ul>' .
513
+ '<p>' . __( '<strong>Note:</strong> This is not case sensitive.', 'easy-table-of-contents' ) . '</p>',
514
+ 'type' => 'descriptive_text',
515
+ ),
516
+ 'smooth_scroll_offset' => array(
517
+ 'id' => 'smooth_scroll_offset',
518
+ 'name' => __( 'Smooth Scroll Offset', 'easy-table-of-contents' ),
519
+ 'desc' => 'px<br/>' . __( 'If you have a consistent menu across the top of your site, you can adjust the top offset to stop the headings from appearing underneath the top menu. A setting of 30 accommodates the WordPress admin bar. This setting only has an effect after you have enabled Smooth Scroll option.', 'easy-table-of-contents' ),
520
+ 'type' => 'number',
521
+ 'size' => 'small',
522
+ 'default' => 30
523
+ ),
524
+ 'mobile_smooth_scroll_offset' => array(
525
+ 'id' => 'mobile_smooth_scroll_offset',
526
+ 'name' => __( 'Mobile Smooth Scroll Offset', 'easy-table-of-contents' ),
527
+ 'desc' => 'px<br/>' . __( 'This provides the same function as the Smooth Scroll Offset option above but applied when the user is visiting your site on a mobile device.', 'easy-table-of-contents' ),
528
+ 'type' => 'number',
529
+ 'size' => 'small',
530
+ 'default' => 0
531
+ ),
532
+ 'restrict_path' => array(
533
+ 'id' => 'restrict_path',
534
+ 'name' => __( 'Limit Path', 'easy-table-of-contents' ),
535
+ 'desc' => '<br/>' . __( 'Restrict generation of the table of contents to pages that match the required path. This path is from the root of your site and always begins with a forward slash.', 'easy-table-of-contents' ) .
536
+ '<br/><span class="description">' . __( 'Eg: /wiki/, /corporate/annual-reports/', 'easy-table-of-contents' ) . '</span>',
537
+ 'type' => 'text',
538
+ ),
539
+ 'fragment_prefix' => array(
540
+ 'id' => 'fragment_prefix',
541
+ 'name' => __( 'Default Anchor Prefix', 'easy-table-of-contents' ),
542
+ 'desc' => '<br/>' . __( 'Anchor targets are restricted to alphanumeric characters as per HTML specification (see readme for more detail). The default anchor prefix will be used when no characters qualify. When left blank, a number will be used instead.', 'easy-table-of-contents' ) .
543
+ '<br/>' . __( 'This option normally applies to content written in character sets other than ASCII.', 'easy-table-of-contents' ) .
544
+ '<br/><span class="description">' . __( 'Eg: i, toc_index, index, _', 'easy-table-of-contents' ) . '</span>',
545
+ 'type' => 'text',
546
+ 'default' => 'i',
547
+ ),
548
+ 'widget_affix_selector' => array(
549
+ 'id' => 'widget_affix_selector',
550
+ 'name' => __( 'Widget Affix Selector', 'easy-table-of-contents' ),
551
+ 'desc' => '<br/>' . __( 'To enable the option to affix or pin the Table of Contents widget enter the theme\'s sidebar class or id.', 'easy-table-of-contents' ) .
552
+ '<br/>' . __( 'Since every theme is different, this can not be determined automatically. If you are unsure how to find the sidebar\'s class or id, please ask the theme\'s support persons.', 'easy-table-of-contents' ) .
553
+ '<br/><span class="description">' . __( 'Eg: .widget-area or #sidebar', 'easy-table-of-contents' ) . '</span>',
554
+ 'type' => 'text',
555
+ 'default' => '',
556
+ ),
557
+ )
558
+ ),
559
+ );
560
+
561
+ return apply_filters( 'ez_toc_registered_settings', $options );
562
+ }
563
+
564
+ /**
565
+ * The default values for the registered settings and options.
566
+ *
567
+ * @access private
568
+ * @since 1.0
569
+ * @static
570
+ *
571
+ * @return array
572
+ */
573
+ private static function getDefaults() {
574
+
575
+ $defaults = array(
576
+ 'fragment_prefix' => 'i',
577
+ 'position' => 'before',
578
+ 'start' => 4,
579
+ 'show_heading_text' => true,
580
+ 'heading_text' => 'Table of Contents',
581
+ 'enabled_post_types' => array( 'page' ),
582
+ 'auto_insert_post_types' => array(),
583
+ 'show_hierarchy' => true,
584
+ 'counter' => 'decimal',
585
+ 'smooth_scroll' => true,
586
+ 'smooth_scroll_offset' => 30,
587
+ 'mobile_smooth_scroll_offset' => 0,
588
+ 'visibility' => true,
589
+ 'toc_loading' => 'js',
590
+ //'visibility_show' => 'show',
591
+ //'visibility_hide' => 'hide',
592
+ 'visibility_hide_by_default' => false,
593
+ 'width' => 'auto',
594
+ 'width_custom' => 275,
595
+ 'width_custom_units' => 'px',
596
+ 'wrapping' => 'none',
597
+ 'title_font_size' => 120,
598
+ 'title_font_size_units' => '%',
599
+ 'title_font_weight' => 500,
600
+ 'font_size' => 95,
601
+ 'font_size_units' => '%',
602
+ 'theme' => 'grey',
603
+ 'custom_background_colour' => '#fff',
604
+ 'custom_border_colour' => '#ddd',
605
+ 'custom_title_colour' => '#999',
606
+ 'custom_link_colour' => '#428bca',
607
+ 'custom_link_hover_colour' => '#2a6496',
608
+ 'custom_link_visited_colour' => '#428bca',
609
+ 'lowercase' => false,
610
+ 'hyphenate' => false,
611
+ //'bullet_spacing' => false,
612
+ 'include_homepage' => false,
613
+ 'exclude_css' => false,
614
+ 'exclude' => '',
615
+ 'heading_levels' => array( '1', '2', '3', '4', '5', '6' ),
616
+ 'restrict_path' => '',
617
+ 'css_container_class' => '',
618
+ //'show_toc_in_widget_only' => false,
619
+ //'show_toc_in_widget_only_post_types' => array(),
620
+ 'widget_affix_selector' => '',
621
+ );
622
+
623
+ return apply_filters( 'ez_toc_get_default_options', $defaults );
624
+ }
625
+
626
+ /**
627
+ * Get the default options array.
628
+ *
629
+ * @access private
630
+ * @since 1.0
631
+ * @static
632
+ *
633
+ * @return array
634
+ */
635
+ private static function getOptions() {
636
+
637
+ $defaults = self::getDefaults();
638
+ $options = get_option( 'ez-toc-settings', $defaults );
639
+
640
+ //return apply_filters( 'ez_toc_get_options', wp_parse_args( $options, $defaults ) );
641
+ return apply_filters( 'ez_toc_get_options', $options );
642
+ }
643
+
644
+ /**
645
+ * Get option value by key name.
646
+ *
647
+ * @access public
648
+ * @since 1.0
649
+ * @static
650
+ *
651
+ * @param string $key
652
+ * @param bool|false $default
653
+ *
654
+ * @return mixed
655
+ */
656
+ public static function get( $key, $default = false ) {
657
+
658
+ $options = self::getOptions();
659
+
660
+ $value = array_key_exists( $key, $options ) ? $options[ $key ] : $default;
661
+ $value = apply_filters( 'ez_toc_get_option', $value, $key, $default );
662
+
663
+ return apply_filters( 'ez_toc_get_option_' . $key, $value, $key, $default );
664
+ }
665
+
666
+ /**
667
+ * Set an option value by key name.
668
+ *
669
+ * @access public
670
+ * @since 1.0
671
+ * @static
672
+ *
673
+ * @param string $key
674
+ * @param bool|false $value
675
+ *
676
+ * @return bool
677
+ */
678
+ public static function set( $key, $value = false ) {
679
+
680
+ if ( empty( $value ) ) {
681
+
682
+ $remove_option = self::delete( $key );
683
+
684
+ return $remove_option;
685
+ }
686
+
687
+ $options = self::getOptions();
688
+
689
+ $options[ $key ] = apply_filters( 'ez_toc_update_option', $value, $key );
690
+
691
+ return update_option( 'ez-toc-settings', $options );
692
+ }
693
+
694
+ /**
695
+ * Delete an option from the options table by option key name.
696
+ *
697
+ * @access public
698
+ * @since 1.0
699
+ * @static
700
+ *
701
+ * @param string $key
702
+ *
703
+ * @return bool
704
+ */
705
+ public static function delete( $key ) {
706
+
707
+ // First let's grab the current settings
708
+ $options = get_option( 'ez-toc-settings' );
709
+
710
+ // Next let's try to update the value
711
+ if ( array_key_exists( $key, $options ) ) {
712
+
713
+ unset( $options[ $key ] );
714
+ }
715
+
716
+ return update_option( 'ez-toc-settings', $options );
717
+ }
718
+
719
+ /**
720
+ * Sanitize a hex color from user input.
721
+ *
722
+ * Tries to convert $string into a valid hex colour.
723
+ * Returns $default if $string is not a hex value, otherwise returns verified hex.
724
+ *
725
+ * @access private
726
+ * @since 1.0
727
+ * @static
728
+ *
729
+ * @param string $string
730
+ * @param string $default
731
+ *
732
+ * @return mixed|string
733
+ */
734
+ private static function hex_value( $string = '', $default = '#' ) {
735
+
736
+ $return = $default;
737
+
738
+ if ( $string ) {
739
+ // strip out non hex chars
740
+ $return = preg_replace( '/[^a-fA-F0-9]*/', '', $string );
741
+
742
+ switch ( strlen( $return ) ) {
743
+ case 3: // do next
744
+ case 6:
745
+ $return = '#' . $return;
746
+ break;
747
+
748
+ default:
749
+ if ( strlen( $return ) > 6 ) {
750
+ $return = '#' . substr( $return, 0, 6 );
751
+ } // if > 6 chars, then take the first 6
752
+ elseif ( strlen( $return ) > 3 && strlen( $return ) < 6 ) {
753
+ $return = '#' . substr( $return, 0, 3 );
754
+ } // if between 3 and 6, then take first 3
755
+ else {
756
+ $return = $default;
757
+ } // not valid, return $default
758
+ }
759
+ }
760
+
761
+ return $return;
762
+ }
763
+
764
+ /**
765
+ * Get the registered post types minus excluded core types.
766
+ *
767
+ * @access public
768
+ * @since 1.0
769
+ * @static
770
+ *
771
+ * @return array
772
+ */
773
+ public static function getPostTypes() {
774
+
775
+ $exclude = apply_filters( 'ez_toc_exclude_post_types', array( 'attachment', 'revision', 'nav_menu_item', 'safecss' ) );
776
+ $registered = get_post_types( array(), 'objects' );
777
+ $types = array();
778
+
779
+ foreach ( $registered as $post ) {
780
+
781
+ if ( in_array( $post->name, $exclude ) ) {
782
+
783
+ continue;
784
+ }
785
+
786
+ $types[ $post->name ] = $post->label;
787
+ }
788
+
789
+ return $types;
790
+ }
791
+
792
+ /**
793
+ * Missing Callback
794
+ *
795
+ * If a settings field type callback is not callable, alert the user.
796
+ *
797
+ * @access public
798
+ * @since 1.0
799
+ * @static
800
+ *
801
+ * @param array $args Arguments passed by the setting
802
+ */
803
+ public static function missingCallback( $args ) {
804
+
805
+ printf(
806
+ __( 'The callback function used for the <strong>%s</strong> setting is missing.', 'easy-table-of-contents' ),
807
+ $args['id']
808
+ );
809
+ }
810
+
811
+ /**
812
+ * Text Callback
813
+ *
814
+ * Renders text fields.
815
+ *
816
+ * @access public
817
+ * @since 1.0
818
+ * @static
819
+ *
820
+ * @param array $args Arguments passed by the setting
821
+ * @param null $value
822
+ */
823
+ public static function text( $args, $value = null ) {
824
+
825
+ if ( is_null( $value ) ) {
826
+
827
+ $value = self::get( $args['id'], $args['default'] );
828
+ }
829
+
830
+ if ( isset( $args['faux'] ) && true === $args['faux'] ) {
831
+
832
+ $args['readonly'] = true;
833
+ $value = isset( $args['default'] ) ? $args['default'] : '';
834
+ $name = '';
835
+
836
+ } else {
837
+
838
+ $name = ' name="ez-toc-settings[' . $args['id'] . ']"';
839
+ }
840
+
841
+ $readonly = isset( $args['readonly'] ) && $args['readonly'] === true ? ' readonly="readonly"' : '';
842
+ $size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
843
+
844
+ $html = '<input type="text" class="' . $size . '-text" id="ez-toc-settings[' . $args['id'] . ']"' . $name . ' value="' . esc_attr( stripslashes( $value ) ) . '"' . $readonly . '/>';
845
+
846
+ if ( 0 < strlen( $args['desc'] ) ) {
847
+
848
+ $html .= '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
849
+ }
850
+
851
+ echo $html;
852
+ }
853
+
854
+ /**
855
+ * Textarea Callback.
856
+ *
857
+ * Renders a textarea.
858
+ *
859
+ * @access public
860
+ * @since 1.1
861
+ * @static
862
+ *
863
+ * @param array $args Arguments passed by the setting
864
+ * @param null $value
865
+ */
866
+ public static function textarea( $args, $value = null ) {
867
+
868
+ $html = '';
869
+
870
+ if ( is_null( $value ) ) {
871
+
872
+ $value = self::get( $args['id'], $args['default'] );
873
+ }
874
+
875
+ if ( isset( $args['faux'] ) && true === $args['faux'] ) {
876
+
877
+ $args['readonly'] = true;
878
+ $value = isset( $args['default'] ) ? $args['default'] : '';
879
+ $name = '';
880
+
881
+ } else {
882
+
883
+ $name = ' name="ez-toc-settings[' . $args['id'] . ']"';
884
+ }
885
+
886
+ $readonly = isset( $args['readonly'] ) && $args['readonly'] === true ? ' readonly="readonly"' : '';
887
+ $size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
888
+
889
+ if ( 0 < strlen( $args['desc'] ) ) {
890
+
891
+ $html .= '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
892
+ }
893
+
894
+ $html .= '<textarea rows="10" cols="50" class="' . $size . '-text" id="ez-toc-settings[' . $args['id'] . ']"' . $name . $readonly . '/>' . esc_textarea( $value ) . '</textarea>';
895
+
896
+ echo $html;
897
+ }
898
+
899
+ /**
900
+ * Number Callback
901
+ *
902
+ * Renders number fields.
903
+ *
904
+ * @access public
905
+ * @since 1.0
906
+ * @static
907
+ *
908
+ * @param array $args Arguments passed by the setting
909
+ */
910
+ public static function number( $args ) {
911
+
912
+ $value = self::get( $args['id'], $args['default'] );
913
+
914
+ if ( isset( $args['faux'] ) && true === $args['faux'] ) {
915
+
916
+ $args['readonly'] = true;
917
+ $value = isset( $args['default'] ) ? $args['default'] : '';
918
+ $name = '';
919
+
920
+ } else {
921
+
922
+ $name = ' name="ez-toc-settings[' . $args['id'] . ']"';
923
+ }
924
+
925
+ $readonly = isset( $args['readonly'] ) && $args['readonly'] === true ? ' readonly="readonly"' : '';
926
+ $size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
927
+
928
+ $html = '<input type="number" class="' . $size . '-text" id="ez-toc-settings[' . $args['id'] . ']"' . $name . ' value="' . esc_attr( stripslashes( $value ) ) . '"' . $readonly . '/>';
929
+
930
+ if ( 0 < strlen( $args['desc'] ) ) {
931
+
932
+ $html .= '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
933
+ }
934
+
935
+ echo $html;
936
+ }
937
+
938
+ /**
939
+ * Checkbox Callback
940
+ *
941
+ * Renders checkboxes.
942
+ *
943
+ * @access public
944
+ * @since 1.0
945
+ * @static
946
+ *
947
+ * @param array $args Arguments passed by the setting
948
+ * @param null $value
949
+ */
950
+ public static function checkbox( $args, $value = null ) {
951
+
952
+ if ( is_null( $value ) ) {
953
+
954
+ $value = self::get( $args['id'], $args['default'] );
955
+ }
956
+
957
+ if ( isset( $args['faux'] ) && true === $args['faux'] ) {
958
+
959
+ $name = '';
960
+
961
+ } else {
962
+
963
+ $name = ' name="ez-toc-settings[' . $args['id'] . ']"';
964
+ }
965
+
966
+ $checked = $value ? checked( 1, $value, false ) : '';
967
+
968
+ $html = '<input type="checkbox" id="ez-toc-settings[' . $args['id'] . ']"' . $name . ' value="1" ' . $checked . '/>';
969
+
970
+ if ( 0 < strlen( $args['desc'] ) ) {
971
+
972
+ $html .= '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
973
+ }
974
+
975
+ echo $html;
976
+ }
977
+
978
+ /**
979
+ * Multicheck Callback
980
+ *
981
+ * Renders multiple checkboxes.
982
+ *
983
+ * @access public
984
+ * @since 1.0
985
+ * @static
986
+ *
987
+ * @param array $args Arguments passed by the setting
988
+ * @param null $value
989
+ */
990
+ public static function checkboxgroup( $args, $value = null ) {
991
+
992
+ if ( is_null( $value ) ) {
993
+
994
+ $value = self::get( $args['id'], $args['default'] );
995
+ }
996
+
997
+ if ( ! empty( $args['options'] ) ) {
998
+
999
+ foreach ( $args['options'] as $key => $option ):
1000
+
1001
+ if ( in_array( $key, $value ) ) {
1002
+
1003
+ $enabled = $option;
1004
+
1005
+ } else {
1006
+
1007
+ $enabled = null;
1008
+ }
1009
+
1010
+ echo '<input name="ez-toc-settings[' . $args['id'] . '][' . $key . ']" id="ez-toc-settings[' . $args['id'] . '][' . $key . ']" type="checkbox" value="' . $key . '" ' . checked( $option, $enabled, false ) . '/>&nbsp;';
1011
+ echo '<label for="ez-toc-settings[' . $args['id'] . '][' . $key . ']">' . $option . '</label><br/>';
1012
+
1013
+ endforeach;
1014
+
1015
+ if ( 0 < strlen( $args['desc'] ) ) {
1016
+
1017
+ echo '<p class="description">' . $args['desc'] . '</p>';
1018
+ }
1019
+ }
1020
+ }
1021
+
1022
+ /**
1023
+ * Radio Callback
1024
+ *
1025
+ * Renders radio groups.
1026
+ *
1027
+ * @access public
1028
+ * @since 1.0
1029
+ * @static
1030
+ *
1031
+ * @param array $args Arguments passed by the setting
1032
+ */
1033
+ public static function radio( $args ) {
1034
+
1035
+ $value = self::get( $args['id'], $args['default'] );
1036
+
1037
+ foreach ( $args['options'] as $key => $option ) {
1038
+
1039
+ echo '<input name="ez-toc-settings[' . $args['id'] . ']"" id="ez-toc-settings[' . $args['id'] . '][' . $key . ']" type="radio" value="' . $key . '" ' . checked( $key, $value, false ) . '/>&nbsp;';
1040
+ echo '<label for="ez-toc-settings[' . $args['id'] . '][' . $key . ']">' . $option . '</label><br/>';
1041
+ }
1042
+
1043
+ if ( 0 < strlen( $args['desc'] ) ) {
1044
+
1045
+ echo '<p class="description">' . $args['desc'] . '</p>';
1046
+ }
1047
+ }
1048
+
1049
+ /**
1050
+ * Select Callback
1051
+ *
1052
+ * Renders select fields.
1053
+ *
1054
+ * @access public
1055
+ * @since 1.0
1056
+ * @static
1057
+ *
1058
+ * @param array $args Arguments passed by the setting.
1059
+ */
1060
+ public static function select( $args ) {
1061
+
1062
+ $value = self::get( $args['id'], $args['default'] );
1063
+
1064
+ if ( isset( $args['placeholder'] ) ) {
1065
+ $placeholder = $args['placeholder'];
1066
+ } else {
1067
+ $placeholder = '';
1068
+ }
1069
+
1070
+ if ( isset( $args['chosen'] ) ) {
1071
+ $chosen = 'class="enhanced"';
1072
+ } else {
1073
+ $chosen = '';
1074
+ }
1075
+
1076
+ $html = '<select id="ez-toc-settings[' . $args['id'] . ']" name="ez-toc-settings[' . $args['id'] . ']" ' . $chosen . 'data-placeholder="' . $placeholder . '" />';
1077
+
1078
+ foreach ( $args['options'] as $option => $name ) {
1079
+ $selected = selected( $option, $value, false );
1080
+ $html .= '<option value="' . $option . '" ' . $selected . '>' . $name . '</option>';
1081
+ }
1082
+
1083
+ $html .= '</select>';
1084
+
1085
+ if ( 0 < strlen( $args['desc'] ) ) {
1086
+
1087
+ $html .= '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
1088
+ }
1089
+
1090
+ echo $html;
1091
+ }
1092
+
1093
+ /**
1094
+ * Select Drop Down Callback
1095
+ *
1096
+ * Renders select with option group fields.
1097
+ *
1098
+ * @access public
1099
+ * @since 1.0
1100
+ * @static
1101
+ *
1102
+ * @param array $args Arguments passed by the setting.
1103
+ */
1104
+ public static function selectgroup( $args ) {
1105
+
1106
+ $value = self::get( $args['id'], $args['default'] );
1107
+
1108
+ if ( isset( $args['placeholder'] ) ) {
1109
+ $placeholder = $args['placeholder'];
1110
+ } else {
1111
+ $placeholder = '';
1112
+ }
1113
+
1114
+ if ( isset( $args['chosen'] ) ) {
1115
+ $chosen = 'class="enhanced"';
1116
+ } else {
1117
+ $chosen = '';
1118
+ }
1119
+
1120
+ $html = '<select id="ez-toc-settings[' . $args['id'] . ']" name="ez-toc-settings[' . $args['id'] . ']" ' . $chosen . 'data-placeholder="' . $placeholder . '" />';
1121
+
1122
+ foreach ( $args['options'] as $group ) {
1123
+
1124
+ $html .= sprintf( '<optgroup label="%1$s">', $group['name'] );
1125
+
1126
+ foreach ( $group['options'] as $option => $name ) {
1127
+
1128
+ $selected = selected( $option, $value, false );
1129
+ $html .= '<option value="' . $option . '" ' . $selected . '>' . $name . '</option>';
1130
+ }
1131
+
1132
+ $html .= '</optgroup>';
1133
+ }
1134
+
1135
+ $html .= '</select>';
1136
+
1137
+ if ( 0 < strlen( $args['desc'] ) ) {
1138
+
1139
+ $html .= '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
1140
+ }
1141
+
1142
+ echo $html;
1143
+ }
1144
+
1145
+ /**
1146
+ * Header Callback
1147
+ *
1148
+ * Renders the header.
1149
+ *
1150
+ * @access public
1151
+ * @since 1.0
1152
+ * @static
1153
+ *
1154
+ * @param array $args Arguments passed by the setting
1155
+ */
1156
+ public static function header( $args ) {
1157
+
1158
+ echo '<hr/>';
1159
+
1160
+ if ( 0 < strlen( $args['desc'] ) ) {
1161
+
1162
+ echo '<p>' . wp_kses_post( $args['desc'] ) . '</p>';
1163
+ }
1164
+ }
1165
+
1166
+ /**
1167
+ * Descriptive text callback.
1168
+ *
1169
+ * Renders descriptive text onto the settings field.
1170
+ *
1171
+ * @access public
1172
+ * @since 1.0
1173
+ * @static
1174
+ *
1175
+ * @param array $args Arguments passed by the setting
1176
+ */
1177
+ public static function descriptive_text( $args ) {
1178
+
1179
+ echo wp_kses_post( $args['desc'] );
1180
+ }
1181
+
1182
+ /**
1183
+ * Color picker Callback
1184
+ *
1185
+ * Renders color picker fields.
1186
+ *
1187
+ * @access public
1188
+ * @since 1.0
1189
+ * @static
1190
+ *
1191
+ * @param array $args Arguments passed by the setting
1192
+ */
1193
+ public static function color( $args ) {
1194
+
1195
+ $value = self::get( $args['id'], $args['default'] );
1196
+
1197
+ $default = isset( $args['default'] ) ? $args['default'] : '';
1198
+
1199
+ $html = '<input type="text" class="ez-toc-color-picker" id="ez-toc-settings[' . $args['id'] . ']" name="ez-toc-settings[' . $args['id'] . ']" value="' . esc_attr( $value ) . '" data-default-color="' . esc_attr( $default ) . '" />';
1200
+
1201
+ if ( 0 < strlen( $args['desc'] ) ) {
1202
+
1203
+ echo '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
1204
+ }
1205
+
1206
+ echo $html;
1207
+ }
1208
+
1209
+ /**
1210
+ * Custom table of contents width.
1211
+ *
1212
+ * @access public
1213
+ * @since 1.0
1214
+ * @static
1215
+ *
1216
+ * @param array $args
1217
+ */
1218
+ public static function custom_width( $args ) {
1219
+
1220
+ //$value = self::get( $args['id'], $args['default'] );
1221
+
1222
+ self::text(
1223
+ array(
1224
+ 'id' => $args['id'],
1225
+ 'desc' => '',
1226
+ 'size' => 'small',
1227
+ 'default' => $args['default'],
1228
+ )
1229
+ );
1230
+
1231
+ self::select(
1232
+ array(
1233
+ 'id' => $args['id'] . '_units',
1234
+ 'desc' => '',
1235
+ 'options' => array(
1236
+ 'px' => 'px',
1237
+ '%' => '%',
1238
+ 'em' => 'em',
1239
+ ),
1240
+ 'default' => 'px',
1241
+ )
1242
+ );
1243
+
1244
+ if ( 0 < strlen( $args['desc'] ) ) {
1245
+
1246
+ echo '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
1247
+ }
1248
+ }
1249
+
1250
+ /**
1251
+ * Custom font size callback.
1252
+ *
1253
+ * @access public
1254
+ * @since 1.0
1255
+ * @static
1256
+ *
1257
+ * @param array $args
1258
+ */
1259
+ public static function font_size( $args ) {
1260
+
1261
+ //$value = self::get( $args['id'], $args['default'] );
1262
+
1263
+ self::text(
1264
+ array(
1265
+ 'id' => $args['id'],
1266
+ 'desc' => '',
1267
+ 'size' => 'small',
1268
+ 'default' => $args['default'],
1269
+ )
1270
+ );
1271
+
1272
+ self::select(
1273
+ array(
1274
+ 'id' => $args['id'] . '_units',
1275
+ 'desc' => '',
1276
+ 'options' => array(
1277
+ 'pt' => 'pt',
1278
+ 'px' => 'px',
1279
+ '%' => '%',
1280
+ 'em' => 'em',
1281
+ ),
1282
+ 'default' => '%',
1283
+ )
1284
+ );
1285
+
1286
+ if ( 0 < strlen( $args['desc'] ) ) {
1287
+
1288
+ echo '<label for="ez-toc-settings[' . $args['id'] . ']"> ' . $args['desc'] . '</label>';
1289
+ }
1290
+ }
1291
+ }
1292
+
1293
+ add_action( 'admin_init', array( 'ezTOC_Option', 'register' ) );
1294
+ }
includes/class.post.php CHANGED
@@ -1,1405 +1,1415 @@
1
- <?php
2
-
3
- use function Easy_Plugins\Table_Of_Contents\String\br2;
4
-
5
- class ezTOC_Post {
6
-
7
- /**
8
- * @since 2.0
9
- * @var int
10
- */
11
- private $queriedObjectID;
12
-
13
- /**
14
- * @since 2.0
15
- * @var WP_Post
16
- */
17
- private $post;
18
-
19
- /**
20
- * @since 2.0
21
- * @var false|string
22
- */
23
- private $permalink;
24
-
25
- /**
26
- * The post content broken into pages by user inserting `<!--nextpage-->` into the post content.
27
- * @see ezTOC_Post::extractPages()
28
- * @since 2.0
29
- * @var array
30
- */
31
- private $pages = array();
32
-
33
- /**
34
- * The user defined heading levels to be included in the TOC.
35
- * @see ezTOC_Post::getHeadingLevels()
36
- * @since 2.0
37
- * @var array
38
- */
39
- private $headingLevels = array();
40
-
41
- /**
42
- * Array of nodes that are excluded by class/id selector.
43
- * @since 2.0
44
- * @var string[]
45
- */
46
- private $excludedNodes = array();
47
-
48
- /**
49
- * Keeps a track of used anchors for collision detecting.
50
- * @see ezTOC_Post::generateHeadingIDFromTitle()
51
- * @since 2.0
52
- * @var array
53
- */
54
- private $collision_collector = array();
55
-
56
- /**
57
- * @var bool
58
- */
59
- private $hasTOCItems = false;
60
-
61
- /**
62
- * ezTOC_Post constructor.
63
- *
64
- * @since 2.0
65
- *
66
- * @param WP_Post $post
67
- * @param bool $apply_content_filter Whether or not to apply the `the_content` filter on the post content.
68
- */
69
- public function __construct( WP_Post $post, $apply_content_filter = true ) {
70
-
71
- $this->post = $post;
72
- $this->permalink = get_permalink( $post );
73
- $this->queriedObjectID = get_queried_object_id();
74
-
75
- if ( $apply_content_filter ) {
76
-
77
- $this->applyContentFilter()->process();
78
-
79
- } else {
80
-
81
- $this->process();
82
- }
83
- }
84
-
85
- /**
86
- * @access public
87
- * @since 2.0
88
- *
89
- * @param $id
90
- *
91
- * @return ezTOC_Post|null
92
- */
93
- public static function get( $id ) {
94
-
95
- $post = get_post( $id );
96
-
97
- if ( ! $post instanceof WP_Post ) {
98
-
99
- return null;
100
- }
101
-
102
- return new static( $post );
103
- }
104
-
105
- /**
106
- * Process post content for headings.
107
- *
108
- * This must be run after object init or after @see ezTOC_Post::applyContentFilter().
109
- *
110
- * @since 2.0
111
- *
112
- * @return static
113
- */
114
- private function process() {
115
-
116
- $this->processPages();
117
-
118
- return $this;
119
- }
120
-
121
- /**
122
- * Apply `the_content` filter to the post content.
123
- *
124
- * @since 2.0
125
- *
126
- * @return static
127
- */
128
- private function applyContentFilter() {
129
-
130
- add_filter( 'strip_shortcodes_tagnames', array( __CLASS__, 'stripShortcodes' ), 10, 2 );
131
-
132
- /*
133
- * Ensure the ezTOC content filter is not applied when running `the_content` filter.
134
- */
135
- remove_filter( 'the_content', array( 'ezTOC', 'the_content' ), 100 );
136
-
137
- $this->post->post_content = apply_filters( 'the_content', strip_shortcodes( $this->post->post_content ) );
138
-
139
- add_filter( 'the_content', array( 'ezTOC', 'the_content' ), 100 );
140
-
141
- remove_filter( 'strip_shortcodes_tagnames', array( __CLASS__, 'stripShortcodes' ) );
142
-
143
- return $this;
144
- }
145
-
146
- /**
147
- * Callback for the `strip_shortcodes_tagnames` filter.
148
- *
149
- * Strip the shortcodes so their content is no processed for headings.
150
- *
151
- * @see ezTOC_Post::applyContentFilter()
152
- *
153
- * @since 2.0
154
- *
155
- * @param array $tags_to_remove Array of shortcode tags to remove.
156
- * @param string $content Content shortcodes are being removed from.
157
- *
158
- * @return array
159
- */
160
- public static function stripShortcodes( $tags_to_remove, $content ) {
161
-
162
- //error_log( var_export( $tags_to_remove, true ) );
163
-
164
- /*
165
- * Ensure the ezTOC shortcodes are not processed when applying `the_content` filter
166
- * otherwise an infinite loop may occur.
167
- */
168
- $tags_to_remove = apply_filters(
169
- 'ez_toc_strip_shortcodes_tagnames',
170
- array(
171
- 'ez-toc',
172
- apply_filters( 'ez_toc_shortcode', 'toc' ),
173
- ),
174
- $content
175
- );
176
-
177
- //error_log( var_export( $tags_to_remove, true ) );
178
-
179
- return $tags_to_remove;
180
- }
181
-
182
- /**
183
- * This is a work around for theme's and plugins
184
- * which break the WordPress global $wp_query var by unsetting it
185
- * or overwriting it which breaks the method call
186
- * that `get_query_var()` uses to return the query variable.
187
- *
188
- * @access protected
189
- * @since 2.0
190
- *
191
- * @return int
192
- */
193
- protected function getCurrentPage() {
194
-
195
- global $wp_query;
196
-
197
- // Check to see if the global `$wp_query` var is an instance of WP_Query and that the get() method is callable.
198
- // If it is then when can simply use the get_query_var() function.
199
- if ( $wp_query instanceof WP_Query && is_callable( array( $wp_query, 'get' ) ) ) {
200
-
201
- $page = get_query_var( 'page', 1 );
202
-
203
- return 1 > $page ? 1 : $page;
204
-
205
- // 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.
206
- } elseif ( isset( $GLOBALS['wp_query']->query_vars[ 'page' ] ) ) {
207
-
208
- return $GLOBALS['wp_query']->query_vars[ 'page' ];
209
-
210
- // We should not reach this, but if we do, lets check the original parsed query vars in $GLOBALS['wp_the_query']->query_vars.
211
- } elseif ( isset( $GLOBALS['wp_the_query']->query_vars[ 'page' ] ) ) {
212
-
213
- return $GLOBALS['wp_the_query']->query_vars[ 'page' ];
214
-
215
- // Ok, if all else fails, check the $_REQUEST super global.
216
- } elseif ( isset( $_REQUEST[ 'page' ] ) ) {
217
-
218
- return $_REQUEST[ 'page' ];
219
- }
220
-
221
- // Finally, return the $default if it was supplied.
222
- return 1;
223
- }
224
-
225
- /**
226
- * Get the number of page the post has.
227
- *
228
- * @access protected
229
- * @since 2.0
230
- *
231
- * @return int
232
- */
233
- protected function getNumberOfPages() {
234
-
235
- return count( $this->pages );
236
- }
237
-
238
- /**
239
- * Whether or not the post has multiple pages.
240
- *
241
- * @access protected
242
- * @since 2.0
243
- *
244
- * @return bool
245
- */
246
- protected function isMultipage() {
247
-
248
- return 1 < $this->getNumberOfPages();
249
- }
250
-
251
- /**
252
- * Parse the post content and headings.
253
- *
254
- * @access private
255
- * @since 2.0
256
- */
257
- private function processPages() {
258
-
259
- //if ( ! class_exists( 'TagFilter' ) ) {
260
- //
261
- // require_once( EZ_TOC_PATH . '/includes/vendor/ultimate-web-scraper/tag_filter.php' );
262
- //}
263
-
264
- $split = preg_split( '/<!--nextpage-->/msuU', $this->post->post_content );
265
- $pages = array();
266
-
267
- if ( is_array( $split ) ) {
268
-
269
- $page = 1;
270
-
271
- //$tagFilterOptions = TagFilter::GetHTMLOptions();
272
-
273
- //// Set custom TagFilter options.
274
- //$tagFilterOptions['charset'] = get_option( 'blog_charset' );
275
- ////$tagFilterOptions['output_mode'] = 'xml';
276
-
277
- foreach ( $split as $content ) {
278
-
279
- //$html = TagFilter::Explode( $content, $tagFilterOptions );
280
- //
281
- ///**
282
- // * @since 2.0
283
- // *
284
- // * @param $selectors array Array of classes/id selector to exclude from TOC.
285
- // * @param $content string Post content.
286
- // */
287
- //$selectors = apply_filters( 'ez_toc_exclude_by_selector', array(), $content );
288
- //
289
- //$nodes = $html->Find( implode( ',', $selectors ) );
290
- //
291
- //foreach ( $nodes['ids'] as $id ) {
292
- //
293
- // $html->Remove( $id );
294
- //}
295
- //
296
- //$eligibleContent = $html->Implode( 0, $tagFilterOptions );
297
- //
298
- ///**
299
- // * TagFilter::Implode() writes br tags as `<br>` while WP normalizes to `<br />`.
300
- // * Normalize `$eligibleContent` to match WP.
301
- // *
302
- // * @see wpautop()
303
- // */
304
- ////$eligibleContent = str_replace( array( '<br>', '<br/>' ), array( '<br />' ), $eligibleContent );
305
- //$eligibleContent = \Easy_Plugins\Table_Of_Contents\String\force_balance_tags( $eligibleContent );
306
-
307
- $this->extractExcludedNodes( $page, $content );
308
-
309
- $pages[ $page ] = array(
310
- 'headings' => $this->extractHeadings( $content ),
311
- 'content' => $content,
312
- );
313
-
314
- $page++;
315
- }
316
-
317
- }
318
-
319
- $this->pages = $pages;
320
- }
321
-
322
- /**
323
- * Get the post's parse content and headings.
324
- *
325
- * @access public
326
- * @since 2.0
327
- *
328
- * @return array
329
- */
330
- public function getPages() {
331
-
332
- return $this->pages;
333
- }
334
-
335
- /**
336
- * Extract nodes that heading are to be excluded.
337
- *
338
- * @since 2.0
339
- *
340
- * @param int $page
341
- * @param string $content
342
- */
343
- private function extractExcludedNodes( $page, $content ) {
344
-
345
- if ( ! class_exists( 'TagFilter' ) ) {
346
-
347
- require_once( EZ_TOC_PATH . '/includes/vendor/ultimate-web-scraper/tag_filter.php' );
348
- }
349
-
350
- $tagFilterOptions = TagFilter::GetHTMLOptions();
351
-
352
- // Set custom TagFilter options.
353
- $tagFilterOptions['charset'] = get_option( 'blog_charset' );
354
- //$tagFilterOptions['output_mode'] = 'xml';
355
-
356
- $html = TagFilter::Explode( $content, $tagFilterOptions );
357
-
358
- /**
359
- * @since 2.0
360
- *
361
- * @param $selectors array Array of classes/id selector to exclude from TOC.
362
- * @param $content string Post content.
363
- */
364
- $selectors = apply_filters( 'ez_toc_exclude_by_selector', array( '.ez-toc-exclude-headings' ), $content );
365
-
366
- $nodes = $html->Find( implode( ',', $selectors ) );
367
-
368
- foreach ( $nodes['ids'] as $id ) {
369
-
370
- //$this->excludedNodes[ $page ][ $id ] = $html->Implode( $id, $tagFilterOptions );
371
- array_push( $this->excludedNodes, $html->Implode( $id, $tagFilterOptions ) );
372
- }
373
-
374
- //$eligibleContent = $html->Implode( 0, $tagFilterOptions );
375
-
376
- /**
377
- * TagFilter::Implode() writes br tags as `<br>` while WP normalizes to `<br />`.
378
- * Normalize `$eligibleContent` to match WP.
379
- *
380
- * @see wpautop()
381
- */
382
- //$eligibleContent = \Easy_Plugins\Table_Of_Contents\String\force_balance_tags( $eligibleContent );
383
- }
384
-
385
- /**
386
- * Extract the posts content for headings.
387
- *
388
- * @access private
389
- * @since 2.0
390
- *
391
- * @param string $content
392
- *
393
- * @return array
394
- */
395
- private function extractHeadings( $content ) {
396
-
397
- $matches = array();
398
-
399
- // reset the internal collision collection as the_content may have been triggered elsewhere
400
- // eg by themes or other plugins that need to read in content such as metadata fields in
401
- // the head html tag, or to provide descriptions to twitter/facebook
402
- /** @todo does this need to be used??? */
403
- //self::$collision_collector = array();
404
-
405
- $content = apply_filters( 'ez_toc_extract_headings_content', wptexturize( $content ) );
406
-
407
- // get all headings
408
- // the html spec allows for a maximum of 6 heading depths
409
- if ( preg_match_all( '/(<h([1-6]{1})[^>]*>)(.*)<\/h\2>/msuU', $content, $matches, PREG_SET_ORDER ) ) {
410
-
411
- $minimum = absint( ezTOC_Option::get( 'start' ) );
412
-
413
- $this->removeHeadingsFromExcludedNodes( $matches );
414
- $this->removeHeadings( $matches );
415
- $this->excludeHeadings( $matches );
416
- $this->removeEmptyHeadings( $matches );
417
-
418
- if ( count( $matches ) >= $minimum ) {
419
-
420
- $this->alternateHeadings( $matches );
421
- $this->headingIDs( $matches );
422
- $this->hasTOCItems = true;
423
-
424
- } else {
425
-
426
- return array();
427
- }
428
-
429
- }
430
-
431
- return array_values( $matches ); // Rest the array index.
432
- }
433
-
434
- /**
435
- * Whether or not the string is in one of the excluded nodes.
436
- *
437
- * @since 2.0
438
- *
439
- * @param string $string
440
- *
441
- * @return bool
442
- */
443
- private function inExcludedNode( $string ) {
444
-
445
- foreach ( $this->excludedNodes as $node ) {
446
-
447
- if ( empty( $node ) || empty( $string ) ) {
448
-
449
- return false;
450
- }
451
-
452
- if ( false !== strpos( $node, $string ) ) {
453
-
454
- return true;
455
- }
456
- }
457
-
458
- return false;
459
- }
460
-
461
- /**
462
- * Remove headings that are in excluded nodes.
463
- *
464
- * @since 2.0
465
- *
466
- * @param array $matches
467
- *
468
- * @return array
469
- */
470
- private function removeHeadingsFromExcludedNodes( &$matches ) {
471
-
472
- foreach ( $matches as $i => $match ) {
473
-
474
- if ( $this->inExcludedNode( "{$match[3]}</h$match[2]>" ) ) {
475
-
476
- unset( $matches[ $i ] );
477
- }
478
- }
479
-
480
- return $matches;
481
- }
482
-
483
- /**
484
- * Get the heading levels to be included in the TOC.
485
- *
486
- * @access private
487
- * @since 2.0
488
- *
489
- * @return array
490
- */
491
- private function getHeadingLevels() {
492
-
493
- $levels = get_post_meta( $this->post->ID, '_ez-toc-heading-levels', true );
494
-
495
- if ( ! is_array( $levels ) ) {
496
-
497
- $levels = array();
498
- }
499
-
500
- if ( empty( $levels ) ) {
501
-
502
- $levels = ezTOC_Option::get( 'heading_levels', array() );
503
- }
504
-
505
- $this->headingLevels = $levels;
506
-
507
- return $this->headingLevels;
508
- }
509
-
510
- /**
511
- * Remove the heading levels as defined by user settings from the TOC heading matches.
512
- *
513
- * @see ezTOC_Post::extractHeadings()
514
- *
515
- * @access private
516
- * @since 2.0
517
- *
518
- * @param array $matches The heading from the post content extracted with preg_match_all().
519
- *
520
- * @return array
521
- */
522
- private function removeHeadings( &$matches ) {
523
-
524
- $levels = $this->getHeadingLevels();
525
-
526
- if ( count( $levels ) != 6 ) {
527
-
528
- $new_matches = array();
529
- //$count = count( $matches );
530
-
531
- //for ( $i = 0; $i < $count; $i++ ) {
532
- foreach ( $matches as $i => $match ) {
533
-
534
- if ( in_array( $matches[ $i ][2], $levels ) ) {
535
-
536
- $new_matches[ $i ] = $matches[ $i ];
537
- }
538
- }
539
-
540
- $matches = $new_matches;
541
- }
542
-
543
- return $matches;
544
- }
545
-
546
- /**
547
- * Exclude the heading, by title, as defined by the user settings from the TOC matches.
548
- *
549
- * @see ezTOC_Post::extractHeadings()
550
- *
551
- * @access private
552
- * @since 2.0
553
- *
554
- * @param array $matches The headings from the post content extracted with preg_match_all().
555
- *
556
- * @return array
557
- */
558
- private function excludeHeadings( &$matches ) {
559
-
560
- $exclude = get_post_meta( $this->post->ID, '_ez-toc-exclude', true );
561
-
562
- if ( empty( $exclude ) ) {
563
-
564
- $exclude = ezTOC_Option::get( 'exclude' );
565
- }
566
-
567
- if ( $exclude ) {
568
-
569
- $excluded_headings = explode( '|', $exclude );
570
- $excluded_count = count( $excluded_headings );
571
-
572
- if ( $excluded_count > 0 ) {
573
-
574
- for ( $j = 0; $j < $excluded_count; $j++ ) {
575
-
576
- $excluded_headings[ $j ] = preg_quote( $excluded_headings[ $j ] );
577
-
578
- // escape some regular expression characters
579
- // others: http://www.php.net/manual/en/regexp.reference.meta.php
580
- $excluded_headings[ $j ] = str_replace(
581
- array( '\*', '/', '%' ),
582
- array( '.*', '\/', '\%' ),
583
- trim( $excluded_headings[ $j ] )
584
- );
585
- }
586
-
587
- $new_matches = array();
588
- //$count = count( $matches );
589
-
590
- //for ( $i = 0; $i < $count; $i++ ) {
591
- foreach ( $matches as $i => $match ) {
592
-
593
- $found = false;
594
-
595
- $against = html_entity_decode(
596
- wptexturize( strip_tags( str_replace( array( "\r", "\n" ), ' ', $matches[ $i ][0] ) ) ),
597
- ENT_NOQUOTES,
598
- get_option( 'blog_charset' )
599
- );
600
-
601
- for ( $j = 0; $j < $excluded_count; $j++ ) {
602
-
603
- // Since WP manipulates the post content it is required that the excluded header and
604
- // the actual header be manipulated similarly so a match can be made.
605
- $pattern = html_entity_decode(
606
- wptexturize( $excluded_headings[ $j ] ),
607
- ENT_NOQUOTES,
608
- get_option( 'blog_charset' )
609
- );
610
-
611
- if ( @preg_match( '/^' . $pattern . '$/imU', $against ) ) {
612
-
613
- $found = true;
614
- break;
615
- }
616
- }
617
-
618
- if ( ! $found ) {
619
-
620
- $new_matches[ $i ] = $matches[ $i ];
621
- }
622
- }
623
-
624
- //if ( count( $matches ) != count( $new_matches ) ) {
625
-
626
- $matches = $new_matches;
627
- //}
628
- }
629
- }
630
-
631
- return $matches;
632
- }
633
-
634
- /**
635
- * Return the alternate headings added by the user, saved in the post meta.
636
- *
637
- * The result is an associative array where the `key` is the original post heading
638
- * and the `value` is the alternate heading.
639
- *
640
- * @access private
641
- * @since 2.0
642
- *
643
- * @return array
644
- */
645
- private function getAlternateHeadings() {
646
-
647
- $alternates = array();
648
- $value = get_post_meta( $this->post->ID, '_ez-toc-alttext', true );
649
-
650
- if ( $value ) {
651
-
652
- $headings = preg_split( '/\r\n|[\r\n]/', $value );
653
- $count = count( $headings );
654
-
655
- if ( $headings ) {
656
-
657
- for ( $k = 0; $k < $count; $k++ ) {
658
-
659
- $heading = explode( '|', $headings[ $k ] );
660
-
661
- /**
662
- * @link https://wordpress.org/support/topic/undefined-offset-1-home-blog-public-wp-content-plugins-easy-table-of-contents/
663
- */
664
- if ( ! is_array( $heading) ||
665
- ! array_key_exists( 0, $heading ) ||
666
- ! array_key_exists( 1, $heading )
667
- ) {
668
- continue;
669
- }
670
-
671
- if ( 0 < strlen( $heading[0] ) && 0 < strlen( $heading[1] ) ) {
672
-
673
- $alternates[ $heading[0] ] = $heading[1];
674
- }
675
- }
676
-
677
- }
678
-
679
- }
680
-
681
- return $alternates;
682
- }
683
-
684
- /**
685
- * Add the alternate headings to the array.
686
- *
687
- * @see ezTOC_Post::extractHeadings()
688
- *
689
- * @access private
690
- * @since 2.0
691
- *
692
- * @param array $matches The heading from the post content extracted with preg_match_all().
693
- *
694
- * @return array
695
- */
696
- private function alternateHeadings( &$matches ) {
697
-
698
- $alt_headings = $this->getAlternateHeadings();
699
- //$count = count( $matches );
700
-
701
- if ( 0 < count( $alt_headings ) ) {
702
-
703
- //for ( $i = 0; $i < $count; $i++ ) {
704
- foreach ( $matches as $i => $match ) {
705
-
706
- foreach ( $alt_headings as $original_heading => $alt_heading ) {
707
-
708
- // Cleanup and texturize so alt heading can match heading in post content.
709
- $original_heading = wptexturize( trim( $original_heading ) );
710
-
711
- // Deal with special characters such as non-breakable space.
712
- $original_heading = str_replace(
713
- array( "\xc2\xa0" ),
714
- array( ' ' ),
715
- $original_heading
716
- );
717
-
718
- // Escape for regular expression.
719
- $original_heading = preg_quote( $original_heading );
720
-
721
- // Escape for regular expression some other characters: http://www.php.net/manual/en/regexp.reference.meta.php
722
- $original_heading = str_replace(
723
- array( '\*', '/', '%' ),
724
- array( '.*', '\/', '\%' ),
725
- $original_heading
726
- );
727
-
728
- // Cleanup subject so alt heading can match heading in post content.
729
- $subject = strip_tags( $matches[ $i ][0] );
730
-
731
- // Deal with special characters such as non-breakable space.
732
- $subject = str_replace(
733
- array( "\xc2\xa0" ),
734
- array( ' ' ),
735
- $subject
736
- );
737
-
738
- if ( @preg_match( '/^' . $original_heading . '$/imU', $subject ) ) {
739
-
740
- $matches[ $i ]['alternate'] = $alt_heading;
741
- }
742
- }
743
- }
744
- }
745
-
746
- return $matches;
747
- }
748
-
749
- /**
750
- * Add the heading `id` to the array.
751
- *
752
- * @see ezTOC_Post::extractHeadings()
753
- *
754
- * @access private
755
- * @since 2.0
756
- *
757
- * @param array $matches The heading from the post content extracted with preg_match_all().
758
- *
759
- * @return mixed
760
- */
761
- private function headingIDs( &$matches ) {
762
-
763
- //$count = count( $matches );
764
-
765
- //for ( $i = 0; $i < $count; $i++ ) {
766
- foreach ( $matches as $i => $match ) {
767
-
768
- $matches[ $i ]['id'] = $this->generateHeadingIDFromTitle( $matches[ $i ][0] );
769
- }
770
-
771
- return $matches;
772
- }
773
-
774
- /**
775
- * Create unique heading ID from heading string.
776
- *
777
- * @access private
778
- * @since 2.0
779
- *
780
- * @param string $heading
781
- *
782
- * @return bool|string
783
- */
784
- private function generateHeadingIDFromTitle( $heading ) {
785
-
786
- $return = false;
787
-
788
- if ( $heading ) {
789
-
790
- // WP entity encodes the post content.
791
- $return = html_entity_decode( $heading, ENT_QUOTES, get_option( 'blog_charset' ) );
792
- $return = br2( $return, ' ' );
793
- $return = trim( strip_tags( $return ) );
794
-
795
- // Convert accented characters to ASCII.
796
- $return = remove_accents( $return );
797
-
798
- // replace newlines with spaces (eg when headings are split over multiple lines)
799
- $return = str_replace( array( "\r", "\n", "\n\r", "\r\n" ), ' ', $return );
800
-
801
- // Remove `&amp;` and `&nbsp;` NOTE: in order to strip "hidden" `&nbsp;`,
802
- // title needs to be converted to HTML entities.
803
- // @link https://stackoverflow.com/a/21801444/5351316
804
- $return = htmlentities2( $return );
805
- $return = str_replace( array( '&amp;', '&nbsp;' ), ' ', $return );
806
- $return = html_entity_decode( $return, ENT_QUOTES, get_option( 'blog_charset' ) );
807
-
808
- // remove non alphanumeric chars
809
- //$return = preg_replace( '/[^a-zA-Z0-9 \-_]*/', '', $return );
810
- $return = preg_replace( '/[\x00-\x1F\x7F]*/u', '', $return );
811
-
812
- // Reserved Characters.
813
- // * ' ( ) ; : @ & = + $ , / ? # [ ]
814
- $return = str_replace(
815
- array( '*', '\'', '(', ')', ';', '@', '&', '=', '+', '$', ',', '/', '?', '#', '[', ']' ),
816
- '',
817
- $return
818
- );
819
-
820
- // Unsafe Characters.
821
- // % { } | \ ^ ~ [ ] `
822
- $return = str_replace(
823
- array( '%', '{', '}', '|', '\\', '^', '~', '[', ']', '`' ),
824
- '',
825
- $return
826
- );
827
-
828
- // Special Characters.
829
- // $ - _ . + ! * ' ( ) ,
830
- $return = str_replace(
831
- array( '$', '.', '+', '!', '*', '\'', '(', ')', ',' ),
832
- '',
833
- $return
834
- );
835
-
836
- // Dashes
837
- // Special Characters.
838
- // - (minus) - (dash) – (en dash) — (em dash)
839
- $return = str_replace(
840
- array( '-', '-', '–', '—' ),
841
- '-',
842
- $return
843
- );
844
-
845
- // Curley quotes.
846
- // ‘ (curly single open quote) ’ (curly single close quote) “ (curly double open quote) ” (curly double close quote)
847
- $return = str_replace(
848
- array( '‘', '’', '“', '”' ),
849
- '',
850
- $return
851
- );
852
-
853
- // AMP/Caching plugins seems to break URL with the following characters, so lets replace them.
854
- $return = str_replace( array( ':' ), '_', $return );
855
-
856
- // Convert space characters to an `_` (underscore).
857
- $return = preg_replace( '/\s+/', '_', $return );
858
-
859
- // Replace multiple `-` (hyphen) with a single `-` (hyphen).
860
- $return = preg_replace( '/-+/', '-', $return );
861
-
862
- // Replace multiple `_` (underscore) with a single `_` (underscore).
863
- $return = preg_replace( '/_+/', '_', $return );
864
-
865
- // Remove trailing `-` (hyphen) and `_` (underscore).
866
- $return = rtrim( $return, '-_' );
867
-
868
- /*
869
- * Encode URI based on ECMA-262.
870
- *
871
- * Only required to support the jQuery smoothScroll library.
872
- *
873
- * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI#Description
874
- * @link https://stackoverflow.com/a/19858404/5351316
875
- */
876
- $return = preg_replace_callback(
877
- "{[^0-9a-z_.!~*'();,/?:@&=+$#-]}i",
878
- function( $m ) {
879
-
880
- return sprintf( '%%%02X', ord( $m[0] ) );
881
- },
882
- $return
883
- );
884
-
885
- // lowercase everything?
886
- if ( ezTOC_Option::get( 'lowercase' ) ) {
887
-
888
- $return = strtolower( $return );
889
- }
890
-
891
- // if blank, then prepend with the fragment prefix
892
- // blank anchors normally appear on sites that don't use the latin charset
893
- if ( ! $return ) {
894
-
895
- $return = ( ezTOC_Option::get( 'fragment_prefix' ) ) ? ezTOC_Option::get( 'fragment_prefix' ) : '_';
896
- }
897
-
898
- // hyphenate?
899
- if ( ezTOC_Option::get( 'hyphenate' ) ) {
900
-
901
- $return = str_replace( '_', '-', $return );
902
- $return = preg_replace( '/-+/', '-', $return );
903
- }
904
- }
905
-
906
- if ( array_key_exists( $return, $this->collision_collector ) ) {
907
-
908
- $this->collision_collector[ $return ]++;
909
- $return .= '-' . $this->collision_collector[ $return ];
910
-
911
- } else {
912
-
913
- $this->collision_collector[ $return ] = 1;
914
- }
915
-
916
- return apply_filters( 'ez_toc_url_anchor_target', $return, $heading );
917
- }
918
-
919
- /**
920
- * Remove any empty headings from the TOC.
921
- *
922
- * @access private
923
- * @since 2.0
924
- *
925
- * @param array $matches The heading from the post content extracted with preg_match_all().
926
- *
927
- * @return array
928
- */
929
- private function removeEmptyHeadings( &$matches ) {
930
-
931
- $new_matches = array();
932
- //$count = count( $matches );
933
-
934
- //for ( $i = 0; $i < $count; $i ++ ) {
935
- foreach ( $matches as $i => $match ) {
936
-
937
- if ( trim( strip_tags( $matches[ $i ][0] ) ) != false ) {
938
-
939
- $new_matches[ $i ] = $matches[ $i ];
940
- }
941
- }
942
-
943
- //if ( count( $matches ) != count( $new_matches ) ) {
944
-
945
- $matches = $new_matches;
946
- //}
947
-
948
- return $matches;
949
- }
950
-
951
- /**
952
- * Whether or not the post has TOC items.
953
- *
954
- * @see ezTOC_Post::extractHeadings()
955
- *
956
- * @access public
957
- * @since 2.0
958
- *
959
- * @return bool
960
- */
961
- public function hasTOCItems() {
962
-
963
- return $this->hasTOCItems;
964
- }
965
-
966
- /**
967
- * Get the headings of the current page of the post.
968
- *
969
- * @access public
970
- * @since 2.0
971
- *
972
- * @param int|null $page
973
- *
974
- * @return array
975
- */
976
- public function getHeadings( $page = null ) {
977
-
978
- $headings = array();
979
-
980
- if ( is_null( $page ) ) {
981
-
982
- $page = $this->getCurrentPage();
983
- }
984
-
985
- if ( isset( $this->pages[ $page ] ) ) {
986
-
987
- //$headings = wp_list_pluck( $this->pages[ $page ]['headings'], 0 );
988
-
989
- $matches = $this->pages[ $page ]['headings'];
990
- //$count = count( $matches );
991
-
992
- //for ( $i = 0; $i < $count; $i++ ) {
993
- foreach ( $matches as $i => $match ) {
994
-
995
- //$anchor = $matches[ $i ]['id'];
996
- $headings[] = str_replace(
997
- array(
998
- $matches[ $i ][1], // start of heading
999
- '</h' . $matches[ $i ][2] . '>' // end of heading
1000
- ),
1001
- array(
1002
- '>',
1003
- '</h' . $matches[ $i ][2] . '>'
1004
- ),
1005
- $matches[ $i ][0]
1006
- );
1007
- }
1008
- }
1009
-
1010
- return $headings;
1011
- }
1012
-
1013
- /**
1014
- * Get the heading with in page anchors of the current page of the post.
1015
- *
1016
- * @access public
1017
- * @since 2.0
1018
- *
1019
- * @param int|null $page
1020
- *
1021
- * @return array
1022
- */
1023
- public function getHeadingsWithAnchors( $page = null ) {
1024
-
1025
- $headings = array();
1026
-
1027
- if ( is_null( $page ) ) {
1028
-
1029
- $page = $this->getCurrentPage();
1030
- }
1031
-
1032
- if ( isset( $this->pages[ $page ] ) ) {
1033
-
1034
- $matches = $this->pages[ $page ]['headings'];
1035
- //$count = count( $matches );
1036
-
1037
- //for ( $i = 0; $i < $count; $i++ ) {
1038
- foreach ( $matches as $i => $match ) {
1039
-
1040
- $anchor = $matches[ $i ]['id'];
1041
- $headings[] = str_replace(
1042
- array(
1043
- $matches[ $i ][1], // start of heading
1044
- '</h' . $matches[ $i ][2] . '>' // end of heading
1045
- ),
1046
- array(
1047
- '><span class="ez-toc-section" id="' . $anchor . '"></span>',
1048
- '<span class="ez-toc-section-end"></span></h' . $matches[ $i ][2] . '>'
1049
- ),
1050
- $matches[ $i ][0]
1051
- );
1052
- }
1053
- }
1054
-
1055
- return $headings;
1056
- }
1057
-
1058
- /**
1059
- * Get the post TOC list.
1060
- *
1061
- * @access public
1062
- * @since 2.0
1063
- *
1064
- * @return string
1065
- */
1066
- public function getTOCList() {
1067
-
1068
- $html = '';
1069
-
1070
- if ( $this->hasTOCItems ) {
1071
-
1072
- foreach ( $this->pages as $page => $attribute ) {
1073
-
1074
- $html .= $this->createTOC( $page, $attribute['headings'] );
1075
- }
1076
-
1077
- $html = '<ul class="ez-toc-list ez-toc-list-level-1">' . $html . '</ul>';
1078
- }
1079
-
1080
- return $html;
1081
- }
1082
-
1083
- /**
1084
- * Get the post TOC content block.
1085
- *
1086
- * @access public
1087
- * @since 2.0
1088
- *
1089
- * @return string
1090
- */
1091
- public function getTOC() {
1092
-
1093
- $class = array( 'ez-toc-v' . str_replace( '.', '_', ezTOC::VERSION ) );
1094
- $html = '';
1095
-
1096
- if ( $this->hasTOCItems() ) {
1097
-
1098
- // wrapping css classes
1099
- switch ( ezTOC_Option::get( 'wrapping' ) ) {
1100
-
1101
- case 'left':
1102
- $class[] = 'ez-toc-wrap-left';
1103
- break;
1104
-
1105
- case 'right':
1106
- $class[] = 'ez-toc-wrap-right';
1107
- break;
1108
-
1109
- case 'none':
1110
- default:
1111
- // do nothing
1112
- }
1113
-
1114
- if ( ezTOC_Option::get( 'show_hierarchy' ) ) {
1115
-
1116
- $class[] = 'counter-hierarchy';
1117
-
1118
- } else {
1119
-
1120
- $class[] .= 'counter-flat';
1121
- }
1122
-
1123
- switch ( ezTOC_Option::get( 'counter' ) ) {
1124
-
1125
- case 'numeric':
1126
- $class[] .= 'counter-numeric';
1127
- break;
1128
-
1129
- case 'roman':
1130
- $class[] = 'counter-roman';
1131
- break;
1132
-
1133
- case 'decimal':
1134
- $class[] = 'counter-decimal';
1135
- break;
1136
- }
1137
-
1138
- // colour themes
1139
- switch ( ezTOC_Option::get( 'theme' ) ) {
1140
-
1141
- case 'light-blue':
1142
- $class[] = 'ez-toc-light-blue';
1143
- break;
1144
-
1145
- case 'white':
1146
- $class[] = 'ez-toc-white';
1147
- break;
1148
-
1149
- case 'black':
1150
- $class[] = 'ez-toc-black';
1151
- break;
1152
-
1153
- case 'transparent':
1154
- $class[] .= 'ez-toc-transparent';
1155
- break;
1156
-
1157
- case 'grey':
1158
- $class[] = 'ez-toc-grey';
1159
- break;
1160
- }
1161
-
1162
- $custom_classes = ezTOC_Option::get( 'css_container_class', '' );
1163
-
1164
- if ( 0 < strlen( $custom_classes ) ) {
1165
-
1166
- $custom_classes = explode( ' ', $custom_classes );
1167
- $custom_classes = apply_filters( 'ez_toc_container_class', $custom_classes, $this );
1168
-
1169
- if ( is_array( $custom_classes ) ) {
1170
-
1171
- $class = array_merge( $class, $custom_classes );
1172
- }
1173
- }
1174
-
1175
- $class = array_filter( $class );
1176
- $class = array_map( 'trim', $class );
1177
- $class = array_map( 'sanitize_html_class', $class );
1178
-
1179
- $html .= '<div id="ez-toc-container" class="' . implode( ' ', $class ) . '">' . PHP_EOL;
1180
-
1181
- if ( ezTOC_Option::get( 'show_heading_text' ) ) {
1182
-
1183
- $toc_title = ezTOC_Option::get( 'heading_text' );
1184
-
1185
- if ( strpos( $toc_title, '%PAGE_TITLE%' ) !== false ) {
1186
-
1187
- $toc_title = str_replace( '%PAGE_TITLE%', get_the_title(), $toc_title );
1188
- }
1189
-
1190
- if ( strpos( $toc_title, '%PAGE_NAME%' ) !== false ) {
1191
-
1192
- $toc_title = str_replace( '%PAGE_NAME%', get_the_title(), $toc_title );
1193
- }
1194
-
1195
- $html .= '<div class="ez-toc-title-container">' . PHP_EOL;
1196
-
1197
- $html .= '<p class="ez-toc-title">' . esc_html__( htmlentities( $toc_title, ENT_COMPAT, 'UTF-8' ), 'easy-table-of-contents' ). '</p>' . PHP_EOL;
1198
-
1199
- $html .= '<span class="ez-toc-title-toggle">';
1200
-
1201
- if ( ezTOC_Option::get( 'visibility' ) ) {
1202
-
1203
- $html .= '<a class="ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle" style="display: none;"><i class="ez-toc-glyphicon ez-toc-icon-toggle"></i></a>';
1204
- }
1205
-
1206
- $html .= '</span>';
1207
-
1208
- $html .= '</div>' . PHP_EOL;
1209
- }
1210
-
1211
- ob_start();
1212
- do_action( 'ez_toc_before' );
1213
- $html .= ob_get_clean();
1214
-
1215
- $html .= '<nav>' . $this->getTOCList() . '</nav>';
1216
-
1217
- ob_start();
1218
- do_action( 'ez_toc_after' );
1219
- $html .= ob_get_clean();
1220
-
1221
- $html .= '</div>' . PHP_EOL;
1222
-
1223
- // Enqueue the script.
1224
- wp_enqueue_script( 'ez-toc-js' );
1225
- }
1226
-
1227
- return $html;
1228
- }
1229
-
1230
- /**
1231
- * Displays the post's TOC.
1232
- *
1233
- * @access public
1234
- * @since 2.0
1235
- */
1236
- public function toc() {
1237
-
1238
- echo $this->getTOC();
1239
- }
1240
-
1241
- /**
1242
- * Generate the TOC list items for a given page within a post.
1243
- *
1244
- * @access private
1245
- * @since 2.0
1246
- *
1247
- * @param int $page The page of the post to create the TOC items for.
1248
- * @param array $matches The heading from the post content extracted with preg_match_all().
1249
- *
1250
- * @return string The HTML list of TOC items.
1251
- */
1252
- private function createTOC( $page, $matches ) {
1253
-
1254
- // Whether or not the TOC should be built flat or hierarchical.
1255
- $hierarchical = ezTOC_Option::get( 'show_hierarchy' );
1256
- $html = '';
1257
-
1258
- if ( $hierarchical ) {
1259
-
1260
- $current_depth = 100; // headings can't be larger than h6 but 100 as a default to be sure
1261
- $numbered_items = array();
1262
- $numbered_items_min = null;
1263
-
1264
- // reset the internal collision collection
1265
- /** @todo does this need to be used??? */
1266
- //self::$collision_collector = array();
1267
-
1268
- // find the minimum heading to establish our baseline
1269
- //for ( $i = 0; $i < count( $matches ); $i ++ ) {
1270
- foreach ( $matches as $i => $match ) {
1271
- if ( $current_depth > $matches[ $i ][2] ) {
1272
- $current_depth = (int) $matches[ $i ][2];
1273
- }
1274
- }
1275
-
1276
- $numbered_items[ $current_depth ] = 0;
1277
- $numbered_items_min = $current_depth;
1278
-
1279
- //for ( $i = 0; $i < count( $matches ); $i ++ ) {
1280
- foreach ( $matches as $i => $match ) {
1281
-
1282
- $level = $matches[ $i ][2];
1283
- $count = $i + 1;
1284
-
1285
- if ( $current_depth == (int) $matches[ $i ][2] ) {
1286
-
1287
- $html .= '<li class="ez-toc-page-' . $page . ' ez-toc-heading-level-' . $current_depth . '">';
1288
- }
1289
-
1290
- // start lists
1291
- if ( $current_depth != (int) $matches[ $i ][2] ) {
1292
-
1293
- for ( $current_depth; $current_depth < (int) $matches[ $i ][2]; $current_depth++ ) {
1294
-
1295
- $numbered_items[ $current_depth + 1 ] = 0;
1296
- $html .= '<ul class="ez-toc-list-level-' . $level . '"><li class="ez-toc-heading-level-' . $level . '">';
1297
- }
1298
- }
1299
-
1300
- $title = isset( $matches[ $i ]['alternate'] ) ? $matches[ $i ]['alternate'] : $matches[ $i ][0];
1301
- $title = br2( $title, ' ' );
1302
- $title = strip_tags( apply_filters( 'ez_toc_title', $title ), apply_filters( 'ez_toc_title_allowable_tags', '' ) );
1303
-
1304
- $html .= $this->createTOCItemAnchor( $page, $matches[ $i ]['id'], $title, $count );
1305
-
1306
- // end lists
1307
- if ( $i != count( $matches ) - 1 ) {
1308
-
1309
- if ( $current_depth > (int) $matches[ $i + 1 ][2] ) {
1310
-
1311
- for ( $current_depth; $current_depth > (int) $matches[ $i + 1 ][2]; $current_depth-- ) {
1312
-
1313
- $html .= '</li></ul>';
1314
- $numbered_items[ $current_depth ] = 0;
1315
- }
1316
- }
1317
-
1318
- if ( $current_depth == (int) @$matches[ $i + 1 ][2] ) {
1319
-
1320
- $html .= '</li>';
1321
- }
1322
-
1323
- } else {
1324
-
1325
- // this is the last item, make sure we close off all tags
1326
- for ( $current_depth; $current_depth >= $numbered_items_min; $current_depth-- ) {
1327
-
1328
- $html .= '</li>';
1329
-
1330
- if ( $current_depth != $numbered_items_min ) {
1331
- $html .= '</ul>';
1332
- }
1333
- }
1334
- }
1335
- }
1336
-
1337
- } else {
1338
-
1339
- //for ( $i = 0; $i < count( $matches ); $i++ ) {
1340
- foreach ( $matches as $i => $match ) {
1341
-
1342
- $count = $i + 1;
1343
-
1344
- $title = isset( $matches[ $i ]['alternate'] ) ? $matches[ $i ]['alternate'] : $matches[ $i ][0];
1345
- $title = strip_tags( apply_filters( 'ez_toc_title', $title ), apply_filters( 'ez_toc_title_allowable_tags', '' ) );
1346
-
1347
- $html .= '<li class="ez-toc-page-' . $page . '">';
1348
-
1349
- $html .= $this->createTOCItemAnchor( $page, $matches[ $i ]['id'], $title, $count );
1350
-
1351
- $html .= '</li>';
1352
- }
1353
- }
1354
-
1355
- return $html;
1356
- }
1357
-
1358
- /**
1359
- * @access private
1360
- * @since 2.0
1361
- *
1362
- * @param int $page
1363
- * @param string $id
1364
- * @param string $title
1365
- * @param int $count
1366
- *
1367
- * @return string
1368
- */
1369
- private function createTOCItemAnchor( $page, $id, $title, $count ) {
1370
-
1371
- return sprintf(
1372
- '<a class="ez-toc-link ez-toc-heading-' . $count . '" href="%1$s" title="%2$s">%3$s</a>',
1373
- esc_attr( $this->createTOCItemURL( $id, $page ) ),
1374
- esc_attr( strip_tags( $title ) ),
1375
- $title
1376
- );
1377
- }
1378
-
1379
- /**
1380
- * @access private
1381
- * @since 2.0
1382
- *
1383
- * @param string $id
1384
- * @param int $page
1385
- *
1386
- * @return string
1387
- */
1388
- private function createTOCItemURL( $id, $page ) {
1389
-
1390
- $current_post = $this->post->ID === $this->queriedObjectID;
1391
- $current_page = $this->getCurrentPage();
1392
-
1393
- if ( $page === $current_page && $current_post ) {
1394
-
1395
- return '#' . $id;
1396
-
1397
- } elseif ( 1 === $page ) {
1398
-
1399
- return trailingslashit( $this->permalink ) . '#' . $id;
1400
-
1401
- }
1402
-
1403
- return trailingslashit( $this->permalink ) . $page . '/#' . $id;
1404
- }
1405
- }
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use function Easy_Plugins\Table_Of_Contents\String\br2;
4
+
5
+ class ezTOC_Post {
6
+
7
+ /**
8
+ * @since 2.0
9
+ * @var int
10
+ */
11
+ private $queriedObjectID;
12
+
13
+ /**
14
+ * @since 2.0
15
+ * @var WP_Post
16
+ */
17
+ private $post;
18
+
19
+ /**
20
+ * @since 2.0
21
+ * @var false|string
22
+ */
23
+ private $permalink;
24
+
25
+ /**
26
+ * The post content broken into pages by user inserting `<!--nextpage-->` into the post content.
27
+ * @see ezTOC_Post::extractPages()
28
+ * @since 2.0
29
+ * @var array
30
+ */
31
+ private $pages = array();
32
+
33
+ /**
34
+ * The user defined heading levels to be included in the TOC.
35
+ * @see ezTOC_Post::getHeadingLevels()
36
+ * @since 2.0
37
+ * @var array
38
+ */
39
+ private $headingLevels = array();
40
+
41
+ /**
42
+ * Array of nodes that are excluded by class/id selector.
43
+ * @since 2.0
44
+ * @var string[]
45
+ */
46
+ private $excludedNodes = array();
47
+
48
+ /**
49
+ * Keeps a track of used anchors for collision detecting.
50
+ * @see ezTOC_Post::generateHeadingIDFromTitle()
51
+ * @since 2.0
52
+ * @var array
53
+ */
54
+ private $collision_collector = array();
55
+
56
+ /**
57
+ * @var bool
58
+ */
59
+ private $hasTOCItems = false;
60
+
61
+ /**
62
+ * ezTOC_Post constructor.
63
+ *
64
+ * @since 2.0
65
+ *
66
+ * @param WP_Post $post
67
+ * @param bool $apply_content_filter Whether or not to apply the `the_content` filter on the post content.
68
+ */
69
+ public function __construct( WP_Post $post, $apply_content_filter = true ) {
70
+
71
+ $this->post = $post;
72
+ $this->permalink = get_permalink( $post );
73
+ $this->queriedObjectID = get_queried_object_id();
74
+
75
+ if ( $apply_content_filter ) {
76
+
77
+ $this->applyContentFilter()->process();
78
+
79
+ } else {
80
+
81
+ $this->process();
82
+ }
83
+ }
84
+
85
+ /**
86
+ * @access public
87
+ * @since 2.0
88
+ *
89
+ * @param $id
90
+ *
91
+ * @return ezTOC_Post|null
92
+ */
93
+ public static function get( $id ) {
94
+
95
+ $post = get_post( $id );
96
+
97
+ if ( ! $post instanceof WP_Post ) {
98
+
99
+ return null;
100
+ }
101
+
102
+ return new static( $post );
103
+ }
104
+
105
+ /**
106
+ * Process post content for headings.
107
+ *
108
+ * This must be run after object init or after @see ezTOC_Post::applyContentFilter().
109
+ *
110
+ * @since 2.0
111
+ *
112
+ * @return static
113
+ */
114
+ private function process() {
115
+
116
+ $this->processPages();
117
+
118
+ return $this;
119
+ }
120
+
121
+ /**
122
+ * Apply `the_content` filter to the post content.
123
+ *
124
+ * @since 2.0
125
+ *
126
+ * @return static
127
+ */
128
+ private function applyContentFilter() {
129
+
130
+ add_filter( 'strip_shortcodes_tagnames', array( __CLASS__, 'stripShortcodes' ), 10, 2 );
131
+
132
+ /*
133
+ * Ensure the ezTOC content filter is not applied when running `the_content` filter.
134
+ */
135
+ remove_filter( 'the_content', array( 'ezTOC', 'the_content' ), 100 );
136
+
137
+ $this->post->post_content = apply_filters( 'the_content', strip_shortcodes( $this->post->post_content ) );
138
+
139
+ add_filter( 'the_content', array( 'ezTOC', 'the_content' ), 100 );
140
+
141
+ remove_filter( 'strip_shortcodes_tagnames', array( __CLASS__, 'stripShortcodes' ) );
142
+
143
+ return $this;
144
+ }
145
+
146
+ /**
147
+ * Callback for the `strip_shortcodes_tagnames` filter.
148
+ *
149
+ * Strip the shortcodes so their content is no processed for headings.
150
+ *
151
+ * @see ezTOC_Post::applyContentFilter()
152
+ *
153
+ * @since 2.0
154
+ *
155
+ * @param array $tags_to_remove Array of shortcode tags to remove.
156
+ * @param string $content Content shortcodes are being removed from.
157
+ *
158
+ * @return array
159
+ */
160
+ public static function stripShortcodes( $tags_to_remove, $content ) {
161
+
162
+ //error_log( var_export( $tags_to_remove, true ) );
163
+
164
+ /*
165
+ * Ensure the ezTOC shortcodes are not processed when applying `the_content` filter
166
+ * otherwise an infinite loop may occur.
167
+ */
168
+ $tags_to_remove = apply_filters(
169
+ 'ez_toc_strip_shortcodes_tagnames',
170
+ array(
171
+ 'ez-toc',
172
+ apply_filters( 'ez_toc_shortcode', 'toc' ),
173
+ ),
174
+ $content
175
+ );
176
+
177
+ //error_log( var_export( $tags_to_remove, true ) );
178
+
179
+ return $tags_to_remove;
180
+ }
181
+
182
+ /**
183
+ * This is a work around for theme's and plugins
184
+ * which break the WordPress global $wp_query var by unsetting it
185
+ * or overwriting it which breaks the method call
186
+ * that `get_query_var()` uses to return the query variable.
187
+ *
188
+ * @access protected
189
+ * @since 2.0
190
+ *
191
+ * @return int
192
+ */
193
+ protected function getCurrentPage() {
194
+
195
+ global $wp_query;
196
+
197
+ // Check to see if the global `$wp_query` var is an instance of WP_Query and that the get() method is callable.
198
+ // If it is then when can simply use the get_query_var() function.
199
+ if ( $wp_query instanceof WP_Query && is_callable( array( $wp_query, 'get' ) ) ) {
200
+
201
+ $page = get_query_var( 'page', 1 );
202
+
203
+ return 1 > $page ? 1 : $page;
204
+
205
+ // 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.
206
+ } elseif ( isset( $GLOBALS['wp_query']->query_vars[ 'page' ] ) ) {
207
+
208
+ return $GLOBALS['wp_query']->query_vars[ 'page' ];
209
+
210
+ // We should not reach this, but if we do, lets check the original parsed query vars in $GLOBALS['wp_the_query']->query_vars.
211
+ } elseif ( isset( $GLOBALS['wp_the_query']->query_vars[ 'page' ] ) ) {
212
+
213
+ return $GLOBALS['wp_the_query']->query_vars[ 'page' ];
214
+
215
+ // Ok, if all else fails, check the $_REQUEST super global.
216
+ } elseif ( isset( $_REQUEST[ 'page' ] ) ) {
217
+
218
+ return $_REQUEST[ 'page' ];
219
+ }
220
+
221
+ // Finally, return the $default if it was supplied.
222
+ return 1;
223
+ }
224
+
225
+ /**
226
+ * Get the number of page the post has.
227
+ *
228
+ * @access protected
229
+ * @since 2.0
230
+ *
231
+ * @return int
232
+ */
233
+ protected function getNumberOfPages() {
234
+
235
+ return count( $this->pages );
236
+ }
237
+
238
+ /**
239
+ * Whether or not the post has multiple pages.
240
+ *
241
+ * @access protected
242
+ * @since 2.0
243
+ *
244
+ * @return bool
245
+ */
246
+ protected function isMultipage() {
247
+
248
+ return 1 < $this->getNumberOfPages();
249
+ }
250
+
251
+ /**
252
+ * Parse the post content and headings.
253
+ *
254
+ * @access private
255
+ * @since 2.0
256
+ */
257
+ private function processPages() {
258
+
259
+ //if ( ! class_exists( 'TagFilter' ) ) {
260
+ //
261
+ // require_once( EZ_TOC_PATH . '/includes/vendor/ultimate-web-scraper/tag_filter.php' );
262
+ //}
263
+
264
+ $split = preg_split( '/<!--nextpage-->/msuU', $this->post->post_content );
265
+ $pages = array();
266
+
267
+ if ( is_array( $split ) ) {
268
+
269
+ $page = 1;
270
+
271
+ //$tagFilterOptions = TagFilter::GetHTMLOptions();
272
+
273
+ //// Set custom TagFilter options.
274
+ //$tagFilterOptions['charset'] = get_option( 'blog_charset' );
275
+ ////$tagFilterOptions['output_mode'] = 'xml';
276
+
277
+ foreach ( $split as $content ) {
278
+
279
+ //$html = TagFilter::Explode( $content, $tagFilterOptions );
280
+ //
281
+ ///**
282
+ // * @since 2.0
283
+ // *
284
+ // * @param $selectors array Array of classes/id selector to exclude from TOC.
285
+ // * @param $content string Post content.
286
+ // */
287
+ //$selectors = apply_filters( 'ez_toc_exclude_by_selector', array(), $content );
288
+ //
289
+ //$nodes = $html->Find( implode( ',', $selectors ) );
290
+ //
291
+ //foreach ( $nodes['ids'] as $id ) {
292
+ //
293
+ // $html->Remove( $id );
294
+ //}
295
+ //
296
+ //$eligibleContent = $html->Implode( 0, $tagFilterOptions );
297
+ //
298
+ ///**
299
+ // * TagFilter::Implode() writes br tags as `<br>` while WP normalizes to `<br />`.
300
+ // * Normalize `$eligibleContent` to match WP.
301
+ // *
302
+ // * @see wpautop()
303
+ // */
304
+ ////$eligibleContent = str_replace( array( '<br>', '<br/>' ), array( '<br />' ), $eligibleContent );
305
+ //$eligibleContent = \Easy_Plugins\Table_Of_Contents\String\force_balance_tags( $eligibleContent );
306
+
307
+ $this->extractExcludedNodes( $page, $content );
308
+
309
+ $pages[ $page ] = array(
310
+ 'headings' => $this->extractHeadings( $content ),
311
+ 'content' => $content,
312
+ );
313
+
314
+ $page++;
315
+ }
316
+
317
+ }
318
+
319
+ $this->pages = $pages;
320
+ }
321
+
322
+ /**
323
+ * Get the post's parse content and headings.
324
+ *
325
+ * @access public
326
+ * @since 2.0
327
+ *
328
+ * @return array
329
+ */
330
+ public function getPages() {
331
+
332
+ return $this->pages;
333
+ }
334
+
335
+ /**
336
+ * Extract nodes that heading are to be excluded.
337
+ *
338
+ * @since 2.0
339
+ *
340
+ * @param int $page
341
+ * @param string $content
342
+ */
343
+ private function extractExcludedNodes( $page, $content ) {
344
+
345
+ if ( ! class_exists( 'TagFilter' ) ) {
346
+
347
+ require_once( EZ_TOC_PATH . '/includes/vendor/ultimate-web-scraper/tag_filter.php' );
348
+ }
349
+
350
+ $tagFilterOptions = TagFilter::GetHTMLOptions();
351
+
352
+ // Set custom TagFilter options.
353
+ $tagFilterOptions['charset'] = get_option( 'blog_charset' );
354
+ //$tagFilterOptions['output_mode'] = 'xml';
355
+
356
+ $html = TagFilter::Explode( $content, $tagFilterOptions );
357
+
358
+ /**
359
+ * @since 2.0
360
+ *
361
+ * @param $selectors array Array of classes/id selector to exclude from TOC.
362
+ * @param $content string Post content.
363
+ */
364
+ $selectors = apply_filters( 'ez_toc_exclude_by_selector', array( '.ez-toc-exclude-headings' ), $content );
365
+
366
+ $nodes = $html->Find( implode( ',', $selectors ) );
367
+
368
+ foreach ( $nodes['ids'] as $id ) {
369
+
370
+ //$this->excludedNodes[ $page ][ $id ] = $html->Implode( $id, $tagFilterOptions );
371
+ array_push( $this->excludedNodes, $html->Implode( $id, $tagFilterOptions ) );
372
+ }
373
+
374
+ //$eligibleContent = $html->Implode( 0, $tagFilterOptions );
375
+
376
+ /**
377
+ * TagFilter::Implode() writes br tags as `<br>` while WP normalizes to `<br />`.
378
+ * Normalize `$eligibleContent` to match WP.
379
+ *
380
+ * @see wpautop()
381
+ */
382
+ //$eligibleContent = \Easy_Plugins\Table_Of_Contents\String\force_balance_tags( $eligibleContent );
383
+ }
384
+
385
+ /**
386
+ * Extract the posts content for headings.
387
+ *
388
+ * @access private
389
+ * @since 2.0
390
+ *
391
+ * @param string $content
392
+ *
393
+ * @return array
394
+ */
395
+ private function extractHeadings( $content ) {
396
+
397
+ $matches = array();
398
+
399
+ // reset the internal collision collection as the_content may have been triggered elsewhere
400
+ // eg by themes or other plugins that need to read in content such as metadata fields in
401
+ // the head html tag, or to provide descriptions to twitter/facebook
402
+ /** @todo does this need to be used??? */
403
+ //self::$collision_collector = array();
404
+
405
+ $content = apply_filters( 'ez_toc_extract_headings_content', wptexturize( $content ) );
406
+
407
+ // get all headings
408
+ // the html spec allows for a maximum of 6 heading depths
409
+ if ( preg_match_all( '/(<h([1-6]{1})[^>]*>)(.*)<\/h\2>/msuU', $content, $matches, PREG_SET_ORDER ) ) {
410
+
411
+ $minimum = absint( ezTOC_Option::get( 'start' ) );
412
+
413
+ $this->removeHeadingsFromExcludedNodes( $matches );
414
+ $this->removeHeadings( $matches );
415
+ $this->excludeHeadings( $matches );
416
+ $this->removeEmptyHeadings( $matches );
417
+
418
+ if ( count( $matches ) >= $minimum ) {
419
+
420
+ $this->alternateHeadings( $matches );
421
+ $this->headingIDs( $matches );
422
+ $this->hasTOCItems = true;
423
+
424
+ } else {
425
+
426
+ return array();
427
+ }
428
+
429
+ }
430
+
431
+ return array_values( $matches ); // Rest the array index.
432
+ }
433
+
434
+ /**
435
+ * Whether or not the string is in one of the excluded nodes.
436
+ *
437
+ * @since 2.0
438
+ *
439
+ * @param string $string
440
+ *
441
+ * @return bool
442
+ */
443
+ private function inExcludedNode( $string ) {
444
+
445
+ foreach ( $this->excludedNodes as $node ) {
446
+
447
+ if ( empty( $node ) || empty( $string ) ) {
448
+
449
+ return false;
450
+ }
451
+
452
+ if ( false !== strpos( $node, $string ) ) {
453
+
454
+ return true;
455
+ }
456
+ }
457
+
458
+ return false;
459
+ }
460
+
461
+ /**
462
+ * Remove headings that are in excluded nodes.
463
+ *
464
+ * @since 2.0
465
+ *
466
+ * @param array $matches
467
+ *
468
+ * @return array
469
+ */
470
+ private function removeHeadingsFromExcludedNodes( &$matches ) {
471
+
472
+ foreach ( $matches as $i => $match ) {
473
+
474
+ if ( $this->inExcludedNode( "{$match[3]}</h$match[2]>" ) ) {
475
+
476
+ unset( $matches[ $i ] );
477
+ }
478
+ }
479
+
480
+ return $matches;
481
+ }
482
+
483
+ /**
484
+ * Get the heading levels to be included in the TOC.
485
+ *
486
+ * @access private
487
+ * @since 2.0
488
+ *
489
+ * @return array
490
+ */
491
+ private function getHeadingLevels() {
492
+
493
+ $levels = get_post_meta( $this->post->ID, '_ez-toc-heading-levels', true );
494
+
495
+ if ( ! is_array( $levels ) ) {
496
+
497
+ $levels = array();
498
+ }
499
+
500
+ if ( empty( $levels ) ) {
501
+
502
+ $levels = ezTOC_Option::get( 'heading_levels', array() );
503
+ }
504
+
505
+ $this->headingLevels = $levels;
506
+
507
+ return $this->headingLevels;
508
+ }
509
+
510
+ /**
511
+ * Remove the heading levels as defined by user settings from the TOC heading matches.
512
+ *
513
+ * @see ezTOC_Post::extractHeadings()
514
+ *
515
+ * @access private
516
+ * @since 2.0
517
+ *
518
+ * @param array $matches The heading from the post content extracted with preg_match_all().
519
+ *
520
+ * @return array
521
+ */
522
+ private function removeHeadings( &$matches ) {
523
+
524
+ $levels = $this->getHeadingLevels();
525
+
526
+ if ( count( $levels ) != 6 ) {
527
+
528
+ $new_matches = array();
529
+ //$count = count( $matches );
530
+
531
+ //for ( $i = 0; $i < $count; $i++ ) {
532
+ foreach ( $matches as $i => $match ) {
533
+
534
+ if ( in_array( $matches[ $i ][2], $levels ) ) {
535
+
536
+ $new_matches[ $i ] = $matches[ $i ];
537
+ }
538
+ }
539
+
540
+ $matches = $new_matches;
541
+ }
542
+
543
+ return $matches;
544
+ }
545
+
546
+ /**
547
+ * Exclude the heading, by title, as defined by the user settings from the TOC matches.
548
+ *
549
+ * @see ezTOC_Post::extractHeadings()
550
+ *
551
+ * @access private
552
+ * @since 2.0
553
+ *
554
+ * @param array $matches The headings from the post content extracted with preg_match_all().
555
+ *
556
+ * @return array
557
+ */
558
+ private function excludeHeadings( &$matches ) {
559
+
560
+ $exclude = get_post_meta( $this->post->ID, '_ez-toc-exclude', true );
561
+
562
+ if ( empty( $exclude ) ) {
563
+
564
+ $exclude = ezTOC_Option::get( 'exclude' );
565
+ }
566
+
567
+ if ( $exclude ) {
568
+
569
+ $excluded_headings = explode( '|', $exclude );
570
+ $excluded_count = count( $excluded_headings );
571
+
572
+ if ( $excluded_count > 0 ) {
573
+
574
+ for ( $j = 0; $j < $excluded_count; $j++ ) {
575
+
576
+ $excluded_headings[ $j ] = preg_quote( $excluded_headings[ $j ] );
577
+
578
+ // escape some regular expression characters
579
+ // others: http://www.php.net/manual/en/regexp.reference.meta.php
580
+ $excluded_headings[ $j ] = str_replace(
581
+ array( '\*', '/', '%' ),
582
+ array( '.*', '\/', '\%' ),
583
+ trim( $excluded_headings[ $j ] )
584
+ );
585
+ }
586
+
587
+ $new_matches = array();
588
+ //$count = count( $matches );
589
+
590
+ //for ( $i = 0; $i < $count; $i++ ) {
591
+ foreach ( $matches as $i => $match ) {
592
+
593
+ $found = false;
594
+
595
+ $against = html_entity_decode(
596
+ wptexturize( strip_tags( str_replace( array( "\r", "\n" ), ' ', $matches[ $i ][0] ) ) ),
597
+ ENT_NOQUOTES,
598
+ get_option( 'blog_charset' )
599
+ );
600
+
601
+ for ( $j = 0; $j < $excluded_count; $j++ ) {
602
+
603
+ // Since WP manipulates the post content it is required that the excluded header and
604
+ // the actual header be manipulated similarly so a match can be made.
605
+ $pattern = html_entity_decode(
606
+ wptexturize( $excluded_headings[ $j ] ),
607
+ ENT_NOQUOTES,
608
+ get_option( 'blog_charset' )
609
+ );
610
+
611
+ if ( @preg_match( '/^' . $pattern . '$/imU', $against ) ) {
612
+
613
+ $found = true;
614
+ break;
615
+ }
616
+ }
617
+
618
+ if ( ! $found ) {
619
+
620
+ $new_matches[ $i ] = $matches[ $i ];
621
+ }
622
+ }
623
+
624
+ //if ( count( $matches ) != count( $new_matches ) ) {
625
+
626
+ $matches = $new_matches;
627
+ //}
628
+ }
629
+ }
630
+
631
+ return $matches;
632
+ }
633
+
634
+ /**
635
+ * Return the alternate headings added by the user, saved in the post meta.
636
+ *
637
+ * The result is an associative array where the `key` is the original post heading
638
+ * and the `value` is the alternate heading.
639
+ *
640
+ * @access private
641
+ * @since 2.0
642
+ *
643
+ * @return array
644
+ */
645
+ private function getAlternateHeadings() {
646
+
647
+ $alternates = array();
648
+ $value = get_post_meta( $this->post->ID, '_ez-toc-alttext', true );
649
+
650
+ if ( $value ) {
651
+
652
+ $headings = preg_split( '/\r\n|[\r\n]/', $value );
653
+ $count = count( $headings );
654
+
655
+ if ( $headings ) {
656
+
657
+ for ( $k = 0; $k < $count; $k++ ) {
658
+
659
+ $heading = explode( '|', $headings[ $k ] );
660
+
661
+ /**
662
+ * @link https://wordpress.org/support/topic/undefined-offset-1-home-blog-public-wp-content-plugins-easy-table-of-contents/
663
+ */
664
+ if ( ! is_array( $heading) ||
665
+ ! array_key_exists( 0, $heading ) ||
666
+ ! array_key_exists( 1, $heading )
667
+ ) {
668
+ continue;
669
+ }
670
+
671
+ if ( 0 < strlen( $heading[0] ) && 0 < strlen( $heading[1] ) ) {
672
+
673
+ $alternates[ $heading[0] ] = $heading[1];
674
+ }
675
+ }
676
+
677
+ }
678
+
679
+ }
680
+
681
+ return $alternates;
682
+ }
683
+
684
+ /**
685
+ * Add the alternate headings to the array.
686
+ *
687
+ * @see ezTOC_Post::extractHeadings()
688
+ *
689
+ * @access private
690
+ * @since 2.0
691
+ *
692
+ * @param array $matches The heading from the post content extracted with preg_match_all().
693
+ *
694
+ * @return array
695
+ */
696
+ private function alternateHeadings( &$matches ) {
697
+
698
+ $alt_headings = $this->getAlternateHeadings();
699
+ //$count = count( $matches );
700
+
701
+ if ( 0 < count( $alt_headings ) ) {
702
+
703
+ //for ( $i = 0; $i < $count; $i++ ) {
704
+ foreach ( $matches as $i => $match ) {
705
+
706
+ foreach ( $alt_headings as $original_heading => $alt_heading ) {
707
+
708
+ // Cleanup and texturize so alt heading can match heading in post content.
709
+ $original_heading = wptexturize( trim( $original_heading ) );
710
+
711
+ // Deal with special characters such as non-breakable space.
712
+ $original_heading = str_replace(
713
+ array( "\xc2\xa0" ),
714
+ array( ' ' ),
715
+ $original_heading
716
+ );
717
+
718
+ // Escape for regular expression.
719
+ $original_heading = preg_quote( $original_heading );
720
+
721
+ // Escape for regular expression some other characters: http://www.php.net/manual/en/regexp.reference.meta.php
722
+ $original_heading = str_replace(
723
+ array( '\*', '/', '%' ),
724
+ array( '.*', '\/', '\%' ),
725
+ $original_heading
726
+ );
727
+
728
+ // Cleanup subject so alt heading can match heading in post content.
729
+ $subject = strip_tags( $matches[ $i ][0] );
730
+
731
+ // Deal with special characters such as non-breakable space.
732
+ $subject = str_replace(
733
+ array( "\xc2\xa0" ),
734
+ array( ' ' ),
735
+ $subject
736
+ );
737
+
738
+ if ( @preg_match( '/^' . $original_heading . '$/imU', $subject ) ) {
739
+
740
+ $matches[ $i ]['alternate'] = $alt_heading;
741
+ }
742
+ }
743
+ }
744
+ }
745
+
746
+ return $matches;
747
+ }
748
+
749
+ /**
750
+ * Add the heading `id` to the array.
751
+ *
752
+ * @see ezTOC_Post::extractHeadings()
753
+ *
754
+ * @access private
755
+ * @since 2.0
756
+ *
757
+ * @param array $matches The heading from the post content extracted with preg_match_all().
758
+ *
759
+ * @return mixed
760
+ */
761
+ private function headingIDs( &$matches ) {
762
+
763
+ //$count = count( $matches );
764
+
765
+ //for ( $i = 0; $i < $count; $i++ ) {
766
+ foreach ( $matches as $i => $match ) {
767
+
768
+ $matches[ $i ]['id'] = $this->generateHeadingIDFromTitle( $matches[ $i ][0] );
769
+ }
770
+
771
+ return $matches;
772
+ }
773
+
774
+ /**
775
+ * Create unique heading ID from heading string.
776
+ *
777
+ * @access private
778
+ * @since 2.0
779
+ *
780
+ * @param string $heading
781
+ *
782
+ * @return bool|string
783
+ */
784
+ private function generateHeadingIDFromTitle( $heading ) {
785
+
786
+ $return = false;
787
+
788
+ if ( $heading ) {
789
+
790
+ // WP entity encodes the post content.
791
+ $return = html_entity_decode( $heading, ENT_QUOTES, get_option( 'blog_charset' ) );
792
+ $return = br2( $return, ' ' );
793
+ $return = trim( strip_tags( $return ) );
794
+
795
+ // Convert accented characters to ASCII.
796
+ $return = remove_accents( $return );
797
+
798
+ // replace newlines with spaces (eg when headings are split over multiple lines)
799
+ $return = str_replace( array( "\r", "\n", "\n\r", "\r\n" ), ' ', $return );
800
+
801
+ // Remove `&amp;` and `&nbsp;` NOTE: in order to strip "hidden" `&nbsp;`,
802
+ // title needs to be converted to HTML entities.
803
+ // @link https://stackoverflow.com/a/21801444/5351316
804
+ $return = htmlentities2( $return );
805
+ $return = str_replace( array( '&amp;', '&nbsp;' ), ' ', $return );
806
+ $return = html_entity_decode( $return, ENT_QUOTES, get_option( 'blog_charset' ) );
807
+
808
+ // remove non alphanumeric chars
809
+ //$return = preg_replace( '/[^a-zA-Z0-9 \-_]*/', '', $return );
810
+ $return = preg_replace( '/[\x00-\x1F\x7F]*/u', '', $return );
811
+
812
+ // Reserved Characters.
813
+ // * ' ( ) ; : @ & = + $ , / ? # [ ]
814
+ $return = str_replace(
815
+ array( '*', '\'', '(', ')', ';', '@', '&', '=', '+', '$', ',', '/', '?', '#', '[', ']' ),
816
+ '',
817
+ $return
818
+ );
819
+
820
+ // Unsafe Characters.
821
+ // % { } | \ ^ ~ [ ] `
822
+ $return = str_replace(
823
+ array( '%', '{', '}', '|', '\\', '^', '~', '[', ']', '`' ),
824
+ '',
825
+ $return
826
+ );
827
+
828
+ // Special Characters.
829
+ // $ - _ . + ! * ' ( ) ,
830
+ $return = str_replace(
831
+ array( '$', '.', '+', '!', '*', '\'', '(', ')', ',' ),
832
+ '',
833
+ $return
834
+ );
835
+
836
+ // Dashes
837
+ // Special Characters.
838
+ // - (minus) - (dash) – (en dash) — (em dash)
839
+ $return = str_replace(
840
+ array( '-', '-', '–', '—' ),
841
+ '-',
842
+ $return
843
+ );
844
+
845
+ // Curley quotes.
846
+ // ‘ (curly single open quote) ’ (curly single close quote) “ (curly double open quote) ” (curly double close quote)
847
+ $return = str_replace(
848
+ array( '‘', '’', '“', '”' ),
849
+ '',
850
+ $return
851
+ );
852
+
853
+ // AMP/Caching plugins seems to break URL with the following characters, so lets replace them.
854
+ $return = str_replace( array( ':' ), '_', $return );
855
+
856
+ // Convert space characters to an `_` (underscore).
857
+ $return = preg_replace( '/\s+/', '_', $return );
858
+
859
+ // Replace multiple `-` (hyphen) with a single `-` (hyphen).
860
+ $return = preg_replace( '/-+/', '-', $return );
861
+
862
+ // Replace multiple `_` (underscore) with a single `_` (underscore).
863
+ $return = preg_replace( '/_+/', '_', $return );
864
+
865
+ // Remove trailing `-` (hyphen) and `_` (underscore).
866
+ $return = rtrim( $return, '-_' );
867
+
868
+ /*
869
+ * Encode URI based on ECMA-262.
870
+ *
871
+ * Only required to support the jQuery smoothScroll library.
872
+ *
873
+ * @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI#Description
874
+ * @link https://stackoverflow.com/a/19858404/5351316
875
+ */
876
+ $return = preg_replace_callback(
877
+ "{[^0-9a-z_.!~*'();,/?:@&=+$#-]}i",
878
+ function( $m ) {
879
+
880
+ return sprintf( '%%%02X', ord( $m[0] ) );
881
+ },
882
+ $return
883
+ );
884
+
885
+ // lowercase everything?
886
+ if ( ezTOC_Option::get( 'lowercase' ) ) {
887
+
888
+ $return = strtolower( $return );
889
+ }
890
+
891
+ // if blank, then prepend with the fragment prefix
892
+ // blank anchors normally appear on sites that don't use the latin charset
893
+ if ( ! $return ) {
894
+
895
+ $return = ( ezTOC_Option::get( 'fragment_prefix' ) ) ? ezTOC_Option::get( 'fragment_prefix' ) : '_';
896
+ }
897
+
898
+ // hyphenate?
899
+ if ( ezTOC_Option::get( 'hyphenate' ) ) {
900
+
901
+ $return = str_replace( '_', '-', $return );
902
+ $return = preg_replace( '/-+/', '-', $return );
903
+ }
904
+ }
905
+
906
+ if ( array_key_exists( $return, $this->collision_collector ) ) {
907
+
908
+ $this->collision_collector[ $return ]++;
909
+ $return .= '-' . $this->collision_collector[ $return ];
910
+
911
+ } else {
912
+
913
+ $this->collision_collector[ $return ] = 1;
914
+ }
915
+
916
+ return apply_filters( 'ez_toc_url_anchor_target', $return, $heading );
917
+ }
918
+
919
+ /**
920
+ * Remove any empty headings from the TOC.
921
+ *
922
+ * @access private
923
+ * @since 2.0
924
+ *
925
+ * @param array $matches The heading from the post content extracted with preg_match_all().
926
+ *
927
+ * @return array
928
+ */
929
+ private function removeEmptyHeadings( &$matches ) {
930
+
931
+ $new_matches = array();
932
+ //$count = count( $matches );
933
+
934
+ //for ( $i = 0; $i < $count; $i ++ ) {
935
+ foreach ( $matches as $i => $match ) {
936
+
937
+ if ( trim( strip_tags( $matches[ $i ][0] ) ) != false ) {
938
+
939
+ $new_matches[ $i ] = $matches[ $i ];
940
+ }
941
+ }
942
+
943
+ //if ( count( $matches ) != count( $new_matches ) ) {
944
+
945
+ $matches = $new_matches;
946
+ //}
947
+
948
+ return $matches;
949
+ }
950
+
951
+ /**
952
+ * Whether or not the post has TOC items.
953
+ *
954
+ * @see ezTOC_Post::extractHeadings()
955
+ *
956
+ * @access public
957
+ * @since 2.0
958
+ *
959
+ * @return bool
960
+ */
961
+ public function hasTOCItems() {
962
+
963
+ return $this->hasTOCItems;
964
+ }
965
+
966
+ /**
967
+ * Get the headings of the current page of the post.
968
+ *
969
+ * @access public
970
+ * @since 2.0
971
+ *
972
+ * @param int|null $page
973
+ *
974
+ * @return array
975
+ */
976
+ public function getHeadings( $page = null ) {
977
+
978
+ $headings = array();
979
+
980
+ if ( is_null( $page ) ) {
981
+
982
+ $page = $this->getCurrentPage();
983
+ }
984
+
985
+ if ( isset( $this->pages[ $page ] ) ) {
986
+
987
+ //$headings = wp_list_pluck( $this->pages[ $page ]['headings'], 0 );
988
+
989
+ $matches = $this->pages[ $page ]['headings'];
990
+ //$count = count( $matches );
991
+
992
+ //for ( $i = 0; $i < $count; $i++ ) {
993
+ foreach ( $matches as $i => $match ) {
994
+
995
+ //$anchor = $matches[ $i ]['id'];
996
+ $headings[] = str_replace(
997
+ array(
998
+ $matches[ $i ][1], // start of heading
999
+ '</h' . $matches[ $i ][2] . '>' // end of heading
1000
+ ),
1001
+ array(
1002
+ '>',
1003
+ '</h' . $matches[ $i ][2] . '>'
1004
+ ),
1005
+ $matches[ $i ][0]
1006
+ );
1007
+ }
1008
+ }
1009
+
1010
+ return $headings;
1011
+ }
1012
+
1013
+ /**
1014
+ * Get the heading with in page anchors of the current page of the post.
1015
+ *
1016
+ * @access public
1017
+ * @since 2.0
1018
+ *
1019
+ * @param int|null $page
1020
+ *
1021
+ * @return array
1022
+ */
1023
+ public function getHeadingsWithAnchors( $page = null ) {
1024
+
1025
+ $headings = array();
1026
+
1027
+ if ( is_null( $page ) ) {
1028
+
1029
+ $page = $this->getCurrentPage();
1030
+ }
1031
+
1032
+ if ( isset( $this->pages[ $page ] ) ) {
1033
+
1034
+ $matches = $this->pages[ $page ]['headings'];
1035
+ //$count = count( $matches );
1036
+
1037
+ //for ( $i = 0; $i < $count; $i++ ) {
1038
+ foreach ( $matches as $i => $match ) {
1039
+
1040
+ $anchor = $matches[ $i ]['id'];
1041
+ $headings[] = str_replace(
1042
+ array(
1043
+ $matches[ $i ][1], // start of heading
1044
+ '</h' . $matches[ $i ][2] . '>' // end of heading
1045
+ ),
1046
+ array(
1047
+ '><span class="ez-toc-section" id="' . $anchor . '"></span>',
1048
+ '<span class="ez-toc-section-end"></span></h' . $matches[ $i ][2] . '>'
1049
+ ),
1050
+ $matches[ $i ][0]
1051
+ );
1052
+ }
1053
+ }
1054
+
1055
+ return $headings;
1056
+ }
1057
+
1058
+ /**
1059
+ * Get the post TOC list.
1060
+ *
1061
+ * @access public
1062
+ * @since 2.0
1063
+ *
1064
+ * @return string
1065
+ */
1066
+ public function getTOCList() {
1067
+
1068
+ $html = '';
1069
+
1070
+ if ( $this->hasTOCItems ) {
1071
+
1072
+ foreach ( $this->pages as $page => $attribute ) {
1073
+
1074
+ $html .= $this->createTOC( $page, $attribute['headings'] );
1075
+ }
1076
+
1077
+ $html = '<ul class="ez-toc-list ez-toc-list-level-1">' . $html . '</ul>';
1078
+ }
1079
+
1080
+ return $html;
1081
+ }
1082
+
1083
+ /**
1084
+ * Get the post TOC content block.
1085
+ *
1086
+ * @access public
1087
+ * @since 2.0
1088
+ *
1089
+ * @return string
1090
+ */
1091
+ public function getTOC() {
1092
+
1093
+ $class = array( 'ez-toc-v' . str_replace( '.', '_', ezTOC::VERSION ) );
1094
+ $html = '';
1095
+
1096
+ if ( $this->hasTOCItems() ) {
1097
+
1098
+ // wrapping css classes
1099
+ switch ( ezTOC_Option::get( 'wrapping' ) ) {
1100
+
1101
+ case 'left':
1102
+ $class[] = 'ez-toc-wrap-left';
1103
+ break;
1104
+
1105
+ case 'right':
1106
+ $class[] = 'ez-toc-wrap-right';
1107
+ break;
1108
+
1109
+ case 'none':
1110
+ default:
1111
+ // do nothing
1112
+ }
1113
+
1114
+ if ( ezTOC_Option::get( 'show_hierarchy' ) ) {
1115
+
1116
+ $class[] = 'counter-hierarchy';
1117
+
1118
+ } else {
1119
+
1120
+ $class[] .= 'counter-flat';
1121
+ }
1122
+
1123
+ switch ( ezTOC_Option::get( 'counter' ) ) {
1124
+
1125
+ case 'numeric':
1126
+ $class[] .= 'counter-numeric';
1127
+ break;
1128
+
1129
+ case 'roman':
1130
+ $class[] = 'counter-roman';
1131
+ break;
1132
+
1133
+ case 'decimal':
1134
+ $class[] = 'counter-decimal';
1135
+ break;
1136
+ }
1137
+
1138
+ // colour themes
1139
+ switch ( ezTOC_Option::get( 'theme' ) ) {
1140
+
1141
+ case 'light-blue':
1142
+ $class[] = 'ez-toc-light-blue';
1143
+ break;
1144
+
1145
+ case 'white':
1146
+ $class[] = 'ez-toc-white';
1147
+ break;
1148
+
1149
+ case 'black':
1150
+ $class[] = 'ez-toc-black';
1151
+ break;
1152
+
1153
+ case 'transparent':
1154
+ $class[] .= 'ez-toc-transparent';
1155
+ break;
1156
+
1157
+ case 'grey':
1158
+ $class[] = 'ez-toc-grey';
1159
+ break;
1160
+ }
1161
+
1162
+ $custom_classes = ezTOC_Option::get( 'css_container_class', '' );
1163
+
1164
+ if ( 0 < strlen( $custom_classes ) ) {
1165
+
1166
+ $custom_classes = explode( ' ', $custom_classes );
1167
+ $custom_classes = apply_filters( 'ez_toc_container_class', $custom_classes, $this );
1168
+
1169
+ if ( is_array( $custom_classes ) ) {
1170
+
1171
+ $class = array_merge( $class, $custom_classes );
1172
+ }
1173
+ }
1174
+
1175
+ $class = array_filter( $class );
1176
+ $class = array_map( 'trim', $class );
1177
+ $class = array_map( 'sanitize_html_class', $class );
1178
+
1179
+ $html .= '<div id="ez-toc-container" class="' . implode( ' ', $class ) . '">' . PHP_EOL;
1180
+
1181
+ if ( ezTOC_Option::get( 'show_heading_text' ) ) {
1182
+
1183
+ $toc_title = ezTOC_Option::get( 'heading_text' );
1184
+
1185
+ if ( strpos( $toc_title, '%PAGE_TITLE%' ) !== false ) {
1186
+
1187
+ $toc_title = str_replace( '%PAGE_TITLE%', get_the_title(), $toc_title );
1188
+ }
1189
+
1190
+ if ( strpos( $toc_title, '%PAGE_NAME%' ) !== false ) {
1191
+
1192
+ $toc_title = str_replace( '%PAGE_NAME%', get_the_title(), $toc_title );
1193
+ }
1194
+
1195
+ if (ezTOC_Option::get( 'toc_loading' ) != 'css') {
1196
+ $html .= '<div class="ez-toc-title-container">' . PHP_EOL;
1197
+ }
1198
+
1199
+ $html .= '<p class="ez-toc-title">' . esc_html__( htmlentities( $toc_title, ENT_COMPAT, 'UTF-8' ), 'easy-table-of-contents' ). '</p>' . PHP_EOL;
1200
+
1201
+ if (ezTOC_Option::get( 'toc_loading' ) != 'css') {
1202
+ $html .= '<span class="ez-toc-title-toggle">';
1203
+ }
1204
+
1205
+ if ( ezTOC_Option::get( 'visibility' ) ) {
1206
+ if (ezTOC_Option::get( 'toc_loading' ) != 'css') {
1207
+ $html .= '<a class="ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle" style="display: none;"><i class="ez-toc-glyphicon ez-toc-icon-toggle"></i></a>';
1208
+ }else{
1209
+ $html .= '<label for="item"><i class="ez-toc-glyphicon ez-toc-icon-toggle"></i></label><input type="checkbox" id="item">';
1210
+ }
1211
+ }
1212
+
1213
+ if (ezTOC_Option::get( 'toc_loading' ) != 'css') {
1214
+ $html .= '</span>';
1215
+ }
1216
+ if (ezTOC_Option::get( 'toc_loading' ) != 'css') {
1217
+ $html .= '</div>' . PHP_EOL;
1218
+ }
1219
+ }
1220
+
1221
+ ob_start();
1222
+ do_action( 'ez_toc_before' );
1223
+ $html .= ob_get_clean();
1224
+
1225
+ $html .= '<nav>' . $this->getTOCList() . '</nav>';
1226
+
1227
+ ob_start();
1228
+ do_action( 'ez_toc_after' );
1229
+ $html .= ob_get_clean();
1230
+
1231
+ $html .= '</div>' . PHP_EOL;
1232
+
1233
+ // Enqueue the script.
1234
+ wp_enqueue_script( 'ez-toc-js' );
1235
+ }
1236
+
1237
+ return $html;
1238
+ }
1239
+
1240
+ /**
1241
+ * Displays the post's TOC.
1242
+ *
1243
+ * @access public
1244
+ * @since 2.0
1245
+ */
1246
+ public function toc() {
1247
+
1248
+ echo $this->getTOC();
1249
+ }
1250
+
1251
+ /**
1252
+ * Generate the TOC list items for a given page within a post.
1253
+ *
1254
+ * @access private
1255
+ * @since 2.0
1256
+ *
1257
+ * @param int $page The page of the post to create the TOC items for.
1258
+ * @param array $matches The heading from the post content extracted with preg_match_all().
1259
+ *
1260
+ * @return string The HTML list of TOC items.
1261
+ */
1262
+ private function createTOC( $page, $matches ) {
1263
+
1264
+ // Whether or not the TOC should be built flat or hierarchical.
1265
+ $hierarchical = ezTOC_Option::get( 'show_hierarchy' );
1266
+ $html = '';
1267
+
1268
+ if ( $hierarchical ) {
1269
+
1270
+ $current_depth = 100; // headings can't be larger than h6 but 100 as a default to be sure
1271
+ $numbered_items = array();
1272
+ $numbered_items_min = null;
1273
+
1274
+ // reset the internal collision collection
1275
+ /** @todo does this need to be used??? */
1276
+ //self::$collision_collector = array();
1277
+
1278
+ // find the minimum heading to establish our baseline
1279
+ //for ( $i = 0; $i < count( $matches ); $i ++ ) {
1280
+ foreach ( $matches as $i => $match ) {
1281
+ if ( $current_depth > $matches[ $i ][2] ) {
1282
+ $current_depth = (int) $matches[ $i ][2];
1283
+ }
1284
+ }
1285
+
1286
+ $numbered_items[ $current_depth ] = 0;
1287
+ $numbered_items_min = $current_depth;
1288
+
1289
+ //for ( $i = 0; $i < count( $matches ); $i ++ ) {
1290
+ foreach ( $matches as $i => $match ) {
1291
+
1292
+ $level = $matches[ $i ][2];
1293
+ $count = $i + 1;
1294
+
1295
+ if ( $current_depth == (int) $matches[ $i ][2] ) {
1296
+
1297
+ $html .= '<li class="ez-toc-page-' . $page . ' ez-toc-heading-level-' . $current_depth . '">';
1298
+ }
1299
+
1300
+ // start lists
1301
+ if ( $current_depth != (int) $matches[ $i ][2] ) {
1302
+
1303
+ for ( $current_depth; $current_depth < (int) $matches[ $i ][2]; $current_depth++ ) {
1304
+
1305
+ $numbered_items[ $current_depth + 1 ] = 0;
1306
+ $html .= '<ul class="ez-toc-list-level-' . $level . '"><li class="ez-toc-heading-level-' . $level . '">';
1307
+ }
1308
+ }
1309
+
1310
+ $title = isset( $matches[ $i ]['alternate'] ) ? $matches[ $i ]['alternate'] : $matches[ $i ][0];
1311
+ $title = br2( $title, ' ' );
1312
+ $title = strip_tags( apply_filters( 'ez_toc_title', $title ), apply_filters( 'ez_toc_title_allowable_tags', '' ) );
1313
+
1314
+ $html .= $this->createTOCItemAnchor( $page, $matches[ $i ]['id'], $title, $count );
1315
+
1316
+ // end lists
1317
+ if ( $i != count( $matches ) - 1 ) {
1318
+
1319
+ if ( $current_depth > (int) $matches[ $i + 1 ][2] ) {
1320
+
1321
+ for ( $current_depth; $current_depth > (int) $matches[ $i + 1 ][2]; $current_depth-- ) {
1322
+
1323
+ $html .= '</li></ul>';
1324
+ $numbered_items[ $current_depth ] = 0;
1325
+ }
1326
+ }
1327
+
1328
+ if ( $current_depth == (int) @$matches[ $i + 1 ][2] ) {
1329
+
1330
+ $html .= '</li>';
1331
+ }
1332
+
1333
+ } else {
1334
+
1335
+ // this is the last item, make sure we close off all tags
1336
+ for ( $current_depth; $current_depth >= $numbered_items_min; $current_depth-- ) {
1337
+
1338
+ $html .= '</li>';
1339
+
1340
+ if ( $current_depth != $numbered_items_min ) {
1341
+ $html .= '</ul>';
1342
+ }
1343
+ }
1344
+ }
1345
+ }
1346
+
1347
+ } else {
1348
+
1349
+ //for ( $i = 0; $i < count( $matches ); $i++ ) {
1350
+ foreach ( $matches as $i => $match ) {
1351
+
1352
+ $count = $i + 1;
1353
+
1354
+ $title = isset( $matches[ $i ]['alternate'] ) ? $matches[ $i ]['alternate'] : $matches[ $i ][0];
1355
+ $title = strip_tags( apply_filters( 'ez_toc_title', $title ), apply_filters( 'ez_toc_title_allowable_tags', '' ) );
1356
+
1357
+ $html .= '<li class="ez-toc-page-' . $page . '">';
1358
+
1359
+ $html .= $this->createTOCItemAnchor( $page, $matches[ $i ]['id'], $title, $count );
1360
+
1361
+ $html .= '</li>';
1362
+ }
1363
+ }
1364
+
1365
+ return $html;
1366
+ }
1367
+
1368
+ /**
1369
+ * @access private
1370
+ * @since 2.0
1371
+ *
1372
+ * @param int $page
1373
+ * @param string $id
1374
+ * @param string $title
1375
+ * @param int $count
1376
+ *
1377
+ * @return string
1378
+ */
1379
+ private function createTOCItemAnchor( $page, $id, $title, $count ) {
1380
+
1381
+ return sprintf(
1382
+ '<a class="ez-toc-link ez-toc-heading-' . $count . '" href="%1$s" title="%2$s">%3$s</a>',
1383
+ esc_attr( $this->createTOCItemURL( $id, $page ) ),
1384
+ esc_attr( strip_tags( $title ) ),
1385
+ $title
1386
+ );
1387
+ }
1388
+
1389
+ /**
1390
+ * @access private
1391
+ * @since 2.0
1392
+ *
1393
+ * @param string $id
1394
+ * @param int $page
1395
+ *
1396
+ * @return string
1397
+ */
1398
+ private function createTOCItemURL( $id, $page ) {
1399
+
1400
+ $current_post = $this->post->ID === $this->queriedObjectID;
1401
+ $current_page = $this->getCurrentPage();
1402
+
1403
+ if ( $page === $current_page && $current_post ) {
1404
+
1405
+ return '#' . $id;
1406
+
1407
+ } elseif ( 1 === $page ) {
1408
+
1409
+ return trailingslashit( $this->permalink ) . '#' . $id;
1410
+
1411
+ }
1412
+
1413
+ return trailingslashit( $this->permalink ) . $page . '/#' . $id;
1414
+ }
1415
+ }
includes/inc.admin-options-page.php CHANGED
@@ -1,92 +1,92 @@
1
- <div id='toc' class='wrap'>
2
- <h1><?php _e( 'Table of Contents', 'easy-table-of-contents' ); ?></h1>
3
- <div class="toc-tab-panel">
4
- <a id="eztoc-default" class="eztoc-tablinks" data-href="no" href="#general-settings" onclick="tabToggle(event, 'general')">General</a>
5
- <a class="eztoc-tablinks" id="eztoc-technical" href="#technical-support" onclick="tabToggle(event, 'technical')" data-href="no">Technical Support</a>
6
- </div><!-- /.Tab panel -->
7
-
8
- <div class="eztoc-tabcontent" id="general">
9
- <form method="post" action="<?php echo esc_url( self_admin_url( 'options.php' ) ); ?>">
10
-
11
- <div class="metabox-holder">
12
-
13
- <div class="postbox" id="eztoc-general">
14
- <h3><span><?php _e( 'General', 'easy-table-of-contents' ); ?></span></h3>
15
-
16
- <div class="inside">
17
-
18
- <table class="form-table">
19
-
20
- <?php do_settings_fields( 'ez_toc_settings_general', 'ez_toc_settings_general' ); ?>
21
-
22
- </table>
23
-
24
- </div><!-- /.inside -->
25
- </div><!-- /.postbox -->
26
-
27
- </div><!-- /.metabox-holder -->
28
-
29
- <div class="metabox-holder">
30
-
31
- <div class="postbox" id="eztoc-appearance">
32
- <h3><span><?php _e( 'Appearance', 'easy-table-of-contents' ); ?></span></h3>
33
-
34
- <div class="inside">
35
-
36
- <table class="form-table">
37
-
38
- <?php do_settings_fields( 'ez_toc_settings_appearance', 'ez_toc_settings_appearance' ); ?>
39
-
40
- </table>
41
-
42
- </div><!-- /.inside -->
43
- </div><!-- /.postbox -->
44
-
45
- </div><!-- /.metabox-holder -->
46
-
47
- <div class="metabox-holder">
48
-
49
- <div class="postbox" id="eztoc-advanced">
50
- <h3><span><?php _e( 'Advanced', 'easy-table-of-contents' ); ?></span></h3>
51
-
52
- <div class="inside">
53
-
54
- <table class="form-table">
55
-
56
- <?php do_settings_fields( 'ez_toc_settings_advanced', 'ez_toc_settings_advanced' ); ?>
57
-
58
- </table>
59
-
60
- </div><!-- /.inside -->
61
- </div><!-- /.postbox -->
62
-
63
- </div><!-- /.metabox-holder -->
64
-
65
- <?php settings_fields( 'ez-toc-settings' ); ?>
66
- <?php submit_button( __( 'Save Changes', 'easy-table-of-contents' ) ); ?>
67
- </form>
68
- </div><!-- /.General Settings ended -->
69
-
70
- <div class="eztoc_support_div eztoc-tabcontent" id="technical">
71
- <strong><?php echo esc_html__('If you have any query, please write the query in below box or email us at','easy-table-of-contents') ?> <a href="mailto:support@magazine3.in">support@magazine3.in</a> <?php echo esc_html__('we will reply to your email address shortly.','easy-table-of-contents') ?></strong>
72
-
73
- <ul>
74
- <li>
75
- <label class="support-label">Email<span class="star-mark">*</span></label>
76
- <div class="support-input">
77
- <input type="text" id="eztoc_query_email" name="eztoc_query_email" placeholder="email" required>
78
- </div>
79
- </li>
80
- <li>
81
- <label class="support-label">Query<span class="star-mark">*</span></label>
82
- <div class="support-input"><textarea rows="5" cols="60" id="eztoc_query_message" name="eztoc_query_message" placeholder="Write your query"></textarea>
83
- </div>
84
- <div class="clear"> </div>
85
- <span class="eztoc-query-success eztoc-result eztoc_hide"><?php echo esc_html__('Message sent successfully, Please wait we will get back to you shortly','easy-table-of-contents'); ?></span>
86
- <span class="eztoc-query-error eztoc-result eztoc_hide"><?php echo esc_html__('Message not sent. please check your network connection','easy-table-of-contents'); ?></span>
87
- </li>
88
- <li><button class="button eztoc-send-query"><?php echo esc_html__('Send Message','easy-table-of-contents'); ?></button></li>
89
- </ul>
90
-
91
- </div><!-- /.Technical support div ended -->
92
- </div>
1
+ <div id='toc' class='wrap'>
2
+ <h1><?php _e( 'Table of Contents', 'easy-table-of-contents' ); ?></h1>
3
+ <div class="toc-tab-panel">
4
+ <a id="eztoc-default" class="eztoc-tablinks" data-href="no" href="#general-settings" onclick="tabToggle(event, 'general')">General</a>
5
+ <a class="eztoc-tablinks" id="eztoc-technical" href="#technical-support" onclick="tabToggle(event, 'technical')" data-href="no">Technical Support</a>
6
+ </div><!-- /.Tab panel -->
7
+
8
+ <div class="eztoc-tabcontent" id="general">
9
+ <form method="post" action="<?php echo esc_url( self_admin_url( 'options.php' ) ); ?>">
10
+
11
+ <div class="metabox-holder">
12
+
13
+ <div class="postbox" id="eztoc-general">
14
+ <h3><span><?php _e( 'General', 'easy-table-of-contents' ); ?></span></h3>
15
+
16
+ <div class="inside">
17
+
18
+ <table class="form-table">
19
+
20
+ <?php do_settings_fields( 'ez_toc_settings_general', 'ez_toc_settings_general' ); ?>
21
+
22
+ </table>
23
+
24
+ </div><!-- /.inside -->
25
+ </div><!-- /.postbox -->
26
+
27
+ </div><!-- /.metabox-holder -->
28
+
29
+ <div class="metabox-holder">
30
+
31
+ <div class="postbox" id="eztoc-appearance">
32
+ <h3><span><?php _e( 'Appearance', 'easy-table-of-contents' ); ?></span></h3>
33
+
34
+ <div class="inside">
35
+
36
+ <table class="form-table">
37
+
38
+ <?php do_settings_fields( 'ez_toc_settings_appearance', 'ez_toc_settings_appearance' ); ?>
39
+
40
+ </table>
41
+
42
+ </div><!-- /.inside -->
43
+ </div><!-- /.postbox -->
44
+
45
+ </div><!-- /.metabox-holder -->
46
+
47
+ <div class="metabox-holder">
48
+
49
+ <div class="postbox" id="eztoc-advanced">
50
+ <h3><span><?php _e( 'Advanced', 'easy-table-of-contents' ); ?></span></h3>
51
+
52
+ <div class="inside">
53
+
54
+ <table class="form-table">
55
+
56
+ <?php do_settings_fields( 'ez_toc_settings_advanced', 'ez_toc_settings_advanced' ); ?>
57
+
58
+ </table>
59
+
60
+ </div><!-- /.inside -->
61
+ </div><!-- /.postbox -->
62
+
63
+ </div><!-- /.metabox-holder -->
64
+
65
+ <?php settings_fields( 'ez-toc-settings' ); ?>
66
+ <?php submit_button( __( 'Save Changes', 'easy-table-of-contents' ) ); ?>
67
+ </form>
68
+ </div><!-- /.General Settings ended -->
69
+
70
+ <div class="eztoc_support_div eztoc-tabcontent" id="technical">
71
+ <strong><?php echo esc_html__('If you have any query, please write the query in below box or email us at','easy-table-of-contents') ?> <a href="mailto:team@magazine3.in">team@magazine3.in</a> <?php echo esc_html__('we will reply to your email address shortly.','easy-table-of-contents') ?></strong>
72
+
73
+ <ul>
74
+ <li>
75
+ <label class="support-label">Email<span class="star-mark">*</span></label>
76
+ <div class="support-input">
77
+ <input type="text" id="eztoc_query_email" name="eztoc_query_email" placeholder="email" required>
78
+ </div>
79
+ </li>
80
+ <li>
81
+ <label class="support-label">Query<span class="star-mark">*</span></label>
82
+ <div class="support-input"><textarea rows="5" cols="60" id="eztoc_query_message" name="eztoc_query_message" placeholder="Write your query"></textarea>
83
+ </div>
84
+ <div class="clear"> </div>
85
+ <span class="eztoc-query-success eztoc-result eztoc_hide"><?php echo esc_html__('Message sent successfully, Please wait we will get back to you shortly','easy-table-of-contents'); ?></span>
86
+ <span class="eztoc-query-error eztoc-result eztoc_hide"><?php echo esc_html__('Message not sent. please check your network connection','easy-table-of-contents'); ?></span>
87
+ </li>
88
+ <li><button class="button eztoc-send-query"><?php echo esc_html__('Send Message','easy-table-of-contents'); ?></button></li>
89
+ </ul>
90
+
91
+ </div><!-- /.Technical support div ended -->
92
+ </div>
includes/inc.string-functions.php CHANGED
@@ -1,344 +1,344 @@
1
- <?php
2
-
3
- namespace Easy_Plugins\Table_Of_Contents\String;
4
-
5
- /**
6
- * Replace `<br />` tags with parameter.
7
- *
8
- * @since 2.0.8
9
- *
10
- * @param string $string
11
- * @param string $to
12
- *
13
- * @return string
14
- */
15
- function br2( $string, $to = "\r\n" ) {
16
-
17
- $string = preg_replace( '`<br[/\s]*>`i', $to, $string );
18
-
19
- return $string;
20
- }
21
-
22
- /**
23
- * Replace `<br />` tags with new lines.
24
- *
25
- * @link https://stackoverflow.com/a/27509016/5351316
26
- *
27
- * @since 2.0.8
28
- *
29
- * @param string $string
30
- *
31
- * @return string
32
- */
33
- function br2nl( $string ) {
34
-
35
- return br2( $string );
36
- }
37
-
38
- /**
39
- * Pulled from WordPress formatting functions.
40
- *
41
- * Edited to add space before self closing tags.
42
- *
43
- * @since 2.0
44
- *
45
- * @param string $text
46
- *
47
- * @return string|string[]
48
- */
49
- function force_balance_tags( $text ) {
50
- $tagstack = array();
51
- $stacksize = 0;
52
- $tagqueue = '';
53
- $newtext = '';
54
- // Known single-entity/self-closing tags
55
- $single_tags = array( 'area', 'base', 'basefont', 'br', 'col', 'command', 'embed', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param', 'source' );
56
- // Tags that can be immediately nested within themselves
57
- $nestable_tags = array( 'blockquote', 'div', 'object', 'q', 'span' );
58
-
59
- // WP bug fix for comments - in case you REALLY meant to type '< !--'
60
- $text = str_replace( '< !--', '< !--', $text );
61
- // WP bug fix for LOVE <3 (and other situations with '<' before a number)
62
- $text = preg_replace( '#<([0-9]{1})#', '&lt;$1', $text );
63
-
64
- /**
65
- * Matches supported tags.
66
- *
67
- * To get the pattern as a string without the comments paste into a PHP
68
- * REPL like `php -a`.
69
- *
70
- * @see https://html.spec.whatwg.org/#elements-2
71
- * @see https://w3c.github.io/webcomponents/spec/custom/#valid-custom-element-name
72
- *
73
- * @example
74
- * ~# php -a
75
- * php > $s = [paste copied contents of expression below including parentheses];
76
- * php > echo $s;
77
- */
78
- $tag_pattern = (
79
- '#<' . // Start with an opening bracket.
80
- '(/?)' . // Group 1 - If it's a closing tag it'll have a leading slash.
81
- '(' . // Group 2 - Tag name.
82
- // Custom element tags have more lenient rules than HTML tag names.
83
- '(?:[a-z](?:[a-z0-9._]*)-(?:[a-z0-9._-]+)+)' .
84
- '|' .
85
- // Traditional tag rules approximate HTML tag names.
86
- '(?:[\w:]+)' .
87
- ')' .
88
- '(?:' .
89
- // We either immediately close the tag with its '>' and have nothing here.
90
- '\s*' .
91
- '(/?)' . // Group 3 - "attributes" for empty tag.
92
- '|' .
93
- // Or we must start with space characters to separate the tag name from the attributes (or whitespace).
94
- '(\s+)' . // Group 4 - Pre-attribute whitespace.
95
- '([^>]*)' . // Group 5 - Attributes.
96
- ')' .
97
- '>#' // End with a closing bracket.
98
- );
99
-
100
- while ( preg_match( $tag_pattern, $text, $regex ) ) {
101
- $full_match = $regex[0];
102
- $has_leading_slash = ! empty( $regex[1] );
103
- $tag_name = $regex[2];
104
- $tag = strtolower( $tag_name );
105
- $is_single_tag = in_array( $tag, $single_tags, true );
106
- $pre_attribute_ws = isset( $regex[4] ) ? $regex[4] : '';
107
- $attributes = trim( isset( $regex[5] ) ? $regex[5] : $regex[3] );
108
- $has_self_closer = '/' === substr( $attributes, -1 );
109
-
110
- $newtext .= $tagqueue;
111
-
112
- $i = strpos( $text, $full_match );
113
- $l = strlen( $full_match );
114
-
115
- // Clear the shifter.
116
- $tagqueue = '';
117
- if ( $has_leading_slash ) { // End Tag.
118
- // If too many closing tags.
119
- if ( $stacksize <= 0 ) {
120
- $tag = '';
121
- // Or close to be safe $tag = '/' . $tag.
122
-
123
- // If stacktop value = tag close value, then pop.
124
- } elseif ( $tagstack[ $stacksize - 1 ] === $tag ) { // Found closing tag.
125
- $tag = '</' . $tag . '>'; // Close Tag.
126
- array_pop( $tagstack );
127
- $stacksize--;
128
- } else { // Closing tag not at top, search for it.
129
- for ( $j = $stacksize - 1; $j >= 0; $j-- ) {
130
- if ( $tagstack[ $j ] === $tag ) {
131
- // Add tag to tagqueue.
132
- for ( $k = $stacksize - 1; $k >= $j; $k-- ) {
133
- $tagqueue .= '</' . array_pop( $tagstack ) . '>';
134
- $stacksize--;
135
- }
136
- break;
137
- }
138
- }
139
- $tag = '';
140
- }
141
- } else { // Begin Tag.
142
- if ( $has_self_closer ) { // If it presents itself as a self-closing tag...
143
- // ...but it isn't a known single-entity self-closing tag, then don't let it be treated as such and
144
- // immediately close it with a closing tag (the tag will encapsulate no text as a result)
145
- if ( ! $is_single_tag ) {
146
- $attributes = trim( substr( $attributes, 0, -1 ) ) . "></$tag";
147
- }
148
- } elseif ( $is_single_tag ) { // ElseIf it's a known single-entity tag but it doesn't close itself, do so
149
- $pre_attribute_ws = ' ';
150
- $attributes .= 0 < strlen( $attributes ) ? ' /' : '/'; // EDIT: If there are attributes, add space before closing tag to match how WP insert br, hr and img tags.
151
- } else { // It's not a single-entity tag.
152
- // If the top of the stack is the same as the tag we want to push, close previous tag.
153
- if ( $stacksize > 0 && ! in_array( $tag, $nestable_tags, true ) && $tagstack[ $stacksize - 1 ] === $tag ) {
154
- $tagqueue = '</' . array_pop( $tagstack ) . '>';
155
- $stacksize--;
156
- }
157
- $stacksize = array_push( $tagstack, $tag );
158
- }
159
-
160
- // Attributes.
161
- if ( $has_self_closer && $is_single_tag ) {
162
- // We need some space - avoid <br/> and prefer <br />.
163
- $pre_attribute_ws = ' ';
164
- }
165
-
166
- $tag = '<' . $tag . $pre_attribute_ws . $attributes . '>';
167
- // If already queuing a close tag, then put this tag on too.
168
- if ( ! empty( $tagqueue ) ) {
169
- $tagqueue .= $tag;
170
- $tag = '';
171
- }
172
- }
173
- $newtext .= substr( $text, 0, $i ) . $tag;
174
- $text = substr( $text, $i + $l );
175
- }
176
-
177
- // Clear Tag Queue.
178
- $newtext .= $tagqueue;
179
-
180
- // Add remaining text.
181
- $newtext .= $text;
182
-
183
- while ( $x = array_pop( $tagstack ) ) {
184
- $newtext .= '</' . $x . '>'; // Add remaining tags to close.
185
- }
186
-
187
- // WP fix for the bug with HTML comments.
188
- $newtext = str_replace( '< !--', '<!--', $newtext );
189
- $newtext = str_replace( '< !--', '< !--', $newtext );
190
-
191
- return $newtext;
192
- }
193
-
194
- /**
195
- * Multibyte substr_replace(). The mbstring library does not come with a multibyte equivalent of substr_replace().
196
- * This function behaves exactly like substr_replace() even when the arguments are arrays.
197
- *
198
- * @link https://gist.github.com/stemar/8287074
199
- *
200
- * @since 2.0
201
- *
202
- * @param $string
203
- * @param $replacement
204
- * @param $start
205
- * @param null $length
206
- *
207
- * @return array|string
208
- */
209
- function mb_substr_replace( $string, $replacement, $start, $length = null ) {
210
-
211
- if ( is_array( $string ) ) {
212
-
213
- $num = count( $string );
214
-
215
- // $replacement
216
- $replacement = is_array( $replacement ) ? array_slice( $replacement, 0, $num ) : array_pad( array( $replacement ), $num, $replacement );
217
-
218
- // $start
219
- if ( is_array( $start ) ) {
220
- $start = array_slice( $start, 0, $num );
221
- foreach ( $start as $key => $value ) {
222
- $start[ $key ] = is_int( $value ) ? $value : 0;
223
- }
224
- } else {
225
- $start = array_pad( array( $start ), $num, $start );
226
- }
227
-
228
- // $length
229
- if ( ! isset( $length ) ) {
230
- $length = array_fill( 0, $num, 0 );
231
- } elseif ( is_array( $length ) ) {
232
- $length = array_slice( $length, 0, $num );
233
- foreach ( $length as $key => $value ) {
234
- $length[ $key ] = isset( $value ) ? ( is_int( $value ) ? $value : $num ) : 0;
235
- }
236
- } else {
237
- $length = array_pad( array( $length ), $num, $length );
238
- }
239
-
240
- // Recursive call
241
- return array_map( __FUNCTION__, $string, $replacement, $start, $length );
242
- }
243
-
244
- preg_match_all( '/./us', (string) $string, $smatches );
245
- preg_match_all( '/./us', (string) $replacement, $rmatches );
246
-
247
- if ( $length === null ) {
248
-
249
- $length = mb_strlen( $string );
250
- }
251
-
252
- array_splice( $smatches[0], $start, $length, $rmatches[0] );
253
-
254
- return join( $smatches[0] );
255
- }
256
-
257
- /**
258
- * Returns a string with all items from the $find array replaced with their matching
259
- * items in the $replace array. This does a one to one replacement (rather than globally).
260
- *
261
- * This function is multibyte safe.
262
- *
263
- * $find and $replace are arrays, $string is the haystack. All variables are passed by reference.
264
- *
265
- * @since 1.0
266
- *
267
- * @param bool $find
268
- * @param bool $replace
269
- * @param string $string
270
- *
271
- * @return mixed|string
272
- */
273
- function mb_find_replace( &$find = false, &$replace = false, &$string = '' ) {
274
-
275
- if ( is_array( $find ) && is_array( $replace ) && $string ) {
276
-
277
- // check if multibyte strings are supported
278
- if ( function_exists( 'mb_strpos' ) ) {
279
-
280
- //for ( $i = 0; $i < count( $find ); $i ++ ) {
281
- //
282
- // $string = mb_substr(
283
- // $string,
284
- // 0,
285
- // mb_strpos( $string, $find[ $i ] )
286
- // ) . // everything before $find
287
- // $replace[ $i ] . // its replacement
288
- // mb_substr(
289
- // $string,
290
- // mb_strpos( $string, $find[ $i ] ) + mb_strlen( $find[ $i ] )
291
- // ) // everything after $find
292
- // ;
293
- //}
294
-
295
- for ( $i = 0; $i < count( $find ); $i ++ ) {
296
-
297
- $needle = $find[ $i ];
298
- $start = mb_strpos( $string, $needle );
299
-
300
- // If heading can not be found, let try decoding entities to see if it can be found.
301
- if ( false === $start ) {
302
-
303
- $needle = html_entity_decode(
304
- $needle,
305
- ENT_QUOTES,
306
- get_option( 'blog_charset' )
307
- );
308
-
309
- $needle = str_replace(array('’','“','”'), array('\'','"','"'), $needle);
310
-
311
- $start = mb_strpos( $string, $needle );
312
- }
313
-
314
- /*
315
- * `mb_strpos()` can return `false`. Only process `mb_substr_replace()` if position in string is found.
316
- */
317
- if ( is_int( $start ) ) {
318
-
319
- $length = mb_strlen( $needle );
320
- $string = mb_substr_replace( $string, $replace[ $i ], $start, $length );
321
- }
322
-
323
- }
324
-
325
- } else {
326
-
327
- for ( $i = 0; $i < count( $find ); $i ++ ) {
328
-
329
- $start = strpos( $string, $find[ $i ] );
330
- $length = strlen( $find[ $i ] );
331
-
332
- /*
333
- * `strpos()` can return `false`. Only process `substr_replace()` if position in string is found.
334
- */
335
- if ( is_int( $start ) ) {
336
-
337
- $string = substr_replace( $string, $replace[ $i ], $start, $length );
338
- }
339
- }
340
- }
341
- }
342
-
343
- return $string;
344
- }
1
+ <?php
2
+
3
+ namespace Easy_Plugins\Table_Of_Contents\String;
4
+
5
+ /**
6
+ * Replace `<br />` tags with parameter.
7
+ *
8
+ * @since 2.0.8
9
+ *
10
+ * @param string $string
11
+ * @param string $to
12
+ *
13
+ * @return string
14
+ */
15
+ function br2( $string, $to = "\r\n" ) {
16
+
17
+ $string = preg_replace( '`<br[/\s]*>`i', $to, $string );
18
+
19
+ return $string;
20
+ }
21
+
22
+ /**
23
+ * Replace `<br />` tags with new lines.
24
+ *
25
+ * @link https://stackoverflow.com/a/27509016/5351316
26
+ *
27
+ * @since 2.0.8
28
+ *
29
+ * @param string $string
30
+ *
31
+ * @return string
32
+ */
33
+ function br2nl( $string ) {
34
+
35
+ return br2( $string );
36
+ }
37
+
38
+ /**
39
+ * Pulled from WordPress formatting functions.
40
+ *
41
+ * Edited to add space before self closing tags.
42
+ *
43
+ * @since 2.0
44
+ *
45
+ * @param string $text
46
+ *
47
+ * @return string|string[]
48
+ */
49
+ function force_balance_tags( $text ) {
50
+ $tagstack = array();
51
+ $stacksize = 0;
52
+ $tagqueue = '';
53
+ $newtext = '';
54
+ // Known single-entity/self-closing tags
55
+ $single_tags = array( 'area', 'base', 'basefont', 'br', 'col', 'command', 'embed', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param', 'source' );
56
+ // Tags that can be immediately nested within themselves
57
+ $nestable_tags = array( 'blockquote', 'div', 'object', 'q', 'span' );
58
+
59
+ // WP bug fix for comments - in case you REALLY meant to type '< !--'
60
+ $text = str_replace( '< !--', '< !--', $text );
61
+ // WP bug fix for LOVE <3 (and other situations with '<' before a number)
62
+ $text = preg_replace( '#<([0-9]{1})#', '&lt;$1', $text );
63
+
64
+ /**
65
+ * Matches supported tags.
66
+ *
67
+ * To get the pattern as a string without the comments paste into a PHP
68
+ * REPL like `php -a`.
69
+ *
70
+ * @see https://html.spec.whatwg.org/#elements-2
71
+ * @see https://w3c.github.io/webcomponents/spec/custom/#valid-custom-element-name
72
+ *
73
+ * @example
74
+ * ~# php -a
75
+ * php > $s = [paste copied contents of expression below including parentheses];
76
+ * php > echo $s;
77
+ */
78
+ $tag_pattern = (
79
+ '#<' . // Start with an opening bracket.
80
+ '(/?)' . // Group 1 - If it's a closing tag it'll have a leading slash.
81
+ '(' . // Group 2 - Tag name.
82
+ // Custom element tags have more lenient rules than HTML tag names.
83
+ '(?:[a-z](?:[a-z0-9._]*)-(?:[a-z0-9._-]+)+)' .
84
+ '|' .
85
+ // Traditional tag rules approximate HTML tag names.
86
+ '(?:[\w:]+)' .
87
+ ')' .
88
+ '(?:' .
89
+ // We either immediately close the tag with its '>' and have nothing here.
90
+ '\s*' .
91
+ '(/?)' . // Group 3 - "attributes" for empty tag.
92
+ '|' .
93
+ // Or we must start with space characters to separate the tag name from the attributes (or whitespace).
94
+ '(\s+)' . // Group 4 - Pre-attribute whitespace.
95
+ '([^>]*)' . // Group 5 - Attributes.
96
+ ')' .
97
+ '>#' // End with a closing bracket.
98
+ );
99
+
100
+ while ( preg_match( $tag_pattern, $text, $regex ) ) {
101
+ $full_match = $regex[0];
102
+ $has_leading_slash = ! empty( $regex[1] );
103
+ $tag_name = $regex[2];
104
+ $tag = strtolower( $tag_name );
105
+ $is_single_tag = in_array( $tag, $single_tags, true );
106
+ $pre_attribute_ws = isset( $regex[4] ) ? $regex[4] : '';
107
+ $attributes = trim( isset( $regex[5] ) ? $regex[5] : $regex[3] );
108
+ $has_self_closer = '/' === substr( $attributes, -1 );
109
+
110
+ $newtext .= $tagqueue;
111
+
112
+ $i = strpos( $text, $full_match );
113
+ $l = strlen( $full_match );
114
+
115
+ // Clear the shifter.
116
+ $tagqueue = '';
117
+ if ( $has_leading_slash ) { // End Tag.
118
+ // If too many closing tags.
119
+ if ( $stacksize <= 0 ) {
120
+ $tag = '';
121
+ // Or close to be safe $tag = '/' . $tag.
122
+
123
+ // If stacktop value = tag close value, then pop.
124
+ } elseif ( $tagstack[ $stacksize - 1 ] === $tag ) { // Found closing tag.
125
+ $tag = '</' . $tag . '>'; // Close Tag.
126
+ array_pop( $tagstack );
127
+ $stacksize--;
128
+ } else { // Closing tag not at top, search for it.
129
+ for ( $j = $stacksize - 1; $j >= 0; $j-- ) {
130
+ if ( $tagstack[ $j ] === $tag ) {
131
+ // Add tag to tagqueue.
132
+ for ( $k = $stacksize - 1; $k >= $j; $k-- ) {
133
+ $tagqueue .= '</' . array_pop( $tagstack ) . '>';
134
+ $stacksize--;
135
+ }
136
+ break;
137
+ }
138
+ }
139
+ $tag = '';
140
+ }
141
+ } else { // Begin Tag.
142
+ if ( $has_self_closer ) { // If it presents itself as a self-closing tag...
143
+ // ...but it isn't a known single-entity self-closing tag, then don't let it be treated as such and
144
+ // immediately close it with a closing tag (the tag will encapsulate no text as a result)
145
+ if ( ! $is_single_tag ) {
146
+ $attributes = trim( substr( $attributes, 0, -1 ) ) . "></$tag";
147
+ }
148
+ } elseif ( $is_single_tag ) { // ElseIf it's a known single-entity tag but it doesn't close itself, do so
149
+ $pre_attribute_ws = ' ';
150
+ $attributes .= 0 < strlen( $attributes ) ? ' /' : '/'; // EDIT: If there are attributes, add space before closing tag to match how WP insert br, hr and img tags.
151
+ } else { // It's not a single-entity tag.
152
+ // If the top of the stack is the same as the tag we want to push, close previous tag.
153
+ if ( $stacksize > 0 && ! in_array( $tag, $nestable_tags, true ) && $tagstack[ $stacksize - 1 ] === $tag ) {
154
+ $tagqueue = '</' . array_pop( $tagstack ) . '>';
155
+ $stacksize--;
156
+ }
157
+ $stacksize = array_push( $tagstack, $tag );
158
+ }
159
+
160
+ // Attributes.
161
+ if ( $has_self_closer && $is_single_tag ) {
162
+ // We need some space - avoid <br/> and prefer <br />.
163
+ $pre_attribute_ws = ' ';
164
+ }
165
+
166
+ $tag = '<' . $tag . $pre_attribute_ws . $attributes . '>';
167
+ // If already queuing a close tag, then put this tag on too.
168
+ if ( ! empty( $tagqueue ) ) {
169
+ $tagqueue .= $tag;
170
+ $tag = '';
171
+ }
172
+ }
173
+ $newtext .= substr( $text, 0, $i ) . $tag;
174
+ $text = substr( $text, $i + $l );
175
+ }
176
+
177
+ // Clear Tag Queue.
178
+ $newtext .= $tagqueue;
179
+
180
+ // Add remaining text.
181
+ $newtext .= $text;
182
+
183
+ while ( $x = array_pop( $tagstack ) ) {
184
+ $newtext .= '</' . $x . '>'; // Add remaining tags to close.
185
+ }
186
+
187
+ // WP fix for the bug with HTML comments.
188
+ $newtext = str_replace( '< !--', '<!--', $newtext );
189
+ $newtext = str_replace( '< !--', '< !--', $newtext );
190
+
191
+ return $newtext;
192
+ }
193
+
194
+ /**
195
+ * Multibyte substr_replace(). The mbstring library does not come with a multibyte equivalent of substr_replace().
196
+ * This function behaves exactly like substr_replace() even when the arguments are arrays.
197
+ *
198
+ * @link https://gist.github.com/stemar/8287074
199
+ *
200
+ * @since 2.0
201
+ *
202
+ * @param $string
203
+ * @param $replacement
204
+ * @param $start
205
+ * @param null $length
206
+ *
207
+ * @return array|string
208
+ */
209
+ function mb_substr_replace( $string, $replacement, $start, $length = null ) {
210
+
211
+ if ( is_array( $string ) ) {
212
+
213
+ $num = count( $string );
214
+
215
+ // $replacement
216
+ $replacement = is_array( $replacement ) ? array_slice( $replacement, 0, $num ) : array_pad( array( $replacement ), $num, $replacement );
217
+
218
+ // $start
219
+ if ( is_array( $start ) ) {
220
+ $start = array_slice( $start, 0, $num );
221
+ foreach ( $start as $key => $value ) {
222
+ $start[ $key ] = is_int( $value ) ? $value : 0;
223
+ }
224
+ } else {
225
+ $start = array_pad( array( $start ), $num, $start );
226
+ }
227
+
228
+ // $length
229
+ if ( ! isset( $length ) ) {
230
+ $length = array_fill( 0, $num, 0 );
231
+ } elseif ( is_array( $length ) ) {
232
+ $length = array_slice( $length, 0, $num );
233
+ foreach ( $length as $key => $value ) {
234
+ $length[ $key ] = isset( $value ) ? ( is_int( $value ) ? $value : $num ) : 0;
235
+ }
236
+ } else {
237
+ $length = array_pad( array( $length ), $num, $length );
238
+ }
239
+
240
+ // Recursive call
241
+ return array_map( __FUNCTION__, $string, $replacement, $start, $length );
242
+ }
243
+
244
+ preg_match_all( '/./us', (string) $string, $smatches );
245
+ preg_match_all( '/./us', (string) $replacement, $rmatches );
246
+
247
+ if ( $length === null ) {
248
+
249
+ $length = mb_strlen( $string );
250
+ }
251
+
252
+ array_splice( $smatches[0], $start, $length, $rmatches[0] );
253
+
254
+ return join( $smatches[0] );
255
+ }
256
+
257
+ /**
258
+ * Returns a string with all items from the $find array replaced with their matching
259
+ * items in the $replace array. This does a one to one replacement (rather than globally).
260
+ *
261
+ * This function is multibyte safe.
262
+ *
263
+ * $find and $replace are arrays, $string is the haystack. All variables are passed by reference.
264
+ *
265
+ * @since 1.0
266
+ *
267
+ * @param bool $find
268
+ * @param bool $replace
269
+ * @param string $string
270
+ *
271
+ * @return mixed|string
272
+ */
273
+ function mb_find_replace( &$find = false, &$replace = false, &$string = '' ) {
274
+
275
+ if ( is_array( $find ) && is_array( $replace ) && $string ) {
276
+
277
+ // check if multibyte strings are supported
278
+ if ( function_exists( 'mb_strpos' ) ) {
279
+
280
+ //for ( $i = 0; $i < count( $find ); $i ++ ) {
281
+ //
282
+ // $string = mb_substr(
283
+ // $string,
284
+ // 0,
285
+ // mb_strpos( $string, $find[ $i ] )
286
+ // ) . // everything before $find
287
+ // $replace[ $i ] . // its replacement
288
+ // mb_substr(
289
+ // $string,
290
+ // mb_strpos( $string, $find[ $i ] ) + mb_strlen( $find[ $i ] )
291
+ // ) // everything after $find
292
+ // ;
293
+ //}
294
+
295
+ for ( $i = 0; $i < count( $find ); $i ++ ) {
296
+
297
+ $needle = $find[ $i ];
298
+ $start = mb_strpos( $string, $needle );
299
+
300
+ // If heading can not be found, let try decoding entities to see if it can be found.
301
+ if ( false === $start ) {
302
+
303
+ $needle = html_entity_decode(
304
+ $needle,
305
+ ENT_QUOTES,
306
+ get_option( 'blog_charset' )
307
+ );
308
+
309
+ $needle = str_replace(array('’','“','”'), array('\'','"','"'), $needle);
310
+
311
+ $start = mb_strpos( $string, $needle );
312
+ }
313
+
314
+ /*
315
+ * `mb_strpos()` can return `false`. Only process `mb_substr_replace()` if position in string is found.
316
+ */
317
+ if ( is_int( $start ) ) {
318
+
319
+ $length = mb_strlen( $needle );
320
+ $string = mb_substr_replace( $string, $replace[ $i ], $start, $length );
321
+ }
322
+
323
+ }
324
+
325
+ } else {
326
+
327
+ for ( $i = 0; $i < count( $find ); $i ++ ) {
328
+
329
+ $start = strpos( $string, $find[ $i ] );
330
+ $length = strlen( $find[ $i ] );
331
+
332
+ /*
333
+ * `strpos()` can return `false`. Only process `substr_replace()` if position in string is found.
334
+ */
335
+ if ( is_int( $start ) ) {
336
+
337
+ $string = substr_replace( $string, $replace[ $i ], $start, $length );
338
+ }
339
+ }
340
+ }
341
+ }
342
+
343
+ return $string;
344
+ }