LuckyWP Table of Contents - Version 1.0.0

Version Description

Download this release

Release Info

Developer theluckywp
Plugin Icon 128x128 LuckyWP Table of Contents
Version 1.0.0
Comparing to
See all releases

Version 1.0.0

Files changed (72) hide show
  1. LICENSE.txt +339 -0
  2. admin/Admin.php +198 -0
  3. admin/assets/main.min.css +1 -0
  4. admin/assets/main.min.js +1 -0
  5. admin/controllers/EditorBlockController.php +78 -0
  6. admin/controllers/MetaboxController.php +83 -0
  7. admin/controllers/SettingsController.php +14 -0
  8. admin/controllers/ShortcodeController.php +82 -0
  9. admin/forms/CustomizeForm.php +264 -0
  10. admin/views/settings/index.php +9 -0
  11. admin/widgets/OverrideColorBadge.php +19 -0
  12. admin/widgets/PostTypes.php +46 -0
  13. admin/widgets/customizeModal/CustomizeModal.php +41 -0
  14. admin/widgets/customizeModal/views/modal.php +497 -0
  15. admin/widgets/customizeSuccess/CustomizeSuccess.php +21 -0
  16. admin/widgets/customizeSuccess/views/widget.php +12 -0
  17. admin/widgets/fontSizeField/FontSizeField.php +47 -0
  18. admin/widgets/fontSizeField/views/widget.php +30 -0
  19. admin/widgets/metabox/Metabox.php +24 -0
  20. admin/widgets/metabox/views/box.php +50 -0
  21. admin/widgets/widthField/WidthField.php +43 -0
  22. admin/widgets/widthField/views/widget.php +27 -0
  23. config/plugin.php +29 -0
  24. config/settings.php +309 -0
  25. core/Core.php +65 -0
  26. core/admin/AdminController.php +48 -0
  27. core/admin/helpers/AdminHtml.php +84 -0
  28. core/admin/helpers/AdminUrl.php +47 -0
  29. core/base/BaseObject.php +65 -0
  30. core/base/BasePlugin.php +98 -0
  31. core/base/Container.php +160 -0
  32. core/base/Controller.php +74 -0
  33. core/base/Model.php +311 -0
  34. core/base/Request.php +37 -0
  35. core/base/ServiceLocator.php +111 -0
  36. core/base/View.php +83 -0
  37. core/base/ViewContextInterface.php +13 -0
  38. core/base/Widget.php +51 -0
  39. core/front/BaseFront.php +66 -0
  40. core/helpers/ArrayHelper.php +72 -0
  41. core/helpers/Html.php +323 -0
  42. core/helpers/Json.php +27 -0
  43. core/validators/BooleanValidator.php +53 -0
  44. core/validators/FilterValidator.php +23 -0
  45. core/validators/InlineValidator.php +26 -0
  46. core/validators/RangeValidator.php +36 -0
  47. core/validators/RequiredValidator.php +31 -0
  48. core/validators/Validator.php +175 -0
  49. core/wp/Settings.php +551 -0
  50. front/Front.php +162 -0
  51. front/assets/main.min.css +1 -0
  52. front/assets/main.min.js +1 -0
  53. front/views/body.php +36 -0
  54. front/views/items.php +19 -0
  55. functions.php +26 -0
  56. languages/lwptoc-ru_RU.mo +0 -0
  57. languages/lwptoc.pot +468 -0
  58. luckywp-table-of-contents.php +34 -0
  59. lwptocAutoloader.php +95 -0
  60. plugin/Activation.php +20 -0
  61. plugin/Plugin.php +233 -0
  62. plugin/PostSettings.php +263 -0
  63. plugin/Settings.php +301 -0
  64. plugin/Shortcode.php +442 -0
  65. plugin/editorBlock/EditorBlock.php +46 -0
  66. plugin/editorBlock/editorBlock.min.js +1 -0
  67. plugin/editorBlock/src/editorBlock.js +105 -0
  68. plugin/mcePlugin/McePlugin.php +66 -0
  69. plugin/mcePlugin/mce.min.css +1 -0
  70. plugin/mcePlugin/plugin.min.js +1 -0
  71. readme.txt +88 -0
  72. uninstall.php +9 -0
LICENSE.txt ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Lesser General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) <year> <name of author>
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License along
307
+ with this program; if not, write to the Free Software Foundation, Inc.,
308
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
+
310
+ Also add information on how to contact you by electronic and paper mail.
311
+
312
+ If the program is interactive, make it output a short notice like this
313
+ when it starts in an interactive mode:
314
+
315
+ Gnomovision version 69, Copyright (C) year name of author
316
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
+ This is free software, and you are welcome to redistribute it
318
+ under certain conditions; type `show c' for details.
319
+
320
+ The hypothetical commands `show w' and `show c' should show the appropriate
321
+ parts of the General Public License. Of course, the commands you use may
322
+ be called something other than `show w' and `show c'; they could even be
323
+ mouse-clicks or menu items--whatever suits your program.
324
+
325
+ You should also get your employer (if you work as a programmer) or your
326
+ school, if any, to sign a "copyright disclaimer" for the program, if
327
+ necessary. Here is a sample; alter the names:
328
+
329
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
+
332
+ <signature of Ty Coon>, 1 April 1989
333
+ Ty Coon, President of Vice
334
+
335
+ This General Public License does not permit incorporating your program into
336
+ proprietary programs. If your program is a subroutine library, you may
337
+ consider it more useful to permit linking proprietary applications with the
338
+ library. If this is what you want to do, use the GNU Lesser General
339
+ Public License instead of this License.
admin/Admin.php ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\admin;
4
+
5
+ use luckywp\tableOfContents\admin\controllers\EditorBlockController;
6
+ use luckywp\tableOfContents\admin\controllers\MetaboxController;
7
+ use luckywp\tableOfContents\admin\controllers\SettingsController;
8
+ use luckywp\tableOfContents\admin\controllers\ShortcodeController;
9
+ use luckywp\tableOfContents\admin\widgets\metabox\Metabox;
10
+ use luckywp\tableOfContents\admin\widgets\OverrideColorBadge;
11
+ use luckywp\tableOfContents\core\admin\helpers\AdminUrl;
12
+ use luckywp\tableOfContents\core\base\BaseObject;
13
+ use luckywp\tableOfContents\core\Core;
14
+ use luckywp\tableOfContents\core\helpers\ArrayHelper;
15
+ use luckywp\tableOfContents\core\helpers\Html;
16
+ use luckywp\tableOfContents\plugin\PostSettings;
17
+
18
+ class Admin extends BaseObject
19
+ {
20
+
21
+ protected $pageSettingsHook;
22
+
23
+ public function init()
24
+ {
25
+ if (is_admin()) {
26
+ add_action('admin_menu', [$this, 'menu']);
27
+ add_action('admin_enqueue_scripts', [$this, 'assets'], 9);
28
+ add_action('add_meta_boxes', [$this, 'addMetaBoxes']);
29
+
30
+ // Ссылки в списке плагинов
31
+ add_filter('plugin_action_links_' . Core::$plugin->basename, function ($links) {
32
+ array_unshift($links, Html::a(esc_html__('Settings', 'lwptoc'), AdminUrl::toOptions('settings')));
33
+ return $links;
34
+ });
35
+
36
+ // Контроллеры
37
+ MetaboxController::getInstance();
38
+ ShortcodeController::getInstance();
39
+ EditorBlockController::getInstance();
40
+ }
41
+ }
42
+
43
+ public function menu()
44
+ {
45
+ $this->pageSettingsHook = add_submenu_page(
46
+ 'options-general.php',
47
+ esc_html__('Table of Contents', 'lwptoc'),
48
+ esc_html__('Table of Contents', 'lwptoc'),
49
+ 'manage_options',
50
+ Core::$plugin->prefix . 'settings',
51
+ [SettingsController::className(), 'router']
52
+ );
53
+ }
54
+
55
+ /**
56
+ * @return array
57
+ */
58
+ public function getMetaboxPostTypes()
59
+ {
60
+ if (Core::$plugin->settings->autoInsertEnable &&
61
+ $postTypes = Core::$plugin->settings->autoInsertPostTypes) {
62
+ return $postTypes;
63
+ }
64
+ return [];
65
+ }
66
+
67
+ public function addMetaBoxes()
68
+ {
69
+ if (current_user_can('edit_post') && $this->getMetaboxPostTypes()) {
70
+ add_meta_box(
71
+ Core::$plugin->prefix . '_postSettings',
72
+ esc_html__('Table of Contents', 'lwptoc'),
73
+ function ($post) {
74
+ echo Metabox::widget([
75
+ 'post' => $post,
76
+ ]);
77
+ },
78
+ $this->getMetaboxPostTypes(),
79
+ 'side',
80
+ 'low'
81
+ );
82
+ }
83
+ }
84
+
85
+ public function assets($hook)
86
+ {
87
+ global $post;
88
+ if (in_array($hook, [$this->pageSettingsHook, 'post.php', 'post-new.php'])) {
89
+ wp_enqueue_style('wp-color-picker');
90
+ wp_enqueue_script('wp-color-picker');
91
+ }
92
+ wp_enqueue_style(Core::$plugin->prefix . 'adminMain', Core::$plugin->url . '/admin/assets/main.min.css', [], Core::$plugin->version);
93
+ wp_enqueue_script(Core::$plugin->prefix . 'adminMain', Core::$plugin->url . '/admin/assets/main.min.js', ['jquery'], Core::$plugin->version);
94
+ wp_localize_script(Core::$plugin->prefix . 'adminMain', 'lwptocMain', [
95
+ 'ajaxUrl' => admin_url('admin-ajax.php'),
96
+ 'nonce' => wp_create_nonce(Core::$plugin->prefix . 'adminMain'),
97
+ 'postId' => in_array($hook, ['post.php', 'post-new.php']) ? ArrayHelper::getValue($post, 'ID') : null,
98
+ 'tableOfContents' => esc_html__('Table of Contents', 'lwptoc'),
99
+ 'Edit' => esc_html__('Edit', 'lwptoc'),
100
+ ]);
101
+ }
102
+
103
+ public function checkAjaxReferer()
104
+ {
105
+ check_ajax_referer(Core::$plugin->prefix . 'adminMain');
106
+ }
107
+
108
+ /**
109
+ * @param PostSettings|array $source
110
+ * @return array
111
+ */
112
+ public function overrideSettingsToRows($source)
113
+ {
114
+ $getValue = function ($source, $key) {
115
+ $v = ArrayHelper::getValue($source, $key);
116
+ if ($v === null) {
117
+ $lowerKey = strtolower($key);
118
+ if ($lowerKey != $key) {
119
+ $v = ArrayHelper::getValue($source, $lowerKey);
120
+ }
121
+ }
122
+ return $v;
123
+ };
124
+ $rows = [];
125
+ if (null !== $v = $getValue($source, 'position')) {
126
+ $rows[] = [esc_html__('Position', 'lwptoc'), ArrayHelper::getValue(Core::$plugin->positionsList, $v)];
127
+ }
128
+ if (null !== $v = $getValue($source, 'min')) {
129
+ $rows[] = [esc_html__('Minimal Count of Headers', 'lwptoc'), $v];
130
+ }
131
+ if (null !== $v = $getValue($source, 'depth')) {
132
+ $rows[] = [esc_html__('Depth', 'lwptoc'), $v];
133
+ }
134
+ if (null !== $v = $getValue($source, 'hierarchical')) {
135
+ $rows[] = [esc_html__('Hierarchical View', 'lwptoc'), $v ? __('Enabled', 'lwptoc') : __('Disabled', 'lwptoc')];
136
+ }
137
+ if (null !== $v = $getValue($source, 'numeration')) {
138
+ $rows[] = [esc_html__('Numeration', 'lwptoc'), ArrayHelper::getValue(Core::$plugin->numerationsList, $v)];
139
+ }
140
+ if (null !== $v = $getValue($source, 'title')) {
141
+ $rows[] = [esc_html__('Title', 'lwptoc'), $v == '' ? null : $v];
142
+ }
143
+ if (null !== $v = $getValue($source, 'toggle')) {
144
+ $rows[] = [esc_html__('Toggle Show/Hide', 'lwptoc'), $v ? __('Enabled', 'lwptoc') : __('Disabled', 'lwptoc')];
145
+ }
146
+ if (null !== $v = $getValue($source, 'labelShow')) {
147
+ $rows[] = [esc_html__('Label Show', 'lwptoc'), $v == '' ? null : $v];
148
+ }
149
+ if (null !== $v = $getValue($source, 'labelHide')) {
150
+ $rows[] = [esc_html__('Label Hide', 'lwptoc'), $v == '' ? null : $v];
151
+ }
152
+ if (null !== $v = $getValue($source, 'hideItems')) {
153
+ $rows[] = [esc_html__('Hide Items', 'lwptoc'), $v ? __('Enabled', 'lwptoc') : __('Disabled', 'lwptoc')];
154
+ }
155
+ if (null !== $v = $getValue($source, 'smoothScroll')) {
156
+ $rows[] = [esc_html__('Smooth Scroll', 'lwptoc'), $v ? __('Enabled', 'lwptoc') : __('Disabled', 'lwptoc')];
157
+ }
158
+ if (null !== $v = $getValue($source, 'smoothScrollOffset')) {
159
+ $rows[] = [esc_html__('Smooth Scroll Offset Top', 'lwptoc'), $v . 'px'];
160
+ }
161
+ if (null !== $v = $getValue($source, 'width')) {
162
+ $rows[] = [esc_html__('Width', 'lwptoc'), Core::$plugin->widthToLabel($v)];
163
+ }
164
+ if (null !== $v = $getValue($source, 'float')) {
165
+ $rows[] = [esc_html__('Float', 'lwptoc'), ArrayHelper::getValue(Core::$plugin->floatsList, $v)];
166
+ }
167
+ if (null !== $v = $getValue($source, 'titleFontSize')) {
168
+ $rows[] = [esc_html__('Title Font Size', 'lwptoc'), Core::$plugin->fontSizeToLabel($v)];
169
+ }
170
+ if (null !== $v = $getValue($source, 'titleFontWeight')) {
171
+ $rows[] = [esc_html__('Title Font Weight', 'lwptoc'), ArrayHelper::getValue(Core::$plugin->fontWeightsList, $v)];
172
+ }
173
+ if (null !== $v = $getValue($source, 'itemsFontSize')) {
174
+ $rows[] = [esc_html__('Items Font Size', 'lwptoc'), Core::$plugin->fontSizeToLabel($v)];
175
+ }
176
+ if (null !== $v = $getValue($source, 'colorScheme')) {
177
+ $rows[] = [esc_html__('Color Scheme', 'lwptoc'), ArrayHelper::getValue(Core::$plugin->colorSchemesList, $v)];
178
+ }
179
+ foreach ([
180
+ 'backgroundColor' => 'Background Color',
181
+ 'borderColor' => 'Border Color',
182
+ 'titleColor' => 'Title Color',
183
+ 'linkColor' => 'Link Color',
184
+ 'hoverLinkColor' => 'Hover Link Color',
185
+ 'visitedLinkColor' => 'Visited Link Color',
186
+ ] as $var => $label) {
187
+ if (null !== $v = $getValue($source, $var)) {
188
+ $rows[] = [esc_html__($label, 'lwptoc'), OverrideColorBadge::widget(['color' => $v]), false];
189
+ }
190
+ }
191
+ return array_map(function ($row) {
192
+ if ($row[1] !== null && ArrayHelper::getValue($row, 2, true) === true) {
193
+ $row[1] = esc_html($row[1]);
194
+ }
195
+ return $row;
196
+ }, $rows);
197
+ }
198
+ }
admin/assets/main.min.css ADDED
@@ -0,0 +1 @@
 
1
+ @keyframes lwptocLightSpinnerKeyFrames{0%,100%,20%,80%{transform:scale(.7)}50%{transform:scale(1.5)}}.lwptocLightSpinner{display:inline-block;position:relative;width:64px;height:64px}.lwptocLightSpinner div{position:absolute;width:5px;height:5px;background:#fff;border-radius:50%;animation:lwptocLightSpinnerKeyFrames 1.2s linear infinite}.lwptocLightSpinner div:nth-child(1){animation-delay:0s;top:29px;left:53px}.lwptocLightSpinner div:nth-child(2){animation-delay:-.1s;top:18px;left:50px}.lwptocLightSpinner div:nth-child(3){animation-delay:-.2s;top:9px;left:41px}.lwptocLightSpinner div:nth-child(4){animation-delay:-.3s;top:6px;left:29px}.lwptocLightSpinner div:nth-child(5){animation-delay:-.4s;top:9px;left:18px}.lwptocLightSpinner div:nth-child(6){animation-delay:-.5s;top:18px;left:9px}.lwptocLightSpinner div:nth-child(7){animation-delay:-.6s;top:29px;left:6px}.lwptocLightSpinner div:nth-child(8){animation-delay:-.7s;top:41px;left:9px}.lwptocLightSpinner div:nth-child(9){animation-delay:-.8s;top:50px;left:18px}.lwptocLightSpinner div:nth-child(10){animation-delay:-.9s;top:53px;left:29px}.lwptocLightSpinner div:nth-child(11){animation-delay:-1s;top:50px;left:41px}.lwptocLightSpinner div:nth-child(12){animation-delay:-1.1s;top:41px;left:50px}@keyframes lwptocDarkSpinnerKeyFrames{0%,100%,20%,80%{transform:scale(.7)}50%{transform:scale(1.5)}}.lwptocDarkSpinner{display:inline-block;position:relative;width:64px;height:64px}.lwptocDarkSpinner div{position:absolute;width:5px;height:5px;background:#aaa;border-radius:50%;animation:lwptocDarkSpinnerKeyFrames 1.2s linear infinite}.lwptocDarkSpinner div:nth-child(1){animation-delay:0s;top:29px;left:53px}.lwptocDarkSpinner div:nth-child(2){animation-delay:-.1s;top:18px;left:50px}.lwptocDarkSpinner div:nth-child(3){animation-delay:-.2s;top:9px;left:41px}.lwptocDarkSpinner div:nth-child(4){animation-delay:-.3s;top:6px;left:29px}.lwptocDarkSpinner div:nth-child(5){animation-delay:-.4s;top:9px;left:18px}.lwptocDarkSpinner div:nth-child(6){animation-delay:-.5s;top:18px;left:9px}.lwptocDarkSpinner div:nth-child(7){animation-delay:-.6s;top:29px;left:6px}.lwptocDarkSpinner div:nth-child(8){animation-delay:-.7s;top:41px;left:9px}.lwptocDarkSpinner div:nth-child(9){animation-delay:-.8s;top:50px;left:18px}.lwptocDarkSpinner div:nth-child(10){animation-delay:-.9s;top:53px;left:29px}.lwptocDarkSpinner div:nth-child(11){animation-delay:-1s;top:50px;left:41px}.lwptocDarkSpinner div:nth-child(12){animation-delay:-1.1s;top:41px;left:50px}.lwptocPreloaderWrapper{position:relative!important}.lwptocPreloaderOverlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:50;background:rgba(255,255,255,.7)}.lwptocPreloaderOverlay>DIV{position:absolute;top:50%;left:50%;margin-left:-32px;margin-top:-32px}.lwptocModal-container,.lwptocModal-overlay{position:fixed;left:0;top:0;right:0;bottom:0;z-index:1000}.lwptocModal-container{overflow:auto}.lwptocModal-container_i{display:table;height:100%;margin:0 auto}.lwptocModal-container_i2{display:table-cell;padding:24px}.lwptocModal-error{padding:20px;border-radius:10px;background:#000;color:#fff}.lwptocModal-preloader{width:80px;height:80px;border-radius:10px;background:#000 url(preloader.gif) no-repeat 50% 50%}.lwptocModalBox{position:relative;width:500px;background:#fff;color:#000;box-shadow:0 3px 6px rgba(0,0,0,.3)}.lwptocModalBox_close{position:absolute;top:4px;right:8px;color:#666;text-align:center;line-height:29px;width:29px;height:29px;cursor:pointer}.lwptocModalBox_close:before{content:'\f158';font:normal 20px/29px dashicons}.lwptocModalBox_close:hover{color:#00a0d2}.lwptocModalBox_title{background:#fcfcfc;border-bottom:1px solid #dfdfdf;font-weight:600;font-size:13px;line-height:19px;padding:8px 29px 10px 18px}.lwptocModalBox_body{padding:18px 18px 22px 18px}.lwptocModalBox_footer{padding:18px;border-top:1px solid #dfdfdf}.lwptocModalBox_footer_buttons{text-align:right}.lwptocModalBox_footer_buttons .button{margin-left:4px}.lwptocModalBox_footer_buttons .button:first-child{margin-left:0}.lwptocModalBox_footer_buttons:after{content:".";display:block;height:0;font-size:0;line-height:0;clear:both;visibility:hidden}.lwptocMetabox P{padding:0;margin:12px 0 0}.lwptocMetabox P:first-child{margin-top:0}.lwptocMetabox .button-link{margin-left:12px}.lwptocMetabox_settings{margin:6px 0 18px;padding:6px 0 6px 4px;box-shadow:-2px 0 0 #fafafa,-4px 0 0 #ddd,4px 0 0 #fafafa;background:#fafafa}.lwptocMetabox_settings_item{overflow:hidden;white-space:nowrap;text-overflow:ellipsis;margin-top:4px}.lwptocMetabox_settings_item:first-child{margin-top:0}.lwptocMetabox_settings_item B{font-weight:400;color:#888}.lwptocMetabox_settings_item .lwptocColorBadge B{position:relative;top:1px;margin-left:2px}.lwptocCustomize .lwptocModalBox_body{padding:0 0 22px}.lwptocCustomize_errors{margin:14px;border-left:2px solid red;background:#fff4f6;padding:4px 12px 6px;color:red}.lwptocCustomize_errors P{margin:2px 0 0 0}.lwptocCustomize_errors P:first-child{margin-top:0}.lwptocCustomize_tabs{padding:0 14px;margin-top:12px;background:#f1f1f1}.lwptocCustomize_tabs:after{content:".";display:block;height:0;font-size:0;line-height:0;clear:both;visibility:hidden}.lwptocCustomize_tab{float:left;white-space:nowrap;padding:6px 12px 7px;cursor:pointer}.lwptocCustomize_tab:hover{background:#ddd}.lwptocCustomize_tab-active{background:#0073aa!important;color:#fff;cursor:default}.lwptocCustomize_fields{display:none;padding:16px 18px 0}.lwptocCustomize_field{position:relative;margin-top:12px}.lwptocCustomize_field:first-child{margin-top:0}.lwptocCustomize_field_label{font-weight:700}.lwptocCustomize_field_default{float:right;margin-left:4px;cursor:pointer;color:#0073aa;border-bottom:1px dotted #0073aa}.lwptocCustomize_field_default:hover{color:#00a0d2;border-color:#00a0d2}.lwptocCustomize_field_override{display:none;position:absolute;left:-4px;top:-4px;right:-4px;bottom:-4px;background:rgba(42,150,204,.8);color:#fff;font-size:16px;font-weight:700;text-align:center;padding-top:12px;cursor:pointer;-webkit-transition:.1s linear;-moz-transition:.1s linear;-o-transition:.1s linear;transition:.1s linear;opacity:0}.lwptocCustomize_field_defaultValue{display:none;font-size:16px;line-height:20px;font-style:italic;color:#aaa}.lwptocCustomize_field_el{margin-top:4px}.lwptocCustomize_field_el_select,.lwptocCustomize_field_el_textInput{width:100%}.lwptocCustomize_field_desc{margin-top:2px;font-size:90%;color:#888}.lwptocCustomize_field-default .lwptocCustomize_field_default,.lwptocCustomize_field-default .lwptocCustomize_field_el{display:none}.lwptocCustomize_field-default .lwptocCustomize_field_defaultValue,.lwptocCustomize_field-default .lwptocCustomize_field_override{display:block}.lwptocCustomize_field-default .lwptocCustomize_field_desc{margin-top:0}.lwptocCustomize_field-default:hover .lwptocCustomize_field_override{opacity:1}.lwptocWidthField_custom{display:inline}.lwptocWidthField_sizeInput{vertical-align:top;width:64px;padding-bottom:4px;text-align:center}.lwptocWidthField_unitInput{vertical-align:top}.lwptocFontSizeField_custom{display:inline}.lwptocFontSizeField_sizeInput{vertical-align:top;width:64px;padding-bottom:4px;text-align:center}.lwptocFontSizeField_unitInput{vertical-align:top}.lwptocModalSuccess{text-align:center;cursor:default;color:#8bc34a}.lwptocModalSuccess_ico{font-family:dashicons;font-size:160px;line-height:124px}.lwptocModalSuccess_text{color:#fff;font-size:24px;line-height:30px}.lwptocColorBadge B{display:inline-block;margin-right:4px;width:12px;height:12px;border-radius:6px}.lwptocFloatLeft{float:left}.lwptocEditorBlock{display:block;padding:12px 16px 13px;border:2px dashed #ddd;background:#fff;color:#333;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",sans-serif}.lwptocEditorBlock_title{font-size:14px;line-height:18px;font-weight:700}.lwptocEditorBlock_title-loading{color:#aaa}.lwptocEditorBlock_items{margin-top:4px;font-size:12px;line-height:16px}.lwptocEditorBlock_item{margin-top:2px}.lwptocEditorBlock_item:first-child{margin-top:0}.lwptocEditorBlock_item_label{color:#888}.lwptocEditorBlock .lwptocColorBadge B{position:relative;top:1px;margin-left:2px}.mce-i-lwptocButton:before{font:400 20px/1 dashicons;content:"\f163"}
admin/assets/main.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(t,e){var o="lwptocPreloader";t.fn[o+"Show"]=function(){var e=t(this);e.addClass(o+"Wrapper"),e.append('<div class="'+o+'Overlay"><div class="lwptocDarkSpinner"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div></div>')},t.fn[o+"Hide"]=function(){var e=t(this);e.removeClass(o+"Wrapper"),e.find("."+o+"Overlay").remove()}}(jQuery),function(r,e){var l="lwptocModal",t={type:"html",content:"",url:"",ajax:{},ajaxRequest:null,closeOnEsc:!0,closeOnOverlayClick:!0,clone:!1,overlay:{block:void 0,tpl:'<div class="'+l+'-overlay"></div>',css:{backgroundColor:"#000",opacity:.6,zIndex:1e5}},container:{block:void 0,tpl:'<div class="'+l+'-container"><div class="'+l+'-container_i"><div class="'+l+'-container_i2"></div></div></div>'},preloader:{verticalAlign:void 0,tpl:'<div class="'+l+'-preloader" />'},wrap:void 0,body:void 0,errors:{tpl:'<div class="'+l+"-error "+l+'-close"></div>',autocloseDelay:2e3,ajaxUnsuccessfulLoad:"Error"},openEffect:{type:"fade",speed:400},closeEffect:{type:"fade",speed:400},width:"auto",verticalAlign:"middle",beforeOpen:r.noop,afterOpen:r.noop,beforeClose:r.noop,afterClose:r.noop,afterLoading:r.noop,afterLoadingOnShow:r.noop,errorLoading:r.noop},d=0,s=r([]),u=function(e,t){var o=!0;return r(e).each(function(){r(t.target).get(0)==r(this).get(0)&&(o=!1),0==r(t.target).closest("HTML",r(this).get(0)).length&&(o=!1)}),o},i={getParentEl:function(e){var t=r(e);return t.data(l)?t:(t=r(e).closest("."+l+"-container").data(l+"ParentEl"))||!1},transition:function(e,t,o,n){switch(n=null==n?r.noop:n,o.type){case"fade":"show"==t?e.fadeIn(o.speed,n):e.fadeOut(o.speed,n);break;case"none":"show"==t?e.show():e.hide(),n()}},setWrapMarginRight:function(e,t){e.wrap.css("marginRight",t+"px"),r(document).trigger(l+"_setWrapMarginRight",t)},initEl:function(t,e){var o=t.data(l);if(!o){if(d++,(o=e).modalID=d,o.overlay.block=r(o.overlay.tpl),o.overlay.block.css(o.overlay.css),o.container.block=r(o.container.tpl),o.body=r("."+l+"-container_i2",o.container.block),e.clone?o.body.html(t.clone(!0)):(t.before('<div id="'+l+"Reserve"+o.modalID+'" style="display: none" />'),o.body.html(t)),o.body.on("click","."+l+"-close",function(){return t[l]("close"),!1}),o.closeOnOverlayClick&&o.overlay.block.add(o.container.block).click(function(e){u(r(">*",o.body),e)&&t[l]("close")}),o.container.block.data(l+"ParentEl",t),t.data(l,o),s=r.merge(s,t),r.proxy(p.show,t)(),"html"==o.type)return t;if(null!=o.ajax.beforeSend){var n=o.ajax.beforeSend;delete o.ajax.beforeSend}if(null!=o.ajax.success){var i=o.ajax.success;delete o.ajax.success}if(null!=o.ajax.error){var a=o.ajax.error;delete o.ajax.error}var c=r.extend(!0,{url:o.url,beforeSend:function(){o.body.html(o.preloader.tpl).css("verticalAlign",void 0===o.preloader.verticalAlign?o.verticalAlign:o.preloader.verticalAlign),void 0!==n&&n(o,t)},success:function(e){t.trigger("afterLoading."+l),e=o.afterLoading(o,t,e)||e,o.body.css("verticalAlign",o.verticalAlign),null==i?o.body.html(e):i(o,t,e),t.trigger("afterLoadingOnShow."+l),o.afterLoadingOnShow(o,t,e)},error:function(){t.trigger("errorLoading."+l),o.errorLoading(o,t),null==a?(o.body.html(o.errors.tpl),r("."+l+"-error",o.body).html(o.errors.ajaxUnsuccessfulLoad),r("."+l+"-close",o.body).click(function(){return t[l]("close"),!1}),o.errors.autocloseDelay&&setTimeout(function(){t[l]("close")},o.errors.autocloseDelay)):a(o,t)}},o.ajax);o.ajaxRequest=r.ajax(c),t.data(l,o)}},init:function(e){return e=r.extend(!0,{},t,e),r.isFunction(this)?null==e?void r.error(l+": Uncorrect parameters"):""!=e.url?(e.type="ajax",i.initEl(r("<div />"),e)):""!=e.content?(e.type="html",i.initEl(r(e.content),e)):void r.error(l+': Set parameter "url" or "content"'):this.each(function(){i.initEl(r(this),r.extend(!0,{},e))})}},p={show:function(){var e=i.getParentEl(this);if(!1!==e){var t=e.data(l);if(t.overlay.block.hide(),t.container.block.hide(),r("BODY").append(t.overlay.block),r("BODY").append(t.container.block),t.container.block.css("zIndex",t.overlay.block.css("zIndex")),t.beforeOpen(t,e),e.trigger("beforeOpen."+l),"hidden"!=t.wrap.css("overflow-y")){t.wrap.data(l+"Overflow",t.wrap.css("overflow-y"));var o=t.wrap.outerWidth(!0);t.wrap.css("overflow-y","hidden");var n=t.wrap.outerWidth(!0);n!=o&&i.setWrapMarginRight(t,n-o)}return s.not(e).each(function(){r(this).data(l).overlay.block.hide()}),t.body.css("verticalAlign",t.verticalAlign),t.body.parent().css("width",t.width),i.transition(t.overlay.block,"show",1<s.length?{type:"none"}:t.openEffect),i.transition(t.container.block,"show",1<s.length?{type:"none"}:t.openEffect,function(){t.afterOpen(t,e),e.trigger("afterOpen."+l)}),e}r.error(l+": Uncorrect call")},close:function(){if(!r.isFunction(this))return this.each(function(){var e=i.getParentEl(this);if(!1!==e){var t=e.data(l);!1!==t.beforeClose(t,e)&&(e.trigger("beforeClose."+l),s.not(e).last().each(function(){r(this).data(l).overlay.block.show()}),i.transition(t.overlay.block,"hide",1<s.length?{type:"none"}:t.closeEffect),i.transition(t.container.block,"hide",1<s.length?{type:"none"}:t.closeEffect,function(){t.afterClose(t,e),e.trigger("afterClose."+l),t.clone||r("#"+l+"Reserve"+t.modalID).replaceWith(t.body.find(">*")),t.overlay.block.remove(),t.container.block.remove(),e.data(l,null),r("."+l+"-container").length||(t.wrap.data(l+"Overflow")&&t.wrap.css("overflow-y",t.wrap.data(l+"Overflow")),i.setWrapMarginRight(t,0))}),"ajax"==t.type&&t.ajaxRequest.abort(),s=s.not(e))}else r.error(l+": Uncorrect call")});s.each(function(){r(this)[l]("close")})},getActive:function(){var e=r("."+l+"-container").last();return!!e.length&&e.data(l+"ParentEl").data(l)},setDefault:function(e){r.extend(!0,t,e)}};r(function(){t.wrap=r(document.all&&!document.querySelector?"html":"body")}),r(document).bind("keyup."+l,function(e){var t=s.last();t.length&&(t.data(l).closeOnEsc&&27===e.keyCode&&t[l]("close"))}),r[l]=r.fn[l]=function(e){return p[e]?p[e].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof e&&e?void r.error(l+": Method "+e+" does not exist"):i.init.apply(this,arguments)}}(jQuery),function(n,e){var i="lwptocAjaxForm",t={afterInit:n.noop,beforeSend:n.noop,success:n.noop,error:function(){alert("Failed to execute the query. Reload the page and try again.")},b:{}},a={isForm:function(e){return"form"==e.get(0).tagName.toLowerCase()},makeData:function(e){return a.isForm(e)?e.serialize():(t=e.clone().wrap("<form/>"),e.find("select").each(function(e){t.find("select").eq(e).val(n(this).val())}),t.serialize());var t},submit:function(e,t){n.ajax({type:"POST",cache:!1,data:a.makeData(e),url:a.isForm(e)?e.attr("action"):e.data("action"),beforeSend:function(){t.beforeSend(t,e)},success:function(e){t.b.container.html(e),t.success(t)},error:function(){t.error(t,e)}})},initContainer:function(e,t){var o=e.data(i);o||(o=t,e.data(i,o),(o.b.container=e).on("submit","[data-ajax-form]",function(){return a.submit(n(this),o),!1}),o.afterInit(o))},init:function(e){return e=n.extend(!0,{},t,e),n.isFunction(this)?(n.error(i+": Uncorrect call"),!1):this.each(function(){a.initContainer(n(this),n.extend(!0,{},e))})}},o={setDefault:function(e){n.extend(!0,t,e)}};n[i]=n.fn[i]=function(e){return o[e]?o[e].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof e&&e?(n.error(i+": Method "+e+" does not exist"),!1):a.init.apply(this,arguments)}}(jQuery),function(r){r.lwptocModal("setDefault",{preloader:{tpl:'<div class="lwptocLightSpinner"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div>'},overlay:{css:{zIndex:999999}}}),r(function(){var e=r(".js-lwptocSmoothScrollCheckbox");if(e.length){var t=r(".js-lwptocSmoothScrollIOffsetInput").closest("TR");e.change(function(){e.prop("checked")?t.show():t.hide()}).change()}var o=r(".js-lwptocToggleCheckbox");if(o.length){var n=r(".js-lwptocToggleEl").closest("TR");o.change(function(){o.prop("checked")?n.show():n.hide()}).change()}var i=r(".js-lwptocAutoInsertEnableCheckbox");if(i.length){var a=r(".js-lwptocAutoInsertEl").closest("TR");i.change(function(){i.prop("checked")?a.show():a.hide()}).change()}var c=r(".lwptoc_colorPicker");c.length&&c.wpColorPicker()}),r.lwptocCustomize={init:function(t,o){n.init(t.find(".lwptocWidthField")),i.init(t.find(".lwptocFontSizeField")),t.find(".lwptoc_colorPicker").wpColorPicker(),t.on("click",".lwptocCustomize_field_override",function(){var e=r(this).closest(".lwptocCustomize_field");e.removeClass("lwptocCustomize_field-default"),e.find("INPUT[type=text]").focus(),e.find(".lwptocCustomize_field_inputDefault").val(0)}),t.on("click",".lwptocCustomize_field_default",function(){var e=r(this).closest(".lwptocCustomize_field");e.addClass("lwptocCustomize_field-default"),e.find(".lwptocCustomize_field_inputDefault").val(1)}),t.on("click",".lwptocCustomize_tab",function(){t.find(".lwptocCustomize_tab-active").removeClass("lwptocCustomize_tab-active"),r(this).addClass("lwptocCustomize_tab-active"),t.find(".lwptocCustomize_fields").hide(),t.find(".lwptocCustomize_fields-"+r(this).data("tab")).show()}),t.lwptocAjaxForm({beforeSend:function(){t.lwptocPreloaderShow()},success:function(e){t.lwptocPreloaderHide(),o()}})},show:function(e,t){e._ajax_nonce=lwptocMain.nonce,r.lwptocModal({url:lwptocMain.ajaxUrl,closeOnOverlayClick:!1,verticalAlign:"top",ajax:{data:e},afterLoadingOnShow:function(e){r.lwptocCustomize.init(e.body.find(".lwptocCustomize"),function(){e.body.lwptocModal("close")})},afterClose:t})}};var o={reload:function(e){var t=r(e);o.$container.replaceWith(t),o.init(t)},setEnabled:function(e){r.ajax({url:lwptocMain.ajaxUrl,data:{_ajax_nonce:lwptocMain.nonce,action:"lwptoc_metabox_set_enabled",postId:o.$container.data("postId"),enabled:e?1:0},beforeSend:function(){o.$container.lwptocPreloaderShow()},success:function(e){o.$container.lwptocPreloaderHide(),o.reload(e)}})},init:function(){o.$container=r(".lwptocMetabox"),o.$container.length&&(o.$container.find(".lwptocMetabox_disable").click(function(){o.setEnabled(!1)}),o.$container.find(".lwptocMetabox_enable").click(function(){o.setEnabled(!0)}),o.$container.find(".lwptocMetabox_customize").click(function(){r.lwptocCustomize.show({action:"lwptoc_metabox_customize",postId:o.$container.data("postId")},r.noop)}),r(document).on("lwptocMetaboxCustomized",function(e,t){r(".lwptocCustomize-metabox").lwptocModal("close"),o.reload(t.metabox)}))}};r(function(){o.init()});var n={update:function(e){"custom"==e.$typeInput.val()?(e.$input.val(e.$sizeInput.val()+e.$unitInput.val()),e.$custom.show()):(e.$input.val(e.$typeInput.val()),e.$custom.hide())},init:function(e){e.each(function(){var e={$container:r(this)};e.$typeInput=e.$container.find(".lwptocWidthField_typeInput"),e.$sizeInput=e.$container.find(".lwptocWidthField_sizeInput"),e.$unitInput=e.$container.find(".lwptocWidthField_unitInput"),e.$input=e.$container.find(".lwptocWidthField_input"),e.$custom=e.$container.find(".lwptocWidthField_custom"),e.$typeInput.add(e.$sizeInput).add(e.$unitInput).change(function(){n.update(e)})})}};r(function(){n.init(r(".lwptocWidthField"))});var i={update:function(e){"custom"==e.$typeInput.val()?(e.$input.val(e.$sizeInput.val()+e.$unitInput.val()),e.$custom.show()):(e.$input.val(e.$typeInput.val()),e.$custom.hide())},init:function(e){e.each(function(){var e={$container:r(this)};e.$typeInput=e.$container.find(".lwptocFontSizeField_typeInput"),e.$sizeInput=e.$container.find(".lwptocFontSizeField_sizeInput"),e.$unitInput=e.$container.find(".lwptocFontSizeField_unitInput"),e.$input=e.$container.find(".lwptocFontSizeField_input"),e.$custom=e.$container.find(".lwptocFontSizeField_custom"),e.$typeInput.add(e.$sizeInput).add(e.$unitInput).change(function(){i.update(e)})})}};r(function(){i.init(r(".lwptocFontSizeField"))})}(jQuery);
admin/controllers/EditorBlockController.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\admin\controllers;
4
+
5
+ use luckywp\tableOfContents\admin\forms\CustomizeForm;
6
+ use luckywp\tableOfContents\admin\widgets\customizeModal\CustomizeModal;
7
+ use luckywp\tableOfContents\admin\widgets\customizeSuccess\CustomizeSuccess;
8
+ use luckywp\tableOfContents\core\admin\AdminController;
9
+ use luckywp\tableOfContents\core\Core;
10
+ use luckywp\tableOfContents\core\helpers\Json;
11
+ use luckywp\tableOfContents\plugin\PostSettings;
12
+
13
+ class EditorBlockController extends AdminController
14
+ {
15
+
16
+ public function init()
17
+ {
18
+ parent::init();
19
+ add_action('plugins_loaded', [$this, 'initAjax']);
20
+ }
21
+
22
+ public function initAjax()
23
+ {
24
+ add_action('wp_ajax_lwptoc_block_edit', [$this, 'ajaxEdit']);
25
+ add_action('wp_ajax_lwptoc_block_view', [$this, 'ajaxView']);
26
+ }
27
+
28
+ public function ajaxEdit()
29
+ {
30
+ $post = get_post((int)Core::$plugin->request->get('postId'));
31
+ $attrs = Core::$plugin->request->get('attrs');
32
+ if (!is_array($attrs)) {
33
+ $attrs = [];
34
+ }
35
+ $onlyBody = false;
36
+
37
+ $postSettings = new PostSettings($post->ID);
38
+ $model = new CustomizeForm($postSettings, $attrs);
39
+ if ($model->load(Core::$plugin->request->post())) {
40
+ if ($model->validate()) {
41
+ echo CustomizeSuccess::widget([
42
+ 'after' => '<script>$(document).trigger("lwptocEditorBlockChanged", ' . Json::encode($model->getAttrs()) . ');</script>',
43
+ ]);
44
+ wp_die();
45
+ }
46
+ $onlyBody = true;
47
+ }
48
+
49
+ echo CustomizeModal::widget([
50
+ 'onlyBody' => $onlyBody,
51
+ 'post' => $post,
52
+ 'action' => 'lwptoc_block_edit',
53
+ 'model' => $model,
54
+ ]);
55
+ wp_die();
56
+ }
57
+
58
+ public function ajaxView()
59
+ {
60
+ $attrs = Core::$plugin->request->get('attrs');
61
+ if (!is_array($attrs)) {
62
+ $attrs = [];
63
+ }
64
+ $rows = Core::$plugin->admin->overrideSettingsToRows($attrs);
65
+ echo '<div class="lwptocEditorBlock_title">' . esc_html__('Table of Contents', 'lwptoc') . '</div>';
66
+ if ($rows) {
67
+ echo '<div class="lwptocEditorBlock_items">';
68
+ foreach ($rows as $row) {
69
+ echo '<div class="lwptocEditorBlock_item">';
70
+ echo '<span class="lwptocEditorBlock_item_label">' . esc_html($row[0]) . ':</span> ';
71
+ echo $row[1] === null ? '<i>' . __('empty', 'lwptoc') . '</i>' : $row[1];
72
+ echo '</div>';
73
+ }
74
+ echo '</div>';
75
+ }
76
+ wp_die();
77
+ }
78
+ }
admin/controllers/MetaboxController.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\admin\controllers;
4
+
5
+ use luckywp\tableOfContents\admin\forms\CustomizeForm;
6
+ use luckywp\tableOfContents\admin\widgets\customizeModal\CustomizeModal;
7
+ use luckywp\tableOfContents\admin\widgets\customizeSuccess\CustomizeSuccess;
8
+ use luckywp\tableOfContents\admin\widgets\metabox\Metabox;
9
+ use luckywp\tableOfContents\core\admin\AdminController;
10
+ use luckywp\tableOfContents\core\Core;
11
+ use luckywp\tableOfContents\core\helpers\Json;
12
+ use luckywp\tableOfContents\plugin\PostSettings;
13
+
14
+ class MetaboxController extends AdminController
15
+ {
16
+
17
+ public function init()
18
+ {
19
+ parent::init();
20
+ add_action('plugins_loaded', [$this, 'initAjax']);
21
+ }
22
+
23
+ public function initAjax()
24
+ {
25
+ add_action('wp_ajax_lwptoc_metabox_set_enabled', [$this, 'ajaxSetEnabled']);
26
+ add_action('wp_ajax_lwptoc_metabox_customize', [$this, 'ajaxCustomize']);
27
+ }
28
+
29
+ protected function checkAccess($post)
30
+ {
31
+ Core::$plugin->admin->checkAjaxReferer();
32
+ if (!$post ||
33
+ !current_user_can('edit_post', $post->ID) ||
34
+ !in_array($post->post_type, Core::$plugin->admin->getMetaboxPostTypes())
35
+ ) {
36
+ $this->notAllowed();
37
+ }
38
+ }
39
+
40
+ public function ajaxSetEnabled()
41
+ {
42
+ $post = get_post((int)Core::$plugin->request->get('postId'));
43
+ $this->checkAccess($post);
44
+
45
+ $settings = new PostSettings($post->ID);
46
+ $settings->enabled = (bool)Core::$plugin->request->get('enabled', true);
47
+ $settings->save();
48
+
49
+ echo Metabox::widget(['post' => $post]);
50
+ wp_die();
51
+ }
52
+
53
+ public function ajaxCustomize()
54
+ {
55
+ $post = get_post((int)Core::$plugin->request->get('postId'));
56
+ $this->checkAccess($post);
57
+ $onlyBody = false;
58
+
59
+ $postSettings = new PostSettings($post->ID);
60
+ $model = new CustomizeForm($postSettings);
61
+ if ($model->load(Core::$plugin->request->post())) {
62
+ if ($model->validate()) {
63
+ $model->toPostSettings($postSettings);
64
+ $postSettings->save();
65
+ echo CustomizeSuccess::widget([
66
+ 'after' => '<script>$(document).trigger("lwptocMetaboxCustomized", ' . Json::encode([
67
+ 'metabox' => Metabox::widget(['post' => $post]),
68
+ ]) . ');</script>',
69
+ ]);
70
+ wp_die();
71
+ }
72
+ $onlyBody = true;
73
+ }
74
+
75
+ echo CustomizeModal::widget([
76
+ 'onlyBody' => $onlyBody,
77
+ 'post' => $post,
78
+ 'action' => 'lwptoc_metabox_customize',
79
+ 'model' => $model,
80
+ ]);
81
+ wp_die();
82
+ }
83
+ }
admin/controllers/SettingsController.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\admin\controllers;
4
+
5
+ use luckywp\tableOfContents\core\admin\AdminController;
6
+
7
+ class SettingsController extends AdminController
8
+ {
9
+
10
+ public function actionIndex()
11
+ {
12
+ $this->render('index');
13
+ }
14
+ }
admin/controllers/ShortcodeController.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\admin\controllers;
4
+
5
+ use luckywp\tableOfContents\admin\forms\CustomizeForm;
6
+ use luckywp\tableOfContents\admin\widgets\customizeModal\CustomizeModal;
7
+ use luckywp\tableOfContents\admin\widgets\customizeSuccess\CustomizeSuccess;
8
+ use luckywp\tableOfContents\core\admin\AdminController;
9
+ use luckywp\tableOfContents\core\Core;
10
+ use luckywp\tableOfContents\core\helpers\Json;
11
+ use luckywp\tableOfContents\plugin\PostSettings;
12
+
13
+ class ShortcodeController extends AdminController
14
+ {
15
+
16
+ public function init()
17
+ {
18
+ parent::init();
19
+ add_action('plugins_loaded', [$this, 'initAjax']);
20
+ }
21
+
22
+ public function initAjax()
23
+ {
24
+ add_action('wp_ajax_lwptoc_shortcode_customize', [$this, 'ajaxCustomize']);
25
+ add_action('wp_ajax_lwptoc_shortcode_view', [$this, 'ajaxView']);
26
+ }
27
+
28
+ public function ajaxCustomize()
29
+ {
30
+ Core::$plugin->admin->checkAjaxReferer();
31
+ $post = get_post((int)Core::$plugin->request->get('postId'));
32
+ $attrs = Core::$plugin->request->get('attrs');
33
+ if (!is_array($attrs)) {
34
+ $attrs = [];
35
+ }
36
+ $onlyBody = false;
37
+
38
+ $postSettings = new PostSettings($post->ID);
39
+ $model = new CustomizeForm($postSettings, $attrs);
40
+ if ($model->load(Core::$plugin->request->post())) {
41
+ if ($model->validate()) {
42
+ echo CustomizeSuccess::widget([
43
+ 'after' => '<script>$(document).trigger("lwptocShortcodeGenerated", ' . Json::encode(['shortcode' => $model->generateShortcode()]) . ');</script>',
44
+ ]);
45
+ wp_die();
46
+ }
47
+ $onlyBody = true;
48
+ }
49
+
50
+ echo CustomizeModal::widget([
51
+ 'onlyBody' => $onlyBody,
52
+ 'post' => $post,
53
+ 'action' => 'lwptoc_shortcode_customize',
54
+ 'model' => $model,
55
+ ]);
56
+ wp_die();
57
+ }
58
+
59
+ public function ajaxView()
60
+ {
61
+ Core::$plugin->admin->checkAjaxReferer();
62
+ $attrs = Core::$plugin->request->get('attrs');
63
+ if (!is_array($attrs)) {
64
+ $attrs = [];
65
+ }
66
+ $rows = Core::$plugin->admin->overrideSettingsToRows($attrs);
67
+ echo '<div class="lwptocShortcode">';
68
+ echo '<div class="lwptocShortcode_title">' . esc_html__('Table of Contents', 'lwptoc') . '</div>';
69
+ if ($rows) {
70
+ echo '<div class="lwptocShortcode_items">';
71
+ foreach ($rows as $row) {
72
+ echo '<div class="lwptocShortcode_item">';
73
+ echo '<span class="lwptocShortcode_item_label">' . esc_html($row[0]) . ':</span> ';
74
+ echo $row[1] === null ? '<i>' . __('empty', 'lwptoc') . '</i>' : $row[1];
75
+ echo '</div>';
76
+ }
77
+ echo '</div>';
78
+ }
79
+ echo '</div>';
80
+ wp_die();
81
+ }
82
+ }
admin/forms/CustomizeForm.php ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\admin\forms;
4
+
5
+ use luckywp\tableOfContents\core\base\Model;
6
+ use luckywp\tableOfContents\core\Core;
7
+ use luckywp\tableOfContents\core\helpers\ArrayHelper;
8
+ use luckywp\tableOfContents\plugin\PostSettings;
9
+
10
+ class CustomizeForm extends Model
11
+ {
12
+
13
+ public $defaultMin;
14
+ public $min;
15
+
16
+ public $defaultDepth;
17
+ public $depth;
18
+
19
+ public $defaultHierarchical;
20
+ public $hierarchical;
21
+
22
+ public $defaultNumeration;
23
+ public $numeration;
24
+
25
+ public $defaultTitle;
26
+ public $title;
27
+
28
+ public $defaultToggle;
29
+ public $toggle;
30
+
31
+ public $defaultLabelShow;
32
+ public $labelShow;
33
+
34
+ public $defaultLabelHide;
35
+ public $labelHide;
36
+
37
+ public $defaultHideItems;
38
+ public $hideItems;
39
+
40
+ public $defaultSmoothScroll;
41
+ public $smoothScroll;
42
+
43
+ public $defaultSmoothScrollOffset;
44
+ public $smoothScrollOffset;
45
+
46
+ public $defaultPosition;
47
+ public $position;
48
+
49
+ public $defaultWidth;
50
+ public $width;
51
+
52
+ public $defaultFloat;
53
+ public $float;
54
+
55
+ public $defaultTitleFontSize;
56
+ public $titleFontSize;
57
+
58
+ public $defaultTitleFontWeight;
59
+ public $titleFontWeight;
60
+
61
+ public $defaultItemsFontSize;
62
+ public $itemsFontSize;
63
+
64
+ public $defaultColorScheme;
65
+ public $colorScheme;
66
+
67
+ public $defaultBackgroundColor;
68
+ public $backgroundColor;
69
+
70
+ public $defaultBorderColor;
71
+ public $borderColor;
72
+
73
+ public $defaultTitleColor;
74
+ public $titleColor;
75
+
76
+ public $defaultLinkColor;
77
+ public $linkColor;
78
+
79
+ public $defaultHoverLinkColor;
80
+ public $hoverLinkColor;
81
+
82
+ public $defaultVisitedLinkColor;
83
+ public $visitedLinkColor;
84
+
85
+ /**
86
+ * @var PostSettings
87
+ */
88
+ public $postSettings;
89
+
90
+ /**
91
+ * @var bool
92
+ */
93
+ public $isPostSettings;
94
+
95
+ protected $vars = [
96
+ 'min',
97
+ 'depth',
98
+ 'hierarchical',
99
+ 'numeration',
100
+ 'title',
101
+ 'toggle',
102
+ 'labelShow',
103
+ 'labelHide',
104
+ 'hideItems',
105
+ 'smoothScroll',
106
+ 'smoothScrollOffset',
107
+ 'width',
108
+ 'float',
109
+ 'titleFontSize',
110
+ 'titleFontWeight',
111
+ 'itemsFontSize',
112
+ 'colorScheme',
113
+ 'backgroundColor',
114
+ 'borderColor',
115
+ 'titleColor',
116
+ 'linkColor',
117
+ 'hoverLinkColor',
118
+ 'visitedLinkColor',
119
+ ];
120
+
121
+ /**
122
+ * @param PostSettings $postSettings
123
+ * @param array|null $attrs
124
+ * @param array $config
125
+ */
126
+ public function __construct($postSettings, $attrs = null, array $config = [])
127
+ {
128
+ $this->postSettings = $postSettings;
129
+ $this->isPostSettings = $attrs === null;
130
+ if ($this->isPostSettings) {
131
+ $this->vars[] = 'position';
132
+ }
133
+
134
+ if (is_array($attrs)) {
135
+ $attrs = array_change_key_case($attrs, CASE_LOWER);
136
+ }
137
+
138
+ foreach ($this->vars as $var) {
139
+ $value = $this->isPostSettings ? $postSettings->$var : ArrayHelper::getValue($attrs, strtolower($var));
140
+ $this->{'default' . ucfirst($var)} = ($value === null) ? 1 : 0;
141
+ if (!$this->{'default' . ucfirst($var)}) {
142
+ $this->$var = $value;
143
+ }
144
+ }
145
+ parent::__construct($config);
146
+ }
147
+
148
+ public function rules()
149
+ {
150
+ return [
151
+ [
152
+ array_map(function ($v) {
153
+ return 'default' . ucfirst($v);
154
+ }, $this->vars),
155
+ 'boolean'
156
+ ],
157
+ ['min', 'filter', 'filter' => 'intval'],
158
+ ['depth', 'filter', 'filter' => 'intval'],
159
+ ['depth', 'in', 'range' => array_keys(Core::$plugin->depthsList)],
160
+ ['hierarchical', 'boolean'],
161
+ ['numeration', 'in', 'range' => array_keys(Core::$plugin->numerationsList)],
162
+ ['title', 'filter', 'filter' => 'trim'],
163
+ ['toggle', 'boolean'],
164
+ [['labelShow', 'labelHide'], 'filter', 'filter' => 'trim'],
165
+ [
166
+ 'labelShow',
167
+ 'required',
168
+ 'when' => function () {
169
+ return !$this->defaultLabelShow;
170
+ }
171
+ ],
172
+ [
173
+ 'labelHide',
174
+ 'required',
175
+ 'when' => function () {
176
+ return !$this->defaultLabelHide;
177
+ }
178
+ ],
179
+ ['hideItems', 'boolean'],
180
+ ['smoothScroll', 'boolean'],
181
+ ['smoothScrollOffset', 'filter', 'filter' => 'intval'],
182
+ ['position', 'in', 'range' => array_keys(Core::$plugin->positionsList)],
183
+ ['width', 'filter', 'filter' => [Core::$plugin->settings, 'sanitizeWidth']],
184
+ ['float', 'in', 'range' => array_keys(Core::$plugin->floatsList)],
185
+ ['titleFontSize', 'filter', 'filter' => [Core::$plugin->settings, 'sanitizeFontSize']],
186
+ ['titleFontWeight', 'in', 'range' => array_keys(Core::$plugin->fontWeightsList)],
187
+ ['itemsFontSize', 'filter', 'filter' => [Core::$plugin->settings, 'sanitizeFontSize']],
188
+ ['colorScheme', 'in', 'range' => array_keys(Core::$plugin->colorSchemesList)],
189
+ [
190
+ ['backgroundColor', 'borderColor', 'titleColor', 'linkColor', 'hoverLinkColor', 'visitedLinkColor'],
191
+ 'filter',
192
+ 'filter' => [Core::$plugin->settings, 'sanitizeCallbackColor'],
193
+ ],
194
+ ];
195
+ }
196
+
197
+ /**
198
+ * @return array
199
+ */
200
+ public function attributeLabels()
201
+ {
202
+ return [
203
+ 'depth' => esc_html__('Depth', 'lwptoc'),
204
+ 'numeration' => esc_html__('Numeration', 'lwptoc'),
205
+ 'labelShow' => esc_html__('Label Show', 'lwptoc'),
206
+ 'labelHide' => esc_html__('Label Hide', 'lwptoc'),
207
+ 'position' => esc_html__('Position', 'lwptoc'),
208
+ 'float' => esc_html__('Float', 'lwptoc'),
209
+ 'titleFontWeight' => esc_html__('Title Font Weight', 'lwptoc'),
210
+ 'colorScheme' => esc_html__('Color Scheme', 'lwptoc'),
211
+ ];
212
+ }
213
+
214
+ /**
215
+ * @param PostSettings $postSettings
216
+ */
217
+ public function toPostSettings($postSettings)
218
+ {
219
+ $postSettings->position = $this->defaultPosition ? null : $this->position;
220
+ foreach ($this->getAttrs() as $var => $value) {
221
+ $postSettings->$var = $value;
222
+ }
223
+ }
224
+
225
+ /**
226
+ * @return string
227
+ */
228
+ public function generateShortcode()
229
+ {
230
+ return Core::$plugin->shortcode->make($this->getAttrs());
231
+ }
232
+
233
+ /**
234
+ * @return array
235
+ */
236
+ public function getAttrs()
237
+ {
238
+ return [
239
+ 'min' => $this->defaultMin ? null : ($this->min < 0 ? 0 : $this->min),
240
+ 'depth' => $this->defaultDepth ? null : $this->depth,
241
+ 'hierarchical' => $this->defaultHierarchical ? null : (bool)$this->hierarchical,
242
+ 'numeration' => $this->defaultNumeration ? null : $this->numeration,
243
+ 'title' => $this->defaultTitle ? null : $this->title,
244
+ 'toggle' => $this->defaultToggle ? null : (bool)$this->toggle,
245
+ 'labelShow' => $this->defaultLabelShow ? null : $this->labelShow,
246
+ 'labelHide' => $this->defaultLabelHide ? null : $this->labelHide,
247
+ 'hideItems' => $this->defaultHideItems ? null : (bool)$this->hideItems,
248
+ 'smoothScroll' => $this->defaultSmoothScroll ? null : (bool)$this->smoothScroll,
249
+ 'smoothScrollOffset' => $this->defaultSmoothScrollOffset ? null : (int)$this->smoothScrollOffset,
250
+ 'width' => $this->defaultWidth ? null : $this->width,
251
+ 'float' => $this->defaultFloat ? null : $this->float,
252
+ 'titleFontSize' => $this->defaultTitleFontSize ? null : $this->titleFontSize,
253
+ 'titleFontWeight' => $this->defaultTitleFontWeight ? null : $this->titleFontWeight,
254
+ 'itemsFontSize' => $this->defaultItemsFontSize ? null : $this->itemsFontSize,
255
+ 'colorScheme' => $this->defaultColorScheme ? null : $this->colorScheme,
256
+ 'backgroundColor' => $this->defaultBackgroundColor ? null : ($this->backgroundColor ? $this->backgroundColor : ''),
257
+ 'borderColor' => $this->defaultBorderColor ? null : ($this->borderColor ? $this->borderColor : ''),
258
+ 'titleColor' => $this->defaultTitleColor ? null : ($this->titleColor ? $this->titleColor : ''),
259
+ 'linkColor' => $this->defaultLinkColor ? null : ($this->linkColor ? $this->linkColor : ''),
260
+ 'hoverLinkColor' => $this->defaultHoverLinkColor ? null : ($this->hoverLinkColor ? $this->hoverLinkColor : ''),
261
+ 'visitedLinkColor' => $this->defaultVisitedLinkColor ? null : ($this->visitedLinkColor ? $this->visitedLinkColor : ''),
262
+ ];
263
+ }
264
+ }
admin/views/settings/index.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use luckywp\tableOfContents\core\Core;
4
+
5
+ ?>
6
+ <div class="wrap">
7
+ <h1><?= esc_html__('Table of Contents Settings', 'lwptoc') ?></h1>
8
+ <?php Core::$plugin->settings->showPage(false) ?>
9
+ </div>
admin/widgets/OverrideColorBadge.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\admin\widgets;
4
+
5
+ use luckywp\tableOfContents\core\base\Widget;
6
+
7
+ class OverrideColorBadge extends Widget
8
+ {
9
+
10
+ /**
11
+ * @var string
12
+ */
13
+ public $color;
14
+
15
+ public function run()
16
+ {
17
+ return $this->color ? '<span class="lwptocColorBadge"><b style="background:' . $this->color . '"></b>' . $this->color . '</span>' : esc_html__('from scheme', 'lwptoc');
18
+ }
19
+ }
admin/widgets/PostTypes.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\admin\widgets;
4
+
5
+ use luckywp\tableOfContents\core\base\Widget;
6
+ use luckywp\tableOfContents\core\Core;
7
+ use luckywp\tableOfContents\core\helpers\ArrayHelper;
8
+ use luckywp\tableOfContents\core\helpers\Html;
9
+
10
+ class PostTypes extends Widget
11
+ {
12
+
13
+ /**
14
+ * @var array
15
+ */
16
+ public $field;
17
+
18
+ /**
19
+ * @var array
20
+ */
21
+ public $containerOptions = [];
22
+
23
+ public function run()
24
+ {
25
+ $value = Core::$plugin->settings->getValue($this->field['group'], $this->field['id'], [], false);
26
+ if (!is_array($value)) {
27
+ $value = [];
28
+ }
29
+
30
+ // Типы постов
31
+ $postTypes = ArrayHelper::map(Core::$plugin->postTypes, 'name', 'labels.singular_name');
32
+
33
+ // HTML
34
+ $html = Html::beginTag('div', $this->containerOptions);
35
+ $html .= Html::hiddenInput($this->field['name']);
36
+ foreach ($postTypes as $postName => $postLabel) {
37
+ $options = [
38
+ 'label' => $postLabel,
39
+ 'value' => $postName,
40
+ ];
41
+ $html .= '<p>' . Html::checkbox($this->field['name'] . '[]', in_array($postName, $value), $options) . '</p>';
42
+ }
43
+ $html .= '</div>';
44
+ return $html;
45
+ }
46
+ }
admin/widgets/customizeModal/CustomizeModal.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\admin\widgets\customizeModal;
4
+
5
+ use luckywp\tableOfContents\admin\forms\CustomizeForm;
6
+ use luckywp\tableOfContents\core\base\Widget;
7
+ use WP_Post;
8
+
9
+ class CustomizeModal extends Widget
10
+ {
11
+
12
+ /**
13
+ * @var WP_Post
14
+ */
15
+ public $post;
16
+
17
+ /**
18
+ * @var string
19
+ */
20
+ public $action;
21
+
22
+ /**
23
+ * @var CustomizeForm
24
+ */
25
+ public $model;
26
+
27
+ /**
28
+ * @var bool
29
+ */
30
+ public $onlyBody;
31
+
32
+ public function run()
33
+ {
34
+ return $this->render('modal', [
35
+ 'post' => $this->post,
36
+ 'action' => $this->action,
37
+ 'model' => $this->model,
38
+ 'onlyBody' => $this->onlyBody,
39
+ ]);
40
+ }
41
+ }
admin/widgets/customizeModal/views/modal.php ADDED
@@ -0,0 +1,497 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var $post WP_Post
4
+ * @var $action string
5
+ * @var $model \luckywp\tableOfContents\admin\forms\CustomizeForm
6
+ * @var $onlyBody bool
7
+ */
8
+
9
+ use luckywp\tableOfContents\admin\widgets\fontSizeField\FontSizeField;
10
+ use luckywp\tableOfContents\admin\widgets\OverrideColorBadge;
11
+ use luckywp\tableOfContents\admin\widgets\widthField\WidthField;
12
+ use luckywp\tableOfContents\core\admin\helpers\AdminHtml;
13
+ use luckywp\tableOfContents\core\Core;
14
+ use luckywp\tableOfContents\core\helpers\Html;
15
+
16
+ echo $onlyBody ? '' : '<div class="lwptocCustomize lwptocCustomize-metabox">';
17
+ ?>
18
+ <form
19
+ action="<?= admin_url('admin-ajax.php?_ajax_nonce=' . wp_create_nonce(Core::$plugin->prefix . 'adminMain') . '&action=' . $action . '&postId=' . $post->ID) ?>"
20
+ data-ajax-form="1"
21
+ class="lwptocModalBox"
22
+ >
23
+ <div class="lwptocModalBox_close lwptocModal-close" title="<?= __('Cancel', 'lwptoc') ?>"></div>
24
+ <div class="lwptocModalBox_title"><?= __('Customize Table of Contents', 'lwptoc') ?></div>
25
+ <div class="lwptocModalBox_body">
26
+
27
+ <?php
28
+ if ($model->hasErrors()) {
29
+ echo '<div class="lwptocCustomize_errors">';
30
+ foreach ($model->getErrorSummary() as $error) {
31
+ echo '<p>' . $error . '</p>';
32
+ }
33
+ echo '</div>';
34
+ }
35
+ ?>
36
+
37
+ <div class="lwptocCustomize_tabs">
38
+ <div class="lwptocCustomize_tab lwptocCustomize_tab-active" data-tab="general"><?= esc_html__('General', 'lwptoc') ?></div>
39
+ <div class="lwptocCustomize_tab" data-tab="appearance"><?= esc_html__('Appearance', 'lwptoc') ?></div>
40
+ </div>
41
+
42
+ <div class="lwptocCustomize_fields lwptocCustomize_fields-general" style="display:block;">
43
+
44
+ <?php if ($model->isPostSettings) { ?>
45
+ <div class="lwptocCustomize_field<?= $model->defaultPosition ? ' lwptocCustomize_field-default' : '' ?>">
46
+ <div class="lwptocCustomize_field_header">
47
+ <span class="lwptocCustomize_field_label"><?= __('Position', 'lwptoc') ?></span>
48
+ <span class="lwptocCustomize_field_default"><?= __('default', 'lwptoc') ?></span>
49
+ </div>
50
+ <div class="lwptocCustomize_field_override">
51
+ <?= __('Click for override default value', 'lwptoc') ?>
52
+ </div>
53
+ <div class="lwptocCustomize_field_el">
54
+ <?= Html::dropDownList(Html::getInputName($model, 'position'), $model->defaultPosition ? Core::$plugin->settings->autoInsertPosition : $model->position, Core::$plugin->positionsList, [
55
+ 'class' => 'lwptocCustomize_field_el_select',
56
+ ]) ?>
57
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultPosition'), $model->defaultPosition, [
58
+ 'class' => 'lwptocCustomize_field_inputDefault',
59
+ ]) ?>
60
+ </div>
61
+ <div class="lwptocCustomize_field_defaultValue">
62
+ <?= Core::$plugin->positionsList[Core::$plugin->settings->autoInsertPosition] ?>
63
+ </div>
64
+ </div>
65
+ <?php } ?>
66
+
67
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->min)) ? Core::$plugin->settings->generalMin : $model->postSettings->min ?>
68
+ <div class="lwptocCustomize_field<?= $model->defaultMin ? ' lwptocCustomize_field-default' : '' ?>">
69
+ <div class="lwptocCustomize_field_header">
70
+ <span class="lwptocCustomize_field_label"><?= __('Minimal Count of Headers', 'lwptoc') ?></span>
71
+ <span class="lwptocCustomize_field_default"><?= __('default', 'lwptoc') ?></span>
72
+ </div>
73
+ <div class="lwptocCustomize_field_override">
74
+ <?= __('Click for override default value', 'lwptoc') ?>
75
+ </div>
76
+ <div class="lwptocCustomize_field_el">
77
+ <?= Html::textInput(Html::getInputName($model, 'min'), $model->defaultMin ? $defaultValue : $model->min, ['class' => 'lwptocCustomize_field_el_textInput']) ?>
78
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultMin'), $model->defaultMin, [
79
+ 'class' => 'lwptocCustomize_field_inputDefault',
80
+ ]) ?>
81
+ </div>
82
+ <div class="lwptocCustomize_field_desc">
83
+ <?= __('If the count of headers in the post is less, then table of contents is not displayed.', 'lwptoc') ?>
84
+ </div>
85
+ <div class="lwptocCustomize_field_defaultValue">
86
+ <?= $defaultValue ?>
87
+ </div>
88
+ </div>
89
+
90
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->depth)) ? Core::$plugin->settings->generalDepth : $model->postSettings->depth ?>
91
+ <div class="lwptocCustomize_field<?= $model->defaultDepth ? ' lwptocCustomize_field-default' : '' ?>">
92
+ <div class="lwptocCustomize_field_header">
93
+ <span class="lwptocCustomize_field_label"><?= __('Depth', 'lwptoc') ?></span>
94
+ <span class="lwptocCustomize_field_default"><?= __('default', 'lwptoc') ?></span>
95
+ </div>
96
+ <div class="lwptocCustomize_field_override">
97
+ <?= __('Click for override default value', 'lwptoc') ?>
98
+ </div>
99
+ <div class="lwptocCustomize_field_el">
100
+ <?= Html::dropDownList(Html::getInputName($model, 'depth'), $model->defaultDepth ? $defaultValue : $model->depth, Core::$plugin->depthsList, [
101
+ 'class' => 'lwptocCustomize_field_el_select',
102
+ ]) ?>
103
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultDepth'), $model->defaultDepth, [
104
+ 'class' => 'lwptocCustomize_field_inputDefault',
105
+ ]) ?>
106
+ </div>
107
+ <div class="lwptocCustomize_field_defaultValue">
108
+ <?= Core::$plugin->depthsList[$defaultValue] ?>
109
+ </div>
110
+ </div>
111
+
112
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->hierarchical)) ? Core::$plugin->settings->generalHierarchical : $model->postSettings->hierarchical ?>
113
+ <div class="lwptocCustomize_field<?= $model->defaultHierarchical ? ' lwptocCustomize_field-default' : '' ?>">
114
+ <div class="lwptocCustomize_field_header">
115
+ <span class="lwptocCustomize_field_label"><?= __('Hierarchical View', 'lwptoc') ?></span>
116
+ <span class="lwptocCustomize_field_default"><?= __('default', 'lwptoc') ?></span>
117
+ </div>
118
+ <div class="lwptocCustomize_field_override">
119
+ <?= __('Click for override default value', 'lwptoc') ?>
120
+ </div>
121
+ <div class="lwptocCustomize_field_el">
122
+ <label>
123
+ <?= Html::checkbox(Html::getInputName($model, 'hierarchical'), $model->defaultHierarchical ? $defaultValue : $model->hierarchical, ['uncheck' => 0]) ?>
124
+ <?= __('Enable', 'lwptoc') ?>
125
+ </label>
126
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultHierarchical'), $model->defaultHierarchical, [
127
+ 'class' => 'lwptocCustomize_field_inputDefault',
128
+ ]) ?>
129
+ </div>
130
+ <div class="lwptocCustomize_field_defaultValue">
131
+ <?= $defaultValue ? __('Enabled', 'lwptoc') : __('Disabled', 'lwptoc') ?>
132
+ </div>
133
+ </div>
134
+
135
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->numeration)) ? Core::$plugin->settings->generalNumeration : $model->postSettings->numeration ?>
136
+ <div class="lwptocCustomize_field<?= $model->defaultNumeration ? ' lwptocCustomize_field-default' : '' ?>">
137
+ <div class="lwptocCustomize_field_header">
138
+ <span class="lwptocCustomize_field_label"><?= __('Numeration', 'lwptoc') ?></span>
139
+ <span class="lwptocCustomize_field_default"><?= __('default', 'lwptoc') ?></span>
140
+ </div>
141
+ <div class="lwptocCustomize_field_override">
142
+ <?= __('Click for override default value', 'lwptoc') ?>
143
+ </div>
144
+ <div class="lwptocCustomize_field_el">
145
+ <?= Html::dropDownList(Html::getInputName($model, 'numeration'), $model->defaultNumeration ? $defaultValue : $model->numeration, Core::$plugin->numerationsList, [
146
+ 'class' => 'lwptocCustomize_field_el_select',
147
+ ]) ?>
148
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultNumeration'), $model->defaultNumeration, [
149
+ 'class' => 'lwptocCustomize_field_inputDefault',
150
+ ]) ?>
151
+ </div>
152
+ <div class="lwptocCustomize_field_defaultValue">
153
+ <?= Core::$plugin->numerationsList[$defaultValue] ?>
154
+ </div>
155
+ </div>
156
+
157
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->title)) ? Core::$plugin->settings->generalTitle : $model->postSettings->title ?>
158
+ <div class="lwptocCustomize_field<?= $model->defaultTitle ? ' lwptocCustomize_field-default' : '' ?>">
159
+ <div class="lwptocCustomize_field_header">
160
+ <span class="lwptocCustomize_field_label"><?= __('Title', 'lwptoc') ?></span>
161
+ <span class="lwptocCustomize_field_default"><?= __('default', 'lwptoc') ?></span>
162
+ </div>
163
+ <div class="lwptocCustomize_field_override">
164
+ <?= __('Click for override default value', 'lwptoc') ?>
165
+ </div>
166
+ <div class="lwptocCustomize_field_el">
167
+ <?= Html::textInput(Html::getInputName($model, 'title'), $model->defaultTitle ? $defaultValue : $model->title, ['class' => 'lwptocCustomize_field_el_textInput']) ?>
168
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultTitle'), $model->defaultTitle, [
169
+ 'class' => 'lwptocCustomize_field_inputDefault',
170
+ ]) ?>
171
+ </div>
172
+ <div class="lwptocCustomize_field_defaultValue">
173
+ <?php
174
+ if ('' == $defaultValue) {
175
+ echo '<i>' . __('Without title', 'lwptoc') . '</i>';
176
+ } else {
177
+ echo $defaultValue;
178
+ }
179
+ ?>
180
+ </div>
181
+ </div>
182
+
183
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->toggle)) ? Core::$plugin->settings->generalToggle : $model->postSettings->toggle ?>
184
+ <div class="lwptocCustomize_field<?= $model->defaultToggle ? ' lwptocCustomize_field-default' : '' ?>">
185
+ <div class="lwptocCustomize_field_header">
186
+ <span class="lwptocCustomize_field_label"><?= __('Toggle Show/Hide', 'lwptoc') ?></span>
187
+ <span class="lwptocCustomize_field_default"><?= __('default', 'lwptoc') ?></span>
188
+ </div>
189
+ <div class="lwptocCustomize_field_override">
190
+ <?= __('Click for override default value', 'lwptoc') ?>
191
+ </div>
192
+ <div class="lwptocCustomize_field_el">
193
+ <label>
194
+ <?= Html::checkbox(Html::getInputName($model, 'toggle'), $model->defaultToggle ? $defaultValue : $model->toggle, ['uncheck' => 0]) ?>
195
+ <?= __('Enable', 'lwptoc') ?>
196
+ </label>
197
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultToggle'), $model->defaultToggle, [
198
+ 'class' => 'lwptocCustomize_field_inputDefault',
199
+ ]) ?>
200
+ </div>
201
+ <div class="lwptocCustomize_field_defaultValue">
202
+ <?= $defaultValue ? __('Enabled', 'lwptoc') : __('Disabled', 'lwptoc') ?>
203
+ </div>
204
+ </div>
205
+
206
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->labelShow)) ? Core::$plugin->settings->generalLabelShow : $model->postSettings->labelShow ?>
207
+ <div class="lwptocCustomize_field<?= $model->defaultLabelShow ? ' lwptocCustomize_field-default' : '' ?>">
208
+ <div class="lwptocCustomize_field_header">
209
+ <span class="lwptocCustomize_field_label"><?= __('Label Show', 'lwptoc') ?></span>
210
+ <span class="lwptocCustomize_field_default"><?= __('default', 'lwptoc') ?></span>
211
+ </div>
212
+ <div class="lwptocCustomize_field_override">
213
+ <?= __('Click for override default value', 'lwptoc') ?>
214
+ </div>
215
+ <div class="lwptocCustomize_field_el">
216
+ <?= Html::textInput(Html::getInputName($model, 'labelShow'), $model->defaultLabelShow ? $defaultValue : $model->labelShow, ['class' => 'lwptocCustomize_field_el_textInput']) ?>
217
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultLabelShow'), $model->defaultLabelShow, [
218
+ 'class' => 'lwptocCustomize_field_inputDefault',
219
+ ]) ?>
220
+ </div>
221
+ <div class="lwptocCustomize_field_defaultValue">
222
+ <?= $defaultValue ?>
223
+ </div>
224
+ </div>
225
+
226
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->labelHide)) ? Core::$plugin->settings->generalLabelHide : $model->postSettings->labelHide ?>
227
+ <div class="lwptocCustomize_field<?= $model->defaultLabelHide ? ' lwptocCustomize_field-default' : '' ?>">
228
+ <div class="lwptocCustomize_field_header">
229
+ <span class="lwptocCustomize_field_label"><?= __('Label Hide', 'lwptoc') ?></span>
230
+ <span class="lwptocCustomize_field_default"><?= __('default', 'lwptoc') ?></span>
231
+ </div>
232
+ <div class="lwptocCustomize_field_override">
233
+ <?= __('Click for override default value', 'lwptoc') ?>
234
+ </div>
235
+ <div class="lwptocCustomize_field_el">
236
+ <?= Html::textInput(Html::getInputName($model, 'labelHide'), $model->defaultLabelHide ? $defaultValue : $model->labelHide, ['class' => 'lwptocCustomize_field_el_textInput']) ?>
237
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultLabelHide'), $model->defaultLabelHide, [
238
+ 'class' => 'lwptocCustomize_field_inputDefault',
239
+ ]) ?>
240
+ </div>
241
+ <div class="lwptocCustomize_field_defaultValue">
242
+ <?= $defaultValue ?>
243
+ </div>
244
+ </div>
245
+
246
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->hideItems)) ? Core::$plugin->settings->generalHideItems : $model->postSettings->hideItems ?>
247
+ <div class="lwptocCustomize_field<?= $model->defaultHideItems ? ' lwptocCustomize_field-default' : '' ?>">
248
+ <div class="lwptocCustomize_field_header">
249
+ <span class="lwptocCustomize_field_label"><?= __('By default, items of contents will be hidden', 'lwptoc') ?></span>
250
+ <span class="lwptocCustomize_field_default"><?= __('default', 'lwptoc') ?></span>
251
+ </div>
252
+ <div class="lwptocCustomize_field_override">
253
+ <?= __('Click for override default value', 'lwptoc') ?>
254
+ </div>
255
+ <div class="lwptocCustomize_field_el">
256
+ <label>
257
+ <?= Html::checkbox(Html::getInputName($model, 'hideItems'), $model->defaultHideItems ? $defaultValue : $model->hideItems, ['uncheck' => 0]) ?>
258
+ <?= __('Enable', 'lwptoc') ?>
259
+ </label>
260
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultHideItems'), $model->defaultHideItems, [
261
+ 'class' => 'lwptocCustomize_field_inputDefault',
262
+ ]) ?>
263
+ </div>
264
+ <div class="lwptocCustomize_field_defaultValue">
265
+ <?= $defaultValue ? __('Enabled', 'lwptoc') : __('Disabled', 'lwptoc') ?>
266
+ </div>
267
+ </div>
268
+
269
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->smoothScroll)) ? Core::$plugin->settings->generalSmoothScroll : $model->postSettings->smoothScroll ?>
270
+ <div class="lwptocCustomize_field<?= $model->defaultSmoothScroll ? ' lwptocCustomize_field-default' : '' ?>">
271
+ <div class="lwptocCustomize_field_header">
272
+ <span class="lwptocCustomize_field_label"><?= __('Smooth Scroll', 'lwptoc') ?></span>
273
+ <span class="lwptocCustomize_field_default"><?= __('default', 'lwptoc') ?></span>
274
+ </div>
275
+ <div class="lwptocCustomize_field_override">
276
+ <?= __('Click for override default value', 'lwptoc') ?>
277
+ </div>
278
+ <div class="lwptocCustomize_field_el">
279
+ <label>
280
+ <?= Html::checkbox(Html::getInputName($model, 'smoothScroll'), $model->defaultSmoothScroll ? $defaultValue : $model->smoothScroll, ['uncheck' => 0]) ?>
281
+ <?= __('Enable', 'lwptoc') ?>
282
+ </label>
283
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultSmoothScroll'), $model->defaultSmoothScroll, [
284
+ 'class' => 'lwptocCustomize_field_inputDefault',
285
+ ]) ?>
286
+ </div>
287
+ <div class="lwptocCustomize_field_defaultValue">
288
+ <?= $defaultValue ? __('Enabled', 'lwptoc') : __('Disabled', 'lwptoc') ?>
289
+ </div>
290
+ </div>
291
+
292
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->smoothScrollOffset)) ? Core::$plugin->settings->generalSmoothScrollOffset : $model->postSettings->smoothScrollOffset ?>
293
+ <div class="lwptocCustomize_field<?= $model->defaultSmoothScrollOffset ? ' lwptocCustomize_field-default' : '' ?>">
294
+ <div class="lwptocCustomize_field_header">
295
+ <span class="lwptocCustomize_field_label"><?= __('Smooth Scroll Offset Top', 'lwptoc') ?></span>
296
+ <span class="lwptocCustomize_field_default"><?= __('default', 'lwptoc') ?></span>
297
+ </div>
298
+ <div class="lwptocCustomize_field_override">
299
+ <?= __('Click for override default value', 'lwptoc') ?>
300
+ </div>
301
+ <div class="lwptocCustomize_field_el">
302
+ <?= Html::textInput(Html::getInputName($model, 'smoothScrollOffset'), $model->defaultSmoothScrollOffset ? $defaultValue : $model->smoothScrollOffset, ['class' => 'lwptocCustomize_field_el_textInput']) ?>
303
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultSmoothScrollOffset'), $model->defaultSmoothScrollOffset, [
304
+ 'class' => 'lwptocCustomize_field_inputDefault',
305
+ ]) ?>
306
+ </div>
307
+ <div class="lwptocCustomize_field_defaultValue">
308
+ <?= $defaultValue ?>px
309
+ </div>
310
+ </div>
311
+
312
+ </div>
313
+
314
+ <div class="lwptocCustomize_fields lwptocCustomize_fields-appearance">
315
+
316
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->width)) ? Core::$plugin->settings->appearanceWidth : $model->postSettings->width ?>
317
+ <div class="lwptocCustomize_field<?= $model->defaultWidth ? ' lwptocCustomize_field-default' : '' ?>">
318
+ <div class="lwptocCustomize_field_header">
319
+ <span class="lwptocCustomize_field_label"><?= esc_html__('Width', 'lwptoc') ?></span>
320
+ <span class="lwptocCustomize_field_default"><?= esc_html__('default', 'lwptoc') ?></span>
321
+ </div>
322
+ <div class="lwptocCustomize_field_override">
323
+ <?= __('Click for override default value', 'lwptoc') ?>
324
+ </div>
325
+ <div class="lwptocCustomize_field_el">
326
+ <?= WidthField::widget([
327
+ 'name' => Html::getInputName($model, 'width'),
328
+ 'value' => $model->defaultWidth ? $defaultValue : $model->width,
329
+ ]) ?>
330
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultWidth'), $model->defaultWidth, [
331
+ 'class' => 'lwptocCustomize_field_inputDefault',
332
+ ]) ?>
333
+ </div>
334
+ <div class="lwptocCustomize_field_defaultValue">
335
+ <?= Core::$plugin->widthToLabel($defaultValue) ?>
336
+ </div>
337
+ </div>
338
+
339
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->float)) ? Core::$plugin->settings->appearanceFloat : $model->postSettings->float ?>
340
+ <div class="lwptocCustomize_field<?= $model->defaultFloat ? ' lwptocCustomize_field-default' : '' ?>">
341
+ <div class="lwptocCustomize_field_header">
342
+ <span class="lwptocCustomize_field_label"><?= esc_html__('Float', 'lwptoc') ?></span>
343
+ <span class="lwptocCustomize_field_default"><?= esc_html__('default', 'lwptoc') ?></span>
344
+ </div>
345
+ <div class="lwptocCustomize_field_override">
346
+ <?= __('Click for override default value', 'lwptoc') ?>
347
+ </div>
348
+ <div class="lwptocCustomize_field_el">
349
+ <?= Html::dropDownList(Html::getInputName($model, 'float'), $model->defaultFloat ? $defaultValue : $model->float, Core::$plugin->floatsList, [
350
+ 'class' => 'lwptocCustomize_field_el_select',
351
+ ]) ?>
352
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultFloat'), $model->defaultFloat, [
353
+ 'class' => 'lwptocCustomize_field_inputDefault',
354
+ ]) ?>
355
+ </div>
356
+ <div class="lwptocCustomize_field_defaultValue">
357
+ <?= Core::$plugin->floatsList[$defaultValue] ?>
358
+ </div>
359
+ </div>
360
+
361
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->titleFontSize)) ? Core::$plugin->settings->appearanceTitleFontSize : $model->postSettings->titleFontSize ?>
362
+ <div class="lwptocCustomize_field<?= $model->defaultTitleFontSize ? ' lwptocCustomize_field-default' : '' ?>">
363
+ <div class="lwptocCustomize_field_header">
364
+ <span class="lwptocCustomize_field_label"><?= esc_html__('Title Font Size', 'lwptoc') ?></span>
365
+ <span class="lwptocCustomize_field_default"><?= esc_html__('default', 'lwptoc') ?></span>
366
+ </div>
367
+ <div class="lwptocCustomize_field_override">
368
+ <?= __('Click for override default value', 'lwptoc') ?>
369
+ </div>
370
+ <div class="lwptocCustomize_field_el">
371
+ <?= FontSizeField::widget([
372
+ 'name' => Html::getInputName($model, 'titleFontSize'),
373
+ 'value' => $model->defaultTitleFontSize ? $defaultValue : $model->titleFontSize,
374
+ 'defaultSize' => 100,
375
+ ]) ?>
376
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultTitleFontSize'), $model->defaultTitleFontSize, [
377
+ 'class' => 'lwptocCustomize_field_inputDefault',
378
+ ]) ?>
379
+ </div>
380
+ <div class="lwptocCustomize_field_defaultValue">
381
+ <?= Core::$plugin->fontSizeToLabel($defaultValue) ?>
382
+ </div>
383
+ </div>
384
+
385
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->titleFontWeight)) ? Core::$plugin->settings->appearanceTitleFontWeight : $model->postSettings->titleFontWeight ?>
386
+ <div class="lwptocCustomize_field<?= $model->defaultTitleFontWeight ? ' lwptocCustomize_field-default' : '' ?>">
387
+ <div class="lwptocCustomize_field_header">
388
+ <span class="lwptocCustomize_field_label"><?= esc_html__('Title Font Weight', 'lwptoc') ?></span>
389
+ <span class="lwptocCustomize_field_default"><?= esc_html__('default', 'lwptoc') ?></span>
390
+ </div>
391
+ <div class="lwptocCustomize_field_override">
392
+ <?= __('Click for override default value', 'lwptoc') ?>
393
+ </div>
394
+ <div class="lwptocCustomize_field_el">
395
+ <?= Html::dropDownList(Html::getInputName($model, 'titleFontWeight'), $model->defaultTitleFontWeight ? $defaultValue : $model->titleFontWeight, Core::$plugin->fontWeightsList, [
396
+ 'class' => 'lwptocCustomize_field_el_select',
397
+ ]) ?>
398
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultTitleFontWeight'), $model->defaultTitleFontWeight, [
399
+ 'class' => 'lwptocCustomize_field_inputDefault',
400
+ ]) ?>
401
+ </div>
402
+ <div class="lwptocCustomize_field_defaultValue">
403
+ <?= Core::$plugin->fontWeightsList[$defaultValue] ?>
404
+ </div>
405
+ </div>
406
+
407
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->itemsFontSize)) ? Core::$plugin->settings->appearanceItemsFontSize : $model->postSettings->itemsFontSize ?>
408
+ <div class="lwptocCustomize_field<?= $model->defaultItemsFontSize ? ' lwptocCustomize_field-default' : '' ?>">
409
+ <div class="lwptocCustomize_field_header">
410
+ <span class="lwptocCustomize_field_label"><?= esc_html__('Items Font Size', 'lwptoc') ?></span>
411
+ <span class="lwptocCustomize_field_default"><?= esc_html__('default', 'lwptoc') ?></span>
412
+ </div>
413
+ <div class="lwptocCustomize_field_override">
414
+ <?= __('Click for override default value', 'lwptoc') ?>
415
+ </div>
416
+ <div class="lwptocCustomize_field_el">
417
+ <?= FontSizeField::widget([
418
+ 'name' => Html::getInputName($model, 'itemsFontSize'),
419
+ 'value' => $model->defaultItemsFontSize ? $defaultValue : $model->itemsFontSize,
420
+ 'defaultSize' => 90,
421
+ ]) ?>
422
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultItemsFontSize'), $model->defaultItemsFontSize, [
423
+ 'class' => 'lwptocCustomize_field_inputDefault',
424
+ ]) ?>
425
+ </div>
426
+ <div class="lwptocCustomize_field_defaultValue">
427
+ <?= Core::$plugin->fontSizeToLabel($defaultValue) ?>
428
+ </div>
429
+ </div>
430
+
431
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->colorScheme)) ? Core::$plugin->settings->appearanceColorScheme : $model->postSettings->colorScheme ?>
432
+ <div class="lwptocCustomize_field<?= $model->defaultColorScheme ? ' lwptocCustomize_field-default' : '' ?>">
433
+ <div class="lwptocCustomize_field_header">
434
+ <span class="lwptocCustomize_field_label"><?= esc_html__('Color Scheme', 'lwptoc') ?></span>
435
+ <span class="lwptocCustomize_field_default"><?= esc_html__('default', 'lwptoc') ?></span>
436
+ </div>
437
+ <div class="lwptocCustomize_field_override">
438
+ <?= __('Click for override default value', 'lwptoc') ?>
439
+ </div>
440
+ <div class="lwptocCustomize_field_el">
441
+ <?= Html::dropDownList(Html::getInputName($model, 'colorScheme'), $model->defaultColorScheme ? $defaultValue : $model->colorScheme, Core::$plugin->colorSchemesList, [
442
+ 'class' => 'lwptocCustomize_field_el_select',
443
+ ]) ?>
444
+ <?= Html::hiddenInput(Html::getInputName($model, 'defaultColorScheme'), $model->defaultColorScheme, [
445
+ 'class' => 'lwptocCustomize_field_inputDefault',
446
+ ]) ?>
447
+ </div>
448
+ <div class="lwptocCustomize_field_defaultValue">
449
+ <?= Core::$plugin->colorSchemesList[$defaultValue] ?>
450
+ </div>
451
+ </div>
452
+
453
+ <?php foreach ([
454
+ 'backgroundColor' => 'Background Color',
455
+ 'borderColor' => 'Border Color',
456
+ 'titleColor' => 'Title Color',
457
+ 'linkColor' => 'Link Color',
458
+ 'hoverLinkColor' => 'Hover Link Color',
459
+ 'visitedLinkColor' => 'Visited Link Color',
460
+ ] as $var => $label) { ?>
461
+ <?php $defaultValue = ($model->isPostSettings || is_null($model->postSettings->$var)) ? Core::$plugin->settings->{'appearance' . ucfirst($var)} : $model->postSettings->$var ?>
462
+ <div class="lwptocCustomize_field<?= $model->{'default' . ucfirst($var)} ? ' lwptocCustomize_field-default' : '' ?>">
463
+ <div class="lwptocCustomize_field_header">
464
+ <span class="lwptocCustomize_field_label"><?= esc_html__($label, 'lwptoc') ?></span>
465
+ <span class="lwptocCustomize_field_default"><?= esc_html__('default', 'lwptoc') ?></span>
466
+ </div>
467
+ <div class="lwptocCustomize_field_override">
468
+ <?= esc_html__('Click for override default value', 'lwptoc') ?>
469
+ </div>
470
+ <div class="lwptocCustomize_field_defaultValue">
471
+ <?= OverrideColorBadge::widget(['color' => Core::$plugin->settings->{'appearance' . ucfirst($var)}]) ?>
472
+ </div>
473
+ <div class="lwptocCustomize_field_el">
474
+ <?= Html::textInput(Html::getInputName($model, $var), $model->{'default' . ucfirst($var)} ? Core::$plugin->settings->{'appearance' . ucfirst($var)} : $model->$var, ['class' => 'lwptoc_colorPicker']) ?>
475
+ <?= Html::hiddenInput(Html::getInputName($model, 'default' . ucfirst($var)), $model->{'default' . ucfirst($var)}, [
476
+ 'class' => 'lwptocCustomize_field_inputDefault',
477
+ ]) ?>
478
+ </div>
479
+ </div>
480
+ <?php } ?>
481
+
482
+ </div>
483
+
484
+ </div>
485
+ <div class="lwptocModalBox_footer">
486
+ <div class="lwptocModalBox_footer_buttons">
487
+ <?= AdminHtml::button(__('Cancel', 'lwptoc'), [
488
+ 'class' => 'lwptocModal-close lwptocFloatLeft'
489
+ ]) ?>
490
+ <?= AdminHtml::button(__('Save', 'lwptoc'), [
491
+ 'theme' => AdminHtml::BUTTON_THEME_PRIMARY,
492
+ 'submit' => true,
493
+ ]) ?>
494
+ </div>
495
+ </div>
496
+ </form>
497
+ <?= $onlyBody ? '' : '</div>' ?>
admin/widgets/customizeSuccess/CustomizeSuccess.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\admin\widgets\customizeSuccess;
4
+
5
+ use luckywp\tableOfContents\core\base\Widget;
6
+
7
+ class CustomizeSuccess extends Widget
8
+ {
9
+
10
+ /**
11
+ * @var string
12
+ */
13
+ public $after = '';
14
+
15
+ public function run()
16
+ {
17
+ return $this->render('widget', [
18
+ 'after' => $this->after,
19
+ ]);
20
+ }
21
+ }
admin/widgets/customizeSuccess/views/widget.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var $after string
4
+ */
5
+ ?>
6
+ <div class="lwptocModalSuccess lwptocModal-close">
7
+ <div class="lwptocModalSuccess_ico"></div>
8
+ <div class="lwptocModalSuccess_text">
9
+ <?= __('Saved!', 'lwptoc') ?>
10
+ </div>
11
+ </div>
12
+ <?= $after ?>
admin/widgets/fontSizeField/FontSizeField.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\admin\widgets\fontSizeField;
4
+
5
+ use luckywp\tableOfContents\core\base\Widget;
6
+ use luckywp\tableOfContents\core\Core;
7
+
8
+ class FontSizeField extends Widget
9
+ {
10
+
11
+ /**
12
+ * @var string
13
+ */
14
+ public $name;
15
+
16
+ /**
17
+ * @var string
18
+ */
19
+ public $value;
20
+
21
+ /**
22
+ * @var int|float
23
+ */
24
+ public $defaultSize = 100;
25
+
26
+ public function run()
27
+ {
28
+ $type = 'default';
29
+ $size = $this->defaultSize;
30
+ $unit = '%';
31
+
32
+ $value = Core::$plugin->settings->sanitizeFontSize((string)$this->value, $matches);
33
+ if ($value !== 'default') {
34
+ $type = 'custom';
35
+ $size = $matches[1];
36
+ $unit = $matches[2];
37
+ }
38
+
39
+ return $this->render('widget', [
40
+ 'name' => $this->name,
41
+ 'value' => $value,
42
+ 'type' => $type,
43
+ 'size' => $size,
44
+ 'unit' => $unit,
45
+ ]);
46
+ }
47
+ }
admin/widgets/fontSizeField/views/widget.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var $name string
4
+ * @var $value string
5
+ * @var $type string
6
+ * @var $size int
7
+ * @var $unit string
8
+ */
9
+
10
+ use luckywp\tableOfContents\core\Core;
11
+ use luckywp\tableOfContents\core\helpers\Html;
12
+
13
+ ?>
14
+ <div class="lwptocFontSizeField">
15
+ <?= Html::dropDownList(null, $type, [
16
+ 'default' => esc_html__('Default', 'lwptoc'),
17
+ 'custom' => esc_html__('Custom Value', 'lwptoc'),
18
+ ], [
19
+ 'class' => 'lwptocFontSizeField_typeInput',
20
+ ]) ?>
21
+ <div class="lwptocFontSizeField_custom"<?= $type == 'custom' ? '' : ' style="display:none;"' ?>>
22
+ <?= Html::textInput(null, $size, [
23
+ 'class' => 'lwptocFontSizeField_sizeInput',
24
+ ]) ?>
25
+ <?= Html::dropDownList(null, $unit, Core::$plugin->fontSizeUnitsList, [
26
+ 'class' => 'lwptocFontSizeField_unitInput',
27
+ ]) ?>
28
+ </div>
29
+ <?= Html::hiddenInput($name, $value, ['class' => 'lwptocFontSizeField_input']) ?>
30
+ </div>
admin/widgets/metabox/Metabox.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\admin\widgets\metabox;
4
+
5
+ use luckywp\tableOfContents\core\base\Widget;
6
+ use luckywp\tableOfContents\plugin\PostSettings;
7
+ use WP_Post;
8
+
9
+ class Metabox extends Widget
10
+ {
11
+
12
+ /**
13
+ * @var WP_Post
14
+ */
15
+ public $post;
16
+
17
+ public function run()
18
+ {
19
+ return $this->render('box', [
20
+ 'post' => $this->post,
21
+ 'settings' => new PostSettings($this->post->ID),
22
+ ]);
23
+ }
24
+ }
admin/widgets/metabox/views/box.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var $post WP_Post
4
+ * @var $settings \luckywp\tableOfContents\plugin\PostSettings
5
+ */
6
+
7
+ use luckywp\tableOfContents\core\admin\helpers\AdminHtml;
8
+ use luckywp\tableOfContents\core\Core;
9
+
10
+ ?>
11
+ <div class="lwptocMetabox" data-post-id="<?= $post->ID ?>">
12
+ <?php if ($settings->enabled) { ?>
13
+ <p>
14
+ <?= __('The table of contents will be automatic added to this post.', 'lwptoc') ?>
15
+ </p>
16
+ <?php
17
+ $rows = Core::$plugin->admin->overrideSettingsToRows($settings);
18
+ if ($rows) { ?>
19
+ <p>
20
+ <?= __('Overridden settings:', 'lwptoc') ?>
21
+ </p>
22
+ <div class="lwptocMetabox_settings">
23
+ <?php foreach ($rows as $row) { ?>
24
+ <div class="lwptocMetabox_settings_item">
25
+ <b><?= $row[0] ?>:</b>
26
+ <?= $row[1] === null ? '<i>' . __('empty', 'lwptoc') . '</i>' : $row[1] ?>
27
+ </div>
28
+ <?php } ?>
29
+ </div>
30
+ <?php } ?>
31
+ <p>
32
+ <?= AdminHtml::button(__('Customize', 'lwptoc'), [
33
+ 'class' => 'lwptocMetabox_customize',
34
+ ]) ?>
35
+ <?= AdminHtml::button(__('Disable', 'lwptoc'), [
36
+ 'theme' => AdminHtml::BUTTON_THEME_LINK_DELETE,
37
+ 'class' => 'lwptocMetabox_disable',
38
+ ]) ?>
39
+ </p>
40
+ <?php } else { ?>
41
+ <p>
42
+ <?= __('Click "Enable" for automatic add table of contents to this post.', 'lwptoc') ?>
43
+ </p>
44
+ <p>
45
+ <?= AdminHtml::button(__('Enable', 'lwptoc'), [
46
+ 'class' => 'lwptocMetabox_enable',
47
+ ]) ?>
48
+ </p>
49
+ <?php } ?>
50
+ </div>
admin/widgets/widthField/WidthField.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\admin\widgets\widthField;
4
+
5
+ use luckywp\tableOfContents\core\base\Widget;
6
+ use luckywp\tableOfContents\core\Core;
7
+
8
+ class WidthField extends Widget
9
+ {
10
+
11
+ /**
12
+ * @var string
13
+ */
14
+ public $name;
15
+
16
+ /**
17
+ * @var string
18
+ */
19
+ public $value;
20
+
21
+ public function run()
22
+ {
23
+ $size = 75;
24
+ $unit = '%';
25
+
26
+ $value = Core::$plugin->settings->sanitizeWidth((string)$this->value, $matches);
27
+ if (Core::$plugin->isCustomWidth($value)) {
28
+ $type = 'custom';
29
+ $size = $matches[1];
30
+ $unit = $matches[2];
31
+ } else {
32
+ $type = $value;
33
+ }
34
+
35
+ return $this->render('widget', [
36
+ 'name' => $this->name,
37
+ 'value' => $value,
38
+ 'type' => $type,
39
+ 'size' => $size,
40
+ 'unit' => $unit,
41
+ ]);
42
+ }
43
+ }
admin/widgets/widthField/views/widget.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var $name string
4
+ * @var $value string
5
+ * @var $type string
6
+ * @var $size int
7
+ * @var $unit string
8
+ */
9
+
10
+ use luckywp\tableOfContents\core\Core;
11
+ use luckywp\tableOfContents\core\helpers\Html;
12
+
13
+ ?>
14
+ <div class="lwptocWidthField">
15
+ <?= Html::dropDownList(null, $type, Core::$plugin->getWidthsList(), [
16
+ 'class' => 'lwptocWidthField_typeInput',
17
+ ]) ?>
18
+ <div class="lwptocWidthField_custom"<?= $type == 'custom' ? '' : ' style="display:none;"' ?>>
19
+ <?= Html::textInput(null, $size, [
20
+ 'class' => 'lwptocWidthField_sizeInput',
21
+ ]) ?>
22
+ <?= Html::dropDownList(null, $unit, Core::$plugin->blockSizeUnitsList, [
23
+ 'class' => 'lwptocWidthField_unitInput',
24
+ ]) ?>
25
+ </div>
26
+ <?= Html::hiddenInput($name, $value, ['class' => 'lwptocWidthField_input']) ?>
27
+ </div>
config/plugin.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ return [
3
+ 'textDomain' => 'lwptoc',
4
+ 'bootstrap' => [
5
+ 'activation',
6
+ 'admin',
7
+ 'editorBlock',
8
+ 'front',
9
+ 'mcePlugin',
10
+ 'shortcode',
11
+ ],
12
+ 'pluginsLoadedBootstrap' => [
13
+ 'settings',
14
+ ],
15
+ 'components' => [
16
+ 'activation' => \luckywp\tableOfContents\plugin\Activation::className(),
17
+ 'admin' => \luckywp\tableOfContents\admin\Admin::className(),
18
+ 'front' => \luckywp\tableOfContents\front\Front::className(),
19
+ 'mcePlugin' => \luckywp\tableOfContents\plugin\mcePlugin\McePlugin::className(),
20
+ 'request' => \luckywp\tableOfContents\core\base\Request::className(),
21
+ 'settings' => [
22
+ 'class' => \luckywp\tableOfContents\plugin\Settings::className(),
23
+ 'initGroupsConfigFile' => __DIR__ . '/settings.php',
24
+ ],
25
+ 'editorBlock' => \luckywp\tableOfContents\plugin\editorBlock\EditorBlock::className(),
26
+ 'shortcode' => \luckywp\tableOfContents\plugin\Shortcode::className(),
27
+ 'view' => \luckywp\tableOfContents\core\base\View::className(),
28
+ ],
29
+ ];
config/settings.php ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use luckywp\tableOfContents\admin\widgets\fontSizeField\FontSizeField;
4
+ use luckywp\tableOfContents\admin\widgets\widthField\WidthField;
5
+ use luckywp\tableOfContents\core\admin\helpers\AdminHtml;
6
+ use luckywp\tableOfContents\admin\widgets\PostTypes;
7
+ use luckywp\tableOfContents\core\Core;
8
+
9
+ return [
10
+
11
+ // Основные настройки
12
+ 'general' => [
13
+ 'label' => esc_html__('General', 'lwptoc'),
14
+ 'sections' => [
15
+ 'main' => [
16
+ 'fields' => [
17
+
18
+ 'min' => [
19
+ 'label' => esc_html__('Minimal Count of Headers', 'lwptoc'),
20
+ 'widget' => 'textInput',
21
+ 'params' => [
22
+ 'inputOptions' => [
23
+ 'size' => AdminHtml::TEXT_INPUT_SIZE_SMALL,
24
+ ],
25
+ ],
26
+ 'default' => 2,
27
+ 'desc' => esc_html__('If the count of headers in the post is less, then table of contents is not displayed.', 'lwptoc'),
28
+ ],
29
+
30
+ 'depth' => [
31
+ 'label' => esc_html__('Depth', 'lwptoc'),
32
+ 'widget' => 'select',
33
+ 'params' => [
34
+ 'items' => Core::$plugin->depthsList,
35
+ ],
36
+ 'default' => 6,
37
+ ],
38
+
39
+ 'hierarchical' => [
40
+ 'label' => esc_html__('Hierarchical View', 'lwptoc'),
41
+ 'widget' => 'checkbox',
42
+ 'params' => [
43
+ 'checkboxOptions' => [
44
+ 'label' => esc_html__('Enable', 'lwptoc'),
45
+ ],
46
+ ],
47
+ 'default' => true,
48
+ ],
49
+
50
+ 'numeration' => [
51
+ 'label' => esc_html__('Numeration', 'lwptoc'),
52
+ 'widget' => 'select',
53
+ 'params' => [
54
+ 'items' => Core::$plugin->numerationsList,
55
+ ],
56
+ 'default' => 'decimalnested',
57
+ ],
58
+ ],
59
+ ],
60
+
61
+ 'header' => [
62
+ 'title' => esc_html__('Header', 'lwptoc'),
63
+ 'fields' => [
64
+
65
+ 'title' => [
66
+ 'label' => esc_html__('Title', 'lwptoc'),
67
+ 'widget' => 'textInput',
68
+ 'default' => __('Contents', 'lwptoc'),
69
+ ],
70
+
71
+ 'toggle' => [
72
+ 'label' => esc_html__('Toggle Show/Hide', 'lwptoc'),
73
+ 'widget' => 'checkbox',
74
+ 'params' => [
75
+ 'checkboxOptions' => [
76
+ 'label' => esc_html__('Enable', 'lwptoc'),
77
+ 'class' => 'js-lwptocToggleCheckbox',
78
+ ],
79
+ ],
80
+ 'default' => true,
81
+ ],
82
+
83
+ 'labelShow' => [
84
+ 'label' => esc_html__('Label Show', 'lwptoc'),
85
+ 'widget' => 'textInput',
86
+ 'params' => [
87
+ 'inputOptions' => [
88
+ 'class' => 'js-lwptocToggleEl'
89
+ ],
90
+ ],
91
+ 'default' => __('show', 'lwptoc'),
92
+ ],
93
+
94
+ 'labelHide' => [
95
+ 'label' => esc_html__('Label Hide', 'lwptoc'),
96
+ 'widget' => 'textInput',
97
+ 'params' => [
98
+ 'inputOptions' => [
99
+ 'class' => 'js-lwptocToggleEl'
100
+ ],
101
+ ],
102
+ 'default' => __('hide', 'lwptoc'),
103
+ ],
104
+
105
+ 'hideItems' => [
106
+ 'label' => '',
107
+ 'widget' => 'checkbox',
108
+ 'params' => [
109
+ 'checkboxOptions' => [
110
+ 'label' => esc_html__('By default, items of contents will be hidden', 'lwptoc'),
111
+ 'class' => 'js-lwptocToggleEl',
112
+ ],
113
+ ],
114
+ 'default' => false,
115
+ ],
116
+ ],
117
+ ],
118
+ 'behavior' => [
119
+ 'title' => esc_html__('Behavior', 'lwptoc'),
120
+ 'fields' => [
121
+
122
+ 'smoothScroll' => [
123
+ 'label' => esc_html__('Smooth Scroll', 'lwptoc'),
124
+ 'widget' => 'checkbox',
125
+ 'params' => [
126
+ 'checkboxOptions' => [
127
+ 'label' => esc_html__('Enable', 'lwptoc'),
128
+ 'class' => 'js-lwptocSmoothScrollCheckbox',
129
+ ],
130
+ ],
131
+ 'default' => true,
132
+ ],
133
+
134
+ 'smoothScrollOffset' => [
135
+ 'label' => esc_html__('Scroll Offset Top', 'lwptoc'),
136
+ 'widget' => 'textInput',
137
+ 'params' => [
138
+ 'inputOptions' => [
139
+ 'size' => AdminHtml::TEXT_INPUT_SIZE_SMALL,
140
+ 'class' => 'js-lwptocSmoothScrollIOffsetInput'
141
+ ],
142
+ 'after' => ' px',
143
+ ],
144
+ 'default' => 24,
145
+ ],
146
+ ],
147
+ ],
148
+ ],
149
+ ],
150
+
151
+ // Внешний вид
152
+ 'appearance' => [
153
+ 'label' => esc_html__('Appearance', 'lwptoc'),
154
+ 'sections' => [
155
+ 'main' => [
156
+ 'fields' => [
157
+
158
+ 'width' => [
159
+ 'label' => esc_html__('Width', 'lwptoc'),
160
+ 'widget' => function ($field) {
161
+ echo WidthField::widget([
162
+ 'name' => $field['name'],
163
+ 'value' => Core::$plugin->settings->getValue($field['group'], $field['id'], 'auto', false),
164
+ ]);
165
+ },
166
+ 'sanitizeCallback' => [Core::$plugin->settings, 'sanitizeWidth'],
167
+ 'default' => 'auto',
168
+ ],
169
+
170
+ 'float' => [
171
+ 'label' => esc_html__('Float', 'lwptoc'),
172
+ 'widget' => 'select',
173
+ 'params' => [
174
+ 'items' => Core::$plugin->floatsList,
175
+ ],
176
+ 'default' => 'none',
177
+ ],
178
+
179
+ 'titleFontSize' => [
180
+ 'label' => esc_html__('Title Font Size', 'lwptoc'),
181
+ 'widget' => function ($field) {
182
+ echo FontSizeField::widget([
183
+ 'name' => $field['name'],
184
+ 'value' => Core::$plugin->settings->getValue($field['group'], $field['id'], 'default', false),
185
+ 'defaultSize' => 100,
186
+ ]);
187
+ },
188
+ 'sanitizeCallback' => [Core::$plugin->settings, 'sanitizeFontSize'],
189
+ 'default' => 'default',
190
+ ],
191
+
192
+ 'titleFontWeight' => [
193
+ 'label' => esc_html__('Title Font Weight', 'lwptoc'),
194
+ 'widget' => 'select',
195
+ 'params' => [
196
+ 'items' => Core::$plugin->fontWeightsList,
197
+ ],
198
+ 'default' => 'bold',
199
+ ],
200
+
201
+ 'itemsFontSize' => [
202
+ 'label' => esc_html__('Items Font Size', 'lwptoc'),
203
+ 'widget' => function ($field) {
204
+ echo FontSizeField::widget([
205
+ 'name' => $field['name'],
206
+ 'value' => Core::$plugin->settings->getValue($field['group'], $field['id'], 'default', false),
207
+ 'defaultSize' => 90,
208
+ ]);
209
+ },
210
+ 'sanitizeCallback' => [Core::$plugin->settings, 'sanitizeFontSize'],
211
+ 'default' => '90%',
212
+ ],
213
+
214
+ 'colorScheme' => [
215
+ 'label' => esc_html__('Color Scheme', 'lwptoc'),
216
+ 'widget' => 'select',
217
+ 'params' => [
218
+ 'items' => Core::$plugin->colorSchemesList,
219
+ ],
220
+ 'default' => 'light',
221
+ ],
222
+ ],
223
+ ],
224
+ 'overrideColors' => [
225
+ 'title' => esc_html__('Override Color Scheme Colors', 'lwptoc'),
226
+ 'fields' => [
227
+
228
+ 'backgroundColor' => [
229
+ 'widget' => 'color',
230
+ 'label' => esc_html__('Background Color', 'lwptoc'),
231
+ ],
232
+
233
+ 'borderColor' => [
234
+ 'widget' => 'color',
235
+ 'label' => esc_html__('Border Color', 'lwptoc'),
236
+ ],
237
+
238
+ 'titleColor' => [
239
+ 'widget' => 'color',
240
+ 'label' => esc_html__('Title Color', 'lwptoc'),
241
+ ],
242
+
243
+ 'linkColor' => [
244
+ 'widget' => 'color',
245
+ 'label' => esc_html__('Link Color', 'lwptoc'),
246
+ ],
247
+
248
+ 'hoverLinkColor' => [
249
+ 'widget' => 'color',
250
+ 'label' => esc_html__('Hover Link Color', 'lwptoc'),
251
+ ],
252
+
253
+ 'visitedLinkColor' => [
254
+ 'widget' => 'color',
255
+ 'label' => esc_html__('Visited Link Color', 'lwptoc'),
256
+ ],
257
+ ],
258
+ ],
259
+ ],
260
+ ],
261
+
262
+ // Автоматическая вставка
263
+ 'autoInsert' => [
264
+ 'label' => esc_html__('Auto Insert', 'lwptoc'),
265
+ 'sections' => [
266
+ 'main' => [
267
+ 'fields' => [
268
+
269
+ 'enable' => [
270
+ 'label' => esc_html__('Auto Insert Table of Contents', 'lwptoc'),
271
+ 'widget' => 'checkbox',
272
+ 'params' => [
273
+ 'checkboxOptions' => [
274
+ 'label' => esc_html__('Enable', 'lwptoc'),
275
+ 'class' => 'js-lwptocAutoInsertEnableCheckbox',
276
+ ],
277
+ ],
278
+ 'default' => true,
279
+ ],
280
+
281
+ 'position' => [
282
+ 'label' => esc_html__('Position', 'lwptoc'),
283
+ 'widget' => 'select',
284
+ 'params' => [
285
+ 'items' => Core::$plugin->positionsList,
286
+ 'selectOptions' => [
287
+ 'class' => 'js-lwptocAutoInsertEl',
288
+ ],
289
+ ],
290
+ 'default' => 'beforefirstheading',
291
+ ],
292
+
293
+ 'postTypes' => [
294
+ 'label' => esc_html__('Post Types', 'lwptoc'),
295
+ 'widget' => function ($field) {
296
+ echo PostTypes::widget([
297
+ 'field' => $field,
298
+ 'containerOptions' => [
299
+ 'class' => 'js-lwptocAutoInsertEl',
300
+ ],
301
+ ]);
302
+ },
303
+ 'default' => ['post'],
304
+ ],
305
+ ],
306
+ ],
307
+ ],
308
+ ],
309
+ ];
core/Core.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core;
4
+
5
+ use Exception;
6
+ use luckywp\tableOfContents\core\base\BasePlugin;
7
+ use luckywp\tableOfContents\core\base\Container;
8
+
9
+ class Core
10
+ {
11
+
12
+ /**
13
+ * @var Container
14
+ */
15
+ public static $container;
16
+
17
+ /**
18
+ * @var \luckywp\tableOfContents\plugin\Plugin
19
+ */
20
+ public static $plugin;
21
+
22
+ public static function initialize(BasePlugin $plugin)
23
+ {
24
+ static::$container = new Container();
25
+ static::$plugin = $plugin;
26
+ }
27
+
28
+ /**
29
+ * @param object $object
30
+ * @param array $properties
31
+ * @return object
32
+ */
33
+ public static function configure($object, $properties)
34
+ {
35
+ foreach ($properties as $name => $value) {
36
+ $object->$name = $value;
37
+ }
38
+ return $object;
39
+ }
40
+
41
+ /**
42
+ * @param string|array $type
43
+ * @param array $params
44
+ * @return object
45
+ */
46
+ public static function createObject($type, array $params = [])
47
+ {
48
+ if (is_string($type)) {
49
+ return static::$container->get($type, $params);
50
+ } elseif (is_array($type) && isset($type['class'])) {
51
+ $class = $type['class'];
52
+ unset($type['class']);
53
+ return static::$container->get($class, $params, $type);
54
+ }
55
+ throw new Exception('Unsupported configuration type: ' . gettype($type));
56
+ }
57
+
58
+ /**
59
+ * @return bool
60
+ */
61
+ public static function isFront()
62
+ {
63
+ return !is_admin() && !wp_doing_ajax();
64
+ }
65
+ }
core/admin/AdminController.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\admin;
4
+
5
+ use luckywp\tableOfContents\core\admin\helpers\AdminUrl;
6
+ use luckywp\tableOfContents\core\base\Controller;
7
+ use luckywp\tableOfContents\core\Core;
8
+
9
+ class AdminController extends Controller
10
+ {
11
+
12
+ private $_action;
13
+
14
+ public function init()
15
+ {
16
+ parent::init();
17
+ if (AdminUrl::isPage($this->id)) {
18
+ $methodName = 'handle' . ucfirst($this->getAction());
19
+ if (method_exists($this, $methodName)) {
20
+ add_action('wp_loaded', [$this, $methodName]);
21
+ }
22
+ }
23
+ }
24
+
25
+ public function getAction()
26
+ {
27
+ if ($this->_action === null) {
28
+ $this->_action = Core::$plugin->request->get('action', 'index');
29
+ }
30
+ return $this->_action;
31
+ }
32
+
33
+ public static function router()
34
+ {
35
+ /** @var self $controller */
36
+ $controller = static::getInstance();
37
+ $methodName = 'action' . ucfirst($controller->getAction());
38
+ if (!method_exists($controller, $methodName)) {
39
+ $controller->notAllowed();
40
+ }
41
+ $controller->$methodName();
42
+ }
43
+
44
+ public function notAllowed()
45
+ {
46
+ wp_die(esc_html__('Sorry, you are not allowed to access this page.', 'lwptoc'), 403);
47
+ }
48
+ }
core/admin/helpers/AdminHtml.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\admin\helpers;
4
+
5
+ use luckywp\tableOfContents\core\helpers\ArrayHelper;
6
+ use luckywp\tableOfContents\core\helpers\Html;
7
+
8
+ class AdminHtml
9
+ {
10
+
11
+ const BUTTON_SIZE_SMALL = 'small';
12
+ const BUTTON_SIZE_LARGE = 'large';
13
+ const BUTTON_SIZE_HERO = 'hero';
14
+
15
+ const BUTTON_THEME_PRIMARY = 'primary';
16
+ const BUTTON_THEME_SECONDARY = 'secondary';
17
+ const BUTTON_THEME_LINK = 'link';
18
+ const BUTTON_THEME_LINK_DELETE = 'link button-link-delete';
19
+
20
+ /**
21
+ * @param $content
22
+ * @param array $options
23
+ * @return string
24
+ */
25
+ public static function button($content, $options = [])
26
+ {
27
+ $submit = isset($options['submit']) ? (bool)$options['submit'] : false;
28
+ $href = isset($options['href']) ? $options['href'] : false;
29
+ $class = isset($options['class']) ? $options['class'] : false;
30
+ $size = isset($options['size']) ? $options['size'] : false;
31
+ $theme = isset($options['theme']) ? $options['theme'] : false;
32
+ $attrs = isset($options['attrs']) ? $options['attrs'] : [];
33
+ $tag = isset($options['tag']) ? strtolower($options['tag']) : ($href ? 'a' : 'button');
34
+
35
+ if (isset($attrs['class'])) {
36
+ if (!is_array($attrs['class'])) {
37
+ $attrs['class'] = explode(' ', $attrs['class']);
38
+ }
39
+ } else {
40
+ $attrs['class'] = is_array($class) ? $class : ($class === false ? [] : [$class]);
41
+ }
42
+ $attrs['class'][] = 'button';
43
+
44
+ if (($tag == 'input') && (!isset($attrs['value']))) {
45
+ $attrs['value'] = $content;
46
+ }
47
+
48
+ if ($size) {
49
+ $attrs['class'][] = 'button-' . $size;
50
+ }
51
+
52
+ if ($theme) {
53
+ $attrs['class'][] = 'button-' . $theme;
54
+ }
55
+
56
+ if (($tag == 'a') && ($href !== false)) {
57
+ $attrs['href'] = $href;
58
+ }
59
+
60
+ if (($tag == 'input') || ($tag == 'button')) {
61
+ $attrs['type'] = $submit ? 'submit' : 'button';
62
+ }
63
+
64
+ return Html::tag($tag, $content, $attrs);
65
+ }
66
+
67
+ const TEXT_INPUT_SIZE_REGULAR = 'regular';
68
+ const TEXT_INPUT_SIZE_SMALL = 'small';
69
+ const TEXT_INPUT_SIZE_LARGE = 'large';
70
+
71
+ public static function textInput($name, $value, $options = [])
72
+ {
73
+ $options = Html::prepareClassInOptions($options);
74
+
75
+ if ('' != $size = ArrayHelper::getValue($options, 'size')) {
76
+ $options['class'][] = $size . '-text';
77
+ unset($options['size']);
78
+ } else {
79
+ $options['class'][] = $size . 'regular-text';
80
+ }
81
+
82
+ return Html::textInput($name, $value, $options);
83
+ }
84
+ }
core/admin/helpers/AdminUrl.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\admin\helpers;
4
+
5
+ use luckywp\tableOfContents\core\Core;
6
+
7
+ class AdminUrl
8
+ {
9
+
10
+ /**
11
+ * @param string $pageId
12
+ * @param string|null $action
13
+ * @param array $params
14
+ * @return string
15
+ */
16
+ public static function toOptions($pageId, $action = null, $params = [])
17
+ {
18
+ return static::makeUrl('options-general.php', $pageId, $action, $params);
19
+ }
20
+
21
+ /**
22
+ * @param string $wpPage
23
+ * @param string $pageId
24
+ * @param string|null $action
25
+ * @param array $params
26
+ * @return string
27
+ */
28
+ protected static function makeUrl($wpPage, $pageId, $action = null, $params = [])
29
+ {
30
+ $params['page'] = Core::$plugin->prefix . $pageId;
31
+ if ($action !== null) {
32
+ $params['action'] = $action;
33
+ }
34
+ return admin_url($wpPage . '?' . http_build_query($params));
35
+ }
36
+
37
+ /**
38
+ * @param string $pageId
39
+ * @param string $action
40
+ * @return bool
41
+ */
42
+ public static function isPage($pageId, $action = '')
43
+ {
44
+ return Core::$plugin->request->get('page') == Core::$plugin->prefix . $pageId
45
+ && (!$action || Core::$plugin->request->get('action') == $action);
46
+ }
47
+ }
core/base/BaseObject.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\base;
4
+
5
+ use Exception;
6
+ use luckywp\tableOfContents\core\Core;
7
+
8
+ abstract class BaseObject
9
+ {
10
+
11
+ public function __construct($config = [])
12
+ {
13
+ if (!empty($config)) {
14
+ Core::configure($this, $config);
15
+ }
16
+ $this->init();
17
+ }
18
+
19
+ public function init()
20
+ {
21
+ }
22
+
23
+ public static function className()
24
+ {
25
+ return get_called_class();
26
+ }
27
+
28
+ public function __get($name)
29
+ {
30
+ $getter = 'get' . $name;
31
+ if (method_exists($this, $getter)) {
32
+ return $this->$getter();
33
+ }
34
+ throw new Exception('Getting unknown property: ' . get_class($this) . '::' . $name);
35
+ }
36
+
37
+ public function __set($name, $value)
38
+ {
39
+ $setter = 'set' . $name;
40
+ if (method_exists($this, $setter)) {
41
+ $this->$setter($value);
42
+ return;
43
+ }
44
+ throw new Exception('Setting unknown property: ' . get_class($this) . '::' . $name);
45
+ }
46
+
47
+ public function __isset($name)
48
+ {
49
+ $getter = 'get' . $name;
50
+ if (method_exists($this, $getter)) {
51
+ return $this->$getter() !== null;
52
+ }
53
+ return false;
54
+ }
55
+
56
+ public function __unset($name)
57
+ {
58
+ $setter = 'set' . $name;
59
+ if (method_exists($this, $setter)) {
60
+ $this->$setter(null);
61
+ return;
62
+ }
63
+ throw new Exception('Unsetting an unknown or read-only property: ' . get_class($this) . '::' . $name);
64
+ }
65
+ }
core/base/BasePlugin.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\base;
4
+
5
+ use luckywp\tableOfContents\core\Core;
6
+
7
+ /**
8
+ * @property string $basename
9
+ */
10
+ abstract class BasePlugin extends ServiceLocator
11
+ {
12
+
13
+ /**
14
+ * @var string
15
+ */
16
+ public $version;
17
+
18
+ /**
19
+ * @var string
20
+ */
21
+ public $fileName;
22
+
23
+ /**
24
+ * @var string
25
+ */
26
+ public $dir;
27
+
28
+ /**
29
+ * @var string
30
+ */
31
+ public $url;
32
+
33
+ /**
34
+ * @var string
35
+ */
36
+ public $prefix;
37
+
38
+ /**
39
+ * @var string
40
+ */
41
+ public $textDomain;
42
+
43
+ /**
44
+ * @var string
45
+ */
46
+ public $domainPath = 'languages/';
47
+
48
+ /**
49
+ * @var array
50
+ */
51
+ public $bootstrap = [];
52
+
53
+ /**
54
+ * @var array
55
+ */
56
+ public $pluginsLoadedBootstrap = [];
57
+
58
+ public function __construct(array $config = [])
59
+ {
60
+ Core::initialize($this);
61
+ add_action('plugins_loaded', function () {
62
+ if ($this->textDomain) {
63
+ load_plugin_textdomain($this->textDomain, false, basename($this->dir) . '/' . $this->domainPath);
64
+ }
65
+ $this->bootstrap($this->pluginsLoadedBootstrap);
66
+ });
67
+ parent::__construct($config);
68
+ }
69
+
70
+ protected function bootstrap($bootstrap)
71
+ {
72
+ foreach ($bootstrap as $mixed) {
73
+ $component = null;
74
+ if (is_callable($mixed)) {
75
+ if (!$component = call_user_func($mixed, $this)) {
76
+ continue;
77
+ }
78
+ } elseif (is_string($mixed) && $this->has($mixed)) {
79
+ $this->get($mixed);
80
+ }
81
+ }
82
+ }
83
+
84
+ public function run($version, $fileName, $prefix)
85
+ {
86
+ $this->version = $version;
87
+ $this->fileName = $fileName;
88
+ $this->dir = dirname($fileName);
89
+ $this->url = plugins_url('', $fileName);
90
+ $this->prefix = $prefix;
91
+ $this->bootstrap($this->bootstrap);
92
+ }
93
+
94
+ public function getBasename()
95
+ {
96
+ return plugin_basename($this->fileName);
97
+ }
98
+ }
core/base/Container.php ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\base;
4
+
5
+ use Exception;
6
+ use luckywp\tableOfContents\core\Core;
7
+ use ReflectionClass;
8
+
9
+ class Container
10
+ {
11
+ private $_singletons = [];
12
+ private $_definitions = [];
13
+ private $_params = [];
14
+ private $_reflections = [];
15
+
16
+ /**
17
+ * @param string $class
18
+ * @param array $params
19
+ * @param array $config
20
+ * @return object
21
+ */
22
+ public function get($class, $params = [], $config = [])
23
+ {
24
+ if (isset($this->_singletons[$class])) {
25
+ return $this->_singletons[$class];
26
+ } elseif (!isset($this->_definitions[$class])) {
27
+ return $this->build($class, $params, $config);
28
+ }
29
+
30
+ $definition = $this->_definitions[$class];
31
+
32
+ if (is_callable($definition, true)) {
33
+ $params = $this->mergeParams($class, $params);
34
+ $object = call_user_func($definition, $this, $params, $config);
35
+ } elseif (is_array($definition)) {
36
+ $concrete = $definition['class'];
37
+ unset($definition['class']);
38
+
39
+ $config = array_merge($definition, $config);
40
+ $params = $this->mergeParams($class, $params);
41
+
42
+ if ($concrete === $class) {
43
+ $object = $this->build($class, $params, $config);
44
+ } else {
45
+ $object = $this->get($concrete, $params, $config);
46
+ }
47
+ } elseif (is_object($definition)) {
48
+ return $this->_singletons[$class] = $definition;
49
+ } else {
50
+ throw new Exception('Unexpected object definition type: ' . gettype($definition));
51
+ }
52
+
53
+ if (array_key_exists($class, $this->_singletons)) {
54
+ $this->_singletons[$class] = $object;
55
+ }
56
+
57
+ return $object;
58
+ }
59
+
60
+ /**
61
+ * @param string $class
62
+ * @param string|array|callable $definition
63
+ * @param array $params
64
+ * @return $this
65
+ */
66
+ public function set($class, $definition = [], array $params = [])
67
+ {
68
+ $this->_definitions[$class] = $this->normalizeDefinition($class, $definition);
69
+ $this->_params[$class] = $params;
70
+ unset($this->_singletons[$class]);
71
+ return $this;
72
+ }
73
+
74
+ /**
75
+ * @param string $class
76
+ * @param string|array|callable $definition
77
+ * @param array $params
78
+ * @return $this
79
+ */
80
+ public function setSingleton($class, $definition = [], array $params = [])
81
+ {
82
+ $this->_definitions[$class] = $this->normalizeDefinition($class, $definition);
83
+ $this->_params[$class] = $params;
84
+ $this->_singletons[$class] = null;
85
+ return $this;
86
+ }
87
+
88
+ /**
89
+ * @param string $class
90
+ * @param array $params
91
+ * @return array
92
+ */
93
+ protected function mergeParams($class, $params)
94
+ {
95
+ if (empty($this->_params[$class])) {
96
+ return $params;
97
+ } elseif (empty($params)) {
98
+ return $this->_params[$class];
99
+ }
100
+
101
+ $ps = $this->_params[$class];
102
+ foreach ($params as $index => $value) {
103
+ $ps[$index] = $value;
104
+ }
105
+
106
+ return $ps;
107
+ }
108
+
109
+ /**
110
+ * @param string $class
111
+ * @param string|array|callable $definition
112
+ * @return array
113
+ */
114
+ protected function normalizeDefinition($class, $definition)
115
+ {
116
+ if (empty($definition)) {
117
+ return ['class' => $class];
118
+ } elseif (is_string($definition)) {
119
+ return ['class' => $definition];
120
+ } elseif (is_callable($definition, true) || is_object($definition)) {
121
+ return $definition;
122
+ } elseif (is_array($definition)) {
123
+ if (!isset($definition['class'])) {
124
+ if (strpos($class, '\\') !== false) {
125
+ $definition['class'] = $class;
126
+ }
127
+ }
128
+ return $definition;
129
+ }
130
+ throw new Exception('Unsupported definition type for "' . $class . '": ' . gettype($definition));
131
+ }
132
+
133
+ /**
134
+ * @param string $class
135
+ * @param array $params
136
+ * @param array $config
137
+ * @return object
138
+ */
139
+ protected function build($class, array $params, array $config)
140
+ {
141
+ if (isset($this->_reflections[$class])) {
142
+ $reflection = $this->_reflections[$class];
143
+ } else {
144
+ $reflection = new ReflectionClass($class);
145
+ $this->_reflections[$class] = $reflection;
146
+ }
147
+
148
+ if ($reflection->isSubclassOf(BaseObject::className())) {
149
+ $params[] = $config;
150
+ $object = $reflection->newInstanceArgs($params);
151
+ } else {
152
+ $object = $reflection->newInstanceArgs($params);
153
+ if ($config) {
154
+ Core::configure($object, $config);
155
+ }
156
+ }
157
+
158
+ return $object;
159
+ }
160
+ }
core/base/Controller.php ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\base;
4
+
5
+ use luckywp\tableOfContents\core\Core;
6
+ use ReflectionClass;
7
+
8
+ /**
9
+ * @property string $id
10
+ */
11
+ abstract class Controller extends BaseObject implements ViewContextInterface
12
+ {
13
+
14
+ private $_id;
15
+
16
+ public function getId()
17
+ {
18
+ if ($this->_id === null) {
19
+ $class = new ReflectionClass($this);
20
+ $this->_id .= lcfirst(substr($class->getShortName(), 0, -10));
21
+ }
22
+ return $this->_id;
23
+ }
24
+
25
+ private $_viewPath;
26
+
27
+ public function getViewPath()
28
+ {
29
+ if ($this->_viewPath === null) {
30
+ $class = new ReflectionClass($this);
31
+ $this->_viewPath = dirname($class->getFileName()) . '/../views';
32
+ $this->_viewPath .= '/' . $this->id;
33
+ }
34
+ return $this->_viewPath;
35
+ }
36
+
37
+ /**
38
+ * @param string $view
39
+ * @return array
40
+ */
41
+ public function getViewFiles($view)
42
+ {
43
+ return [$this->getViewPath() . '/' . $view . '.php'];
44
+ }
45
+
46
+ /**
47
+ * @param string $view
48
+ * @param array $params
49
+ * @param bool $echo
50
+ * @return string|null
51
+ */
52
+ public function render($view, $params = [], $echo = true)
53
+ {
54
+ $html = Core::$plugin->view->renderFile($this->getViewFiles($view), $params, $this);
55
+ if ($echo) {
56
+ echo $html;
57
+ return null;
58
+ }
59
+ return $html;
60
+ }
61
+
62
+ /**
63
+ * @return self
64
+ */
65
+ public static function getInstance()
66
+ {
67
+ static $instances = [];
68
+ $className = static::className();
69
+ if (!isset($instances[$className])) {
70
+ $instances[$className] = Core::createObject($className);
71
+ }
72
+ return $instances[$className];
73
+ }
74
+ }
core/base/Model.php ADDED
@@ -0,0 +1,311 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\base;
4
+
5
+ use Exception;
6
+ use luckywp\tableOfContents\core\helpers\ArrayHelper;
7
+ use luckywp\tableOfContents\core\validators\Validator;
8
+ use ReflectionClass;
9
+ use ReflectionProperty;
10
+
11
+ /**
12
+ * @property array $attributes
13
+ */
14
+ abstract class Model extends BaseObject
15
+ {
16
+
17
+ /**
18
+ * @param array $data
19
+ * @param string $formName
20
+ * @return bool
21
+ */
22
+ public function load($data, $formName = null)
23
+ {
24
+ $scope = $formName === null ? $this->formName() : $formName;
25
+ if ($scope === '' && !empty($data)) {
26
+ $this->setAttributes($data);
27
+ return true;
28
+ }
29
+ if (isset($data[$scope])) {
30
+ $this->setAttributes($data[$scope]);
31
+ return true;
32
+ }
33
+ return false;
34
+ }
35
+
36
+ /**
37
+ * @return string
38
+ */
39
+ public function formName()
40
+ {
41
+ $reflector = new ReflectionClass($this);
42
+ return $reflector->getShortName();
43
+ }
44
+
45
+
46
+ /**
47
+ * ---------------------------------------------------------------------------
48
+ * Валидация
49
+ * ---------------------------------------------------------------------------
50
+ */
51
+
52
+ /**
53
+ * @var Validator[]
54
+ */
55
+ private $_validators;
56
+
57
+ /**
58
+ * @param string $attribute
59
+ * @return Validator[]
60
+ */
61
+ public function getValidators($attribute = null)
62
+ {
63
+ if ($this->_validators === null) {
64
+ $this->_validators = [];
65
+ foreach ($this->rules() as $rule) {
66
+ if ($rule instanceof Validator) {
67
+ $this->_validators[] = $rule;
68
+ } elseif (is_array($rule) && isset($rule[0], $rule[1])) {
69
+ $validator = Validator::createValidator($rule[1], $this, (array)$rule[0], array_slice($rule, 2));
70
+ $this->_validators[] = $validator;
71
+ } else {
72
+ throw new Exception('Invalid validation rule: a rule must specify both attribute names and validator type.');
73
+ }
74
+ }
75
+ }
76
+
77
+ if ($attribute === null) {
78
+ return $this->_validators;
79
+ }
80
+
81
+ $validators = [];
82
+ foreach ($this->_validators as $validator) {
83
+ if (in_array($attribute, $validator->getAttributeNames(), true)) {
84
+ $validators[] = $validator;
85
+ }
86
+ }
87
+ return $validators;
88
+ }
89
+
90
+ /**
91
+ * @param array $attributeNames
92
+ * @param bool $clearErrors
93
+ * @return bool
94
+ */
95
+ public function validate($attributeNames = null, $clearErrors = true)
96
+ {
97
+ if ($clearErrors) {
98
+ $this->clearErrors();
99
+ }
100
+ if ($attributeNames === null) {
101
+ $attributeNames = $this->attributeNames();
102
+ }
103
+ foreach ($this->getValidators() as $validator) {
104
+ $validator->validateAttributes($this, $attributeNames);
105
+ }
106
+ return !$this->hasErrors();
107
+ }
108
+
109
+ /**
110
+ * @return array
111
+ */
112
+ public function rules()
113
+ {
114
+ return [];
115
+ }
116
+
117
+
118
+ /**
119
+ * ---------------------------------------------------------------------------
120
+ * Атрибуты
121
+ * ---------------------------------------------------------------------------
122
+ */
123
+
124
+ /**
125
+ * @var string[]
126
+ */
127
+ private $_attributeNames;
128
+
129
+ /**
130
+ * @return string[]
131
+ */
132
+ public function attributeNames()
133
+ {
134
+ if ($this->_attributeNames === null) {
135
+ $class = new ReflectionClass($this);
136
+ $this->_attributeNames = [];
137
+ foreach ($class->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
138
+ if (!$property->isStatic()) {
139
+ $this->_attributeNames[] = $property->getName();
140
+ }
141
+ }
142
+ }
143
+ return $this->_attributeNames;
144
+ }
145
+
146
+ /**
147
+ * @return string[]
148
+ */
149
+ public function safeAttributeNames()
150
+ {
151
+ $names = [];
152
+ foreach ($this->getValidators() as $validator) {
153
+ foreach ($validator->attributes as $attribute) {
154
+ $names[] = $attribute;
155
+ }
156
+ }
157
+ return array_unique($names);
158
+ }
159
+
160
+ /**
161
+ * @return array
162
+ */
163
+ public function attributeLabels()
164
+ {
165
+ return [];
166
+ }
167
+
168
+ /**
169
+ * @param string $attribute
170
+ * @return string
171
+ */
172
+ public function getAttributeLabel($attribute)
173
+ {
174
+ return ArrayHelper::getValue($this->attributeLabels(), $attribute, $attribute);
175
+ }
176
+
177
+ /**
178
+ * @return array
179
+ */
180
+ public function attributeHints()
181
+ {
182
+ return [];
183
+ }
184
+
185
+ /**
186
+ * @param array $names
187
+ * @return array
188
+ */
189
+ public function getAttributes($names = null)
190
+ {
191
+ $values = [];
192
+ if ($names === null) {
193
+ $names = $this->attributeNames();
194
+ }
195
+ foreach ($names as $name) {
196
+ $values[$name] = $this->$name;
197
+ }
198
+ return $values;
199
+ }
200
+
201
+ /**
202
+ * @param array $values
203
+ * @param bool $safeOnly
204
+ */
205
+ public function setAttributes($values, $safeOnly = true)
206
+ {
207
+ if (is_array($values)) {
208
+ $attributes = array_flip($safeOnly ? $this->safeAttributeNames() : $this->attributeNames());
209
+ foreach ($values as $name => $value) {
210
+ if (isset($attributes[$name])) {
211
+ $this->$name = $value;
212
+ }
213
+ }
214
+ }
215
+ }
216
+
217
+
218
+ /**
219
+ * ---------------------------------------------------------------------------
220
+ * Ошибки
221
+ * ---------------------------------------------------------------------------
222
+ */
223
+
224
+ /**
225
+ * @var array
226
+ */
227
+ private $_errors = [];
228
+
229
+ /**
230
+ * @param string|null $attribute
231
+ * @return array
232
+ */
233
+ public function getErrors($attribute = null)
234
+ {
235
+ if ($attribute === null) {
236
+ return $this->_errors;
237
+ }
238
+ return ArrayHelper::getValue($this->_errors, $attribute, []);
239
+ }
240
+
241
+ /**
242
+ * @return array
243
+ */
244
+ public function getFirstErrors()
245
+ {
246
+ if (empty($this->_errors)) {
247
+ return [];
248
+ }
249
+
250
+ $errors = [];
251
+ foreach ($this->_errors as $name => $es) {
252
+ if (!empty($es)) {
253
+ $errors[$name] = reset($es);
254
+ }
255
+ }
256
+ return $errors;
257
+ }
258
+
259
+ /**
260
+ * @param bool $showAllErrors
261
+ * @return array
262
+ */
263
+ public function getErrorSummary($showAllErrors = true)
264
+ {
265
+ $lines = [];
266
+ $errors = $showAllErrors ? $this->getErrors() : $this->getFirstErrors();
267
+ foreach ($errors as $es) {
268
+ $lines = array_merge($lines, (array)$es);
269
+ }
270
+ return $lines;
271
+ }
272
+
273
+ /**
274
+ * @param string $attribute
275
+ * @return string
276
+ */
277
+ public function getFirstError($attribute)
278
+ {
279
+ return isset($this->_errors[$attribute]) ? reset($this->_errors[$attribute]) : null;
280
+ }
281
+
282
+ /**
283
+ * @param string $attribute
284
+ * @param string $error
285
+ */
286
+ public function addError($attribute, $error = '')
287
+ {
288
+ $this->_errors[$attribute][] = $error;
289
+ }
290
+
291
+ /**
292
+ * @param string|null $attribute
293
+ */
294
+ public function clearErrors($attribute = null)
295
+ {
296
+ if ($attribute === null) {
297
+ $this->_errors = [];
298
+ } else {
299
+ unset($this->_errors[$attribute]);
300
+ }
301
+ }
302
+
303
+ /**
304
+ * @param string|null $attribute
305
+ * @return bool
306
+ */
307
+ public function hasErrors($attribute = null)
308
+ {
309
+ return $attribute === null ? !empty($this->_errors) : isset($this->_errors[$attribute]);
310
+ }
311
+ }
core/base/Request.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\base;
4
+
5
+ use luckywp\tableOfContents\core\helpers\ArrayHelper;
6
+
7
+ class Request extends BaseObject
8
+ {
9
+
10
+ /**
11
+ * Возвращает значение, переданное методом GET.
12
+ * Значение приводится к исходному виду (по-умолчанию, WP экранирует символы)
13
+ * Если значения не существует, то функция вернёт $defaultValue
14
+ * @param string|null $name
15
+ * @param mixed $defaultValue
16
+ * @return mixed
17
+ */
18
+ public function get($name = null, $defaultValue = null)
19
+ {
20
+ $value = $name === null ? $_GET : ArrayHelper::getValue($_GET, $name, $defaultValue);
21
+ return $value === null ? null : stripslashes_deep($value);
22
+ }
23
+
24
+ /**
25
+ * Возвращает значение, переданное методом POST.
26
+ * Значение приводится к исходному виду (по-умолчанию, WP экранирует символы)
27
+ * Если значения не существует, то функция вернёт $defaultValue
28
+ * @param string|null $name
29
+ * @param mixed $defaultValue
30
+ * @return mixed
31
+ */
32
+ public function post($name = null, $defaultValue = null)
33
+ {
34
+ $value = $name === null ? $_POST : ArrayHelper::getValue($_POST, $name, $defaultValue);
35
+ return $value === null ? null : stripslashes_deep($value);
36
+ }
37
+ }
core/base/ServiceLocator.php ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\base;
4
+
5
+ use Closure;
6
+ use Exception;
7
+ use luckywp\tableOfContents\core\Core;
8
+
9
+ class ServiceLocator extends BaseObject
10
+ {
11
+
12
+ /**
13
+ * @var array
14
+ */
15
+ private $_components = [];
16
+
17
+ /**
18
+ * @var array component
19
+ */
20
+ private $_definitions = [];
21
+
22
+ /**
23
+ * @param string $name
24
+ * @return mixed
25
+ */
26
+ public function __get($name)
27
+ {
28
+ if ($this->has($name)) {
29
+ return $this->get($name);
30
+ }
31
+ return parent::__get($name);
32
+ }
33
+
34
+ /**
35
+ * @param string $name
36
+ * @return bool
37
+ */
38
+ public function __isset($name)
39
+ {
40
+ if ($this->has($name)) {
41
+ return true;
42
+ }
43
+ return parent::__isset($name);
44
+ }
45
+
46
+ /**
47
+ * @param string $id
48
+ * @param bool $checkInstance
49
+ * @return bool
50
+ * @see set()
51
+ */
52
+ public function has($id, $checkInstance = false)
53
+ {
54
+ return $checkInstance ? isset($this->_components[$id]) : isset($this->_definitions[$id]);
55
+ }
56
+
57
+ /**
58
+ * @param string $id
59
+ * @param bool $throwException
60
+ * @return object|null
61
+ */
62
+ public function get($id, $throwException = true)
63
+ {
64
+ if (isset($this->_components[$id])) {
65
+ return $this->_components[$id];
66
+ }
67
+
68
+ if (isset($this->_definitions[$id])) {
69
+ $definition = $this->_definitions[$id];
70
+ if (is_object($definition) && !$definition instanceof Closure) {
71
+ return $this->_components[$id] = $definition;
72
+ }
73
+ return $this->_components[$id] = Core::createObject($definition);
74
+ }
75
+
76
+ if ($throwException) {
77
+ throw new Exception("Unknown component ID: $id");
78
+ }
79
+ return null;
80
+ }
81
+
82
+ /**
83
+ * @param string $id
84
+ * @param mixed $definition
85
+ */
86
+ public function set($id, $definition)
87
+ {
88
+ unset($this->_components[$id]);
89
+
90
+ if ($definition === null) {
91
+ unset($this->_definitions[$id]);
92
+ return;
93
+ }
94
+
95
+ if (is_object($definition) || is_callable($definition, true)) {
96
+ $this->_definitions[$id] = $definition;
97
+ } elseif (is_array($definition)) {
98
+ $this->_definitions[$id] = $definition;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * @param array $components
104
+ */
105
+ public function setComponents($components)
106
+ {
107
+ foreach ($components as $id => $component) {
108
+ $this->set($id, $component);
109
+ }
110
+ }
111
+ }
core/base/View.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\base;
4
+
5
+ class View extends BaseObject
6
+ {
7
+
8
+ /**
9
+ * @var array
10
+ */
11
+ private $_vars = [];
12
+
13
+ /**
14
+ * @var ViewContextInterface
15
+ */
16
+ public $context;
17
+
18
+ /**
19
+ * @param string $view
20
+ * @param array $params
21
+ * @param ViewContextInterface $context
22
+ * @return bool|string
23
+ */
24
+ public function render($view, $params = [], $context = null)
25
+ {
26
+ $currentContext = $context ? $context : $this->context;
27
+ return $this->renderFile($currentContext->getViewFiles($view), $params, $context);
28
+ }
29
+
30
+ /**
31
+ * @param array|string $files
32
+ * @param array $vars
33
+ * @param ViewContextInterface|null $context
34
+ * @return bool|string
35
+ */
36
+ public function renderFile($files, $vars, $context = null)
37
+ {
38
+ $count = count($this->_vars);
39
+ $this->_vars[$count] = $count > 0 ? array_merge($this->_vars[$count - 1], $vars) : $vars;
40
+
41
+ if (!is_array($files)) {
42
+ $files = [$files];
43
+ }
44
+ $file = false;
45
+ foreach ($files as $f) {
46
+ if (file_exists($f)) {
47
+ $file = $f;
48
+ break;
49
+ }
50
+ }
51
+ if ($file === false) {
52
+ return false;
53
+ }
54
+
55
+ $oldContext = $this->context;
56
+ if ($context !== null) {
57
+ $this->context = $context;
58
+ }
59
+ $result = $this->renderPhpFile($file, $this->_vars[$count]);
60
+ $this->context = $oldContext;
61
+
62
+ unset($this->_vars[$count]);
63
+
64
+ return $result;
65
+ }
66
+
67
+ /**
68
+ * @param string $____f
69
+ * @param array $____v
70
+ *
71
+ * @return false|string
72
+ */
73
+ public function renderPhpFile($____f, $____v)
74
+ {
75
+ extract($____v);
76
+ if (!file_exists($____f)) {
77
+ return false;
78
+ }
79
+ ob_start();
80
+ require $____f;
81
+ return ob_get_clean();
82
+ }
83
+ }
core/base/ViewContextInterface.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\base;
4
+
5
+ interface ViewContextInterface
6
+ {
7
+
8
+ /**
9
+ * @param string $view
10
+ * @return array
11
+ */
12
+ public function getViewFiles($view);
13
+ }
core/base/Widget.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\base;
4
+
5
+ use luckywp\tableOfContents\core\Core;
6
+ use ReflectionClass;
7
+
8
+ abstract class Widget extends BaseObject
9
+ {
10
+
11
+ private $_viewPath;
12
+
13
+ public function getViewPath()
14
+ {
15
+ if ($this->_viewPath === null) {
16
+ $class = new ReflectionClass($this);
17
+ $this->_viewPath = dirname($class->getFileName()) . '/views';
18
+ }
19
+ return $this->_viewPath;
20
+ }
21
+
22
+ /**
23
+ * @param string $view
24
+ * @param array $params
25
+ * @param bool $echo
26
+ * @return string|null
27
+ */
28
+ public function render($view, $params = [], $echo = false)
29
+ {
30
+ $html = Core::$plugin->view->renderFile($this->getViewPath() . '/' . $view . '.php', $params);
31
+ if ($echo) {
32
+ echo $html;
33
+ return null;
34
+ }
35
+ return $html;
36
+ }
37
+
38
+ /**
39
+ * @param array $config
40
+ * @return string
41
+ */
42
+ public static function widget($config = [])
43
+ {
44
+ /* @var $widget Widget */
45
+ $config['class'] = get_called_class();
46
+ $widget = Core::createObject($config);
47
+ return $widget->run();
48
+ }
49
+
50
+ abstract public function run();
51
+ }
core/front/BaseFront.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\front;
4
+
5
+ use luckywp\tableOfContents\core\base\BaseObject;
6
+ use luckywp\tableOfContents\core\base\ViewContextInterface;
7
+ use luckywp\tableOfContents\core\Core;
8
+ use ReflectionClass;
9
+
10
+ /**
11
+ * @property string $themeViewsDir
12
+ */
13
+ class BaseFront extends BaseObject implements ViewContextInterface
14
+ {
15
+
16
+ private $_theme_views_dir;
17
+
18
+ protected $defaultThemeViewsDir;
19
+
20
+ public function getThemeViewsDir()
21
+ {
22
+ if ($this->_theme_views_dir === null) {
23
+ $this->_theme_views_dir = apply_filters(Core::$plugin->prefix . 'theme_views_dir', $this->defaultThemeViewsDir);
24
+ }
25
+ return $this->_theme_views_dir;
26
+ }
27
+
28
+ private $_viewPath;
29
+
30
+ public function getViewPath()
31
+ {
32
+ if ($this->_viewPath === null) {
33
+ $class = new ReflectionClass($this);
34
+ $this->_viewPath = dirname($class->getFileName()) . '/views';
35
+ }
36
+ return $this->_viewPath;
37
+ }
38
+
39
+ /**
40
+ * @param string $view
41
+ * @return array
42
+ */
43
+ public function getViewFiles($view)
44
+ {
45
+ return [
46
+ get_template_directory() . '/' . $this->themeViewsDir . '/' . $view . '.php',
47
+ $this->getViewPath() . '/' . $view . '.php',
48
+ ];
49
+ }
50
+
51
+ /**
52
+ * @param string $view
53
+ * @param array $params
54
+ * @param bool $echo
55
+ * @return string|null
56
+ */
57
+ public function render($view, $params = [], $echo = false)
58
+ {
59
+ $html = Core::$plugin->view->renderFile($this->getViewFiles($view), $params, $this);
60
+ if ($echo) {
61
+ echo $html;
62
+ return null;
63
+ }
64
+ return $html;
65
+ }
66
+ }
core/helpers/ArrayHelper.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\helpers;
4
+
5
+ use Exception;
6
+
7
+ class ArrayHelper
8
+ {
9
+
10
+ /**
11
+ * @param array|object $array
12
+ * @param string $key
13
+ * @param mixed $default
14
+ * @return mixed
15
+ */
16
+ public static function getValue($array, $key, $default = null)
17
+ {
18
+ if (false !== $pos = strrpos($key, '.')) {
19
+ $array = static::getValue($array, substr($key, 0, $pos), $default);
20
+ $key = substr($key, $pos + 1);
21
+ }
22
+ if (is_object($array)) {
23
+ try {
24
+ return $array->$key;
25
+ } catch (Exception $e) {
26
+ return null;
27
+ }
28
+ }
29
+ if (is_array($array)) {
30
+ return array_key_exists($key, $array) ? $array[$key] : $default;
31
+ }
32
+ return $default;
33
+ }
34
+
35
+ /**
36
+ * @param array $array
37
+ * @param string $key
38
+ * @param mixed $default
39
+ * @return mixed
40
+ */
41
+ public static function remove(&$array, $key, $default = null)
42
+ {
43
+ if (is_array($array) && array_key_exists($key, $array)) {
44
+ $value = $array[$key];
45
+ unset($array[$key]);
46
+ return $value;
47
+ }
48
+ return $default;
49
+ }
50
+
51
+ /**
52
+ * @param array $array
53
+ * @param string $from
54
+ * @param string $to
55
+ * @param string $group
56
+ * @return array
57
+ */
58
+ public static function map($array, $from, $to, $group = null)
59
+ {
60
+ $result = [];
61
+ foreach ($array as $element) {
62
+ $key = static::getValue($element, $from);
63
+ $value = static::getValue($element, $to);
64
+ if ($group !== null) {
65
+ $result[static::getValue($element, $group)][$key] = $value;
66
+ } else {
67
+ $result[$key] = $value;
68
+ }
69
+ }
70
+ return $result;
71
+ }
72
+ }
core/helpers/Html.php ADDED
@@ -0,0 +1,323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\helpers;
4
+
5
+ use luckywp\tableOfContents\core\base\Model;
6
+
7
+ class Html
8
+ {
9
+
10
+ /**
11
+ * @var array
12
+ * @see https://www.w3.org/TR/html/syntax.html#void-elements
13
+ */
14
+ public static $voidElements = [
15
+ 'area',
16
+ 'base',
17
+ 'br',
18
+ 'col',
19
+ 'embed',
20
+ 'hr',
21
+ 'img',
22
+ 'input',
23
+ 'link',
24
+ 'meta',
25
+ 'param',
26
+ 'source',
27
+ 'track',
28
+ 'wbr',
29
+ ];
30
+
31
+ /**
32
+ * @param string|false|null $name
33
+ * @param string $content
34
+ * @param array $options
35
+ * @return string
36
+ */
37
+ public static function tag($name, $content = '', $options = [])
38
+ {
39
+ if ($name === null || $name === false) {
40
+ return $content;
41
+ }
42
+ $html = "<$name" . static::renderTagAttributes($options) . '>';
43
+ return array_key_exists(strtolower($name), static::$voidElements) ? $html : "$html$content</$name>";
44
+ }
45
+
46
+ /**
47
+ * @param string|false|null $name
48
+ * @param array $options
49
+ * @return string
50
+ */
51
+ public static function beginTag($name, $options = [])
52
+ {
53
+ if ($name === null || $name === false) {
54
+ return '';
55
+ }
56
+ return "<$name" . static::renderTagAttributes($options) . '>';
57
+ }
58
+
59
+ public static function renderTagAttributes($attributes)
60
+ {
61
+ $html = '';
62
+ foreach ($attributes as $name => $value) {
63
+ if (is_bool($value)) {
64
+ if ($value) {
65
+ $html .= " $name";
66
+ }
67
+ } elseif (is_array($value)) {
68
+ if ($name == 'data') {
69
+ foreach ($value as $n => $v) {
70
+ if (is_array($v)) {
71
+ $html .= " $name-$n='" . Json::htmlEncode($v) . "'";
72
+ } else {
73
+ $html .= " $name-$n=\"" . static::encode($v) . '"';
74
+ }
75
+ }
76
+ } elseif ($name === 'class') {
77
+ if (empty($value)) {
78
+ continue;
79
+ }
80
+ $html .= " $name=\"" . static::encode(implode(' ', $value)) . '"';
81
+ } elseif ($name === 'style') {
82
+ if (empty($value)) {
83
+ continue;
84
+ }
85
+ $html .= " $name=\"" . static::encode(static::cssStyleFromArray($value)) . '"';
86
+ } else {
87
+ $html .= " $name='" . Json::htmlEncode($value) . "'";
88
+ }
89
+ } elseif ($value !== null) {
90
+ $html .= " $name=\"" . static::encode($value) . '"';
91
+ }
92
+ }
93
+
94
+ return $html;
95
+ }
96
+
97
+ public static function encode($content, $doubleEncode = true)
98
+ {
99
+ return htmlspecialchars($content, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', $doubleEncode);
100
+ }
101
+
102
+ public static function cssStyleFromArray(array $style)
103
+ {
104
+ $result = '';
105
+ foreach ($style as $name => $value) {
106
+ $result .= "$name: $value; ";
107
+ }
108
+ return $result === '' ? null : rtrim($result);
109
+ }
110
+
111
+ public static function prepareClassInOptions(array $options)
112
+ {
113
+ if (!isset($options['class'])) {
114
+ $options['class'] = [];
115
+ }
116
+ if (!is_array($options['class'])) {
117
+ $options['class'] = [$options['class']];
118
+ }
119
+ return $options;
120
+ }
121
+
122
+ public static function a($text, $url = null, $options = [])
123
+ {
124
+ if ($url !== null) {
125
+ $options['href'] = $url;
126
+ }
127
+ return static::tag('a', $text, $options);
128
+ }
129
+
130
+ public static function label($content, $for = null, $options = [])
131
+ {
132
+ $options['for'] = $for;
133
+ return static::tag('label', $content, $options);
134
+ }
135
+
136
+ public static function input($type, $name = null, $value = null, $options = [])
137
+ {
138
+ if (!isset($options['type'])) {
139
+ $options['type'] = $type;
140
+ }
141
+ $options['name'] = $name;
142
+ $options['value'] = $value === null ? null : (string)$value;
143
+ return static::tag('input', '', $options);
144
+ }
145
+
146
+ public static function textInput($name, $value = null, $options = [])
147
+ {
148
+ return static::input('text', $name, $value, $options);
149
+ }
150
+
151
+ /**
152
+ * @param string $name
153
+ * @param string $value
154
+ * @param array $options
155
+ * @return string
156
+ */
157
+ public static function hiddenInput($name, $value = null, $options = [])
158
+ {
159
+ return static::input('hidden', $name, $value, $options);
160
+ }
161
+
162
+ /**
163
+ * @param string $type "radio" или "checkbox"
164
+ * @param string $name
165
+ * @param bool $checked
166
+ * @param array $options
167
+ * @return string
168
+ */
169
+ protected static function booleanInput($type, $name, $checked = false, $options = [])
170
+ {
171
+ $options['checked'] = (bool)$checked;
172
+ $value = array_key_exists('value', $options) ? $options['value'] : '1';
173
+
174
+ $hidden = '';
175
+ if (isset($options['uncheck'])) {
176
+ $hiddenOptions = [];
177
+ $hidden = static::hiddenInput($name, $options['uncheck'], $hiddenOptions);
178
+ unset($options['uncheck']);
179
+ }
180
+
181
+ if (isset($options['label'])) {
182
+ $label = $options['label'];
183
+ $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : [];
184
+ unset($options['label'], $options['labelOptions']);
185
+ $content = static::label(static::input($type, $name, $value, $options) . ' ' . $label, null, $labelOptions);
186
+ return $hidden . $content;
187
+ }
188
+ return $hidden . static::input($type, $name, $value, $options);
189
+ }
190
+
191
+ /**
192
+ * @param string $name
193
+ * @param bool $checked
194
+ * @param array $options
195
+ * @return string
196
+ */
197
+ public static function checkbox($name, $checked = false, $options = [])
198
+ {
199
+ return static::booleanInput('checkbox', $name, $checked, $options);
200
+ }
201
+
202
+ /**
203
+ * @param array|string|null $selection
204
+ * @param $items
205
+ * @param array $tagOptions
206
+ * @return string
207
+ */
208
+ public static function renderSelectOptions($selection, $items, &$tagOptions = [])
209
+ {
210
+ if (is_array($selection)) {
211
+ $selection = array_map('strval', (array)$selection);
212
+ }
213
+
214
+ $lines = [];
215
+ $encodeSpaces = ArrayHelper::remove($tagOptions, 'encodeSpaces', false);
216
+ $encode = ArrayHelper::remove($tagOptions, 'encode', true);
217
+ if (isset($tagOptions['prompt'])) {
218
+ $promptOptions = ['value' => ''];
219
+ if (is_string($tagOptions['prompt'])) {
220
+ $promptText = $tagOptions['prompt'];
221
+ } else {
222
+ $promptText = $tagOptions['prompt']['text'];
223
+ $promptOptions = array_merge($promptOptions, $tagOptions['prompt']['options']);
224
+ }
225
+ $promptText = $encode ? static::encode($promptText) : $promptText;
226
+ if ($encodeSpaces) {
227
+ $promptText = str_replace(' ', '&nbsp;', $promptText);
228
+ }
229
+ $lines[] = static::tag('option', $promptText, $promptOptions);
230
+ }
231
+
232
+ $options = isset($tagOptions['options']) ? $tagOptions['options'] : [];
233
+ $groups = isset($tagOptions['groups']) ? $tagOptions['groups'] : [];
234
+ unset($tagOptions['prompt'], $tagOptions['options'], $tagOptions['groups']);
235
+ $options['encodeSpaces'] = ArrayHelper::getValue($options, 'encodeSpaces', $encodeSpaces);
236
+ $options['encode'] = ArrayHelper::getValue($options, 'encode', $encode);
237
+
238
+ foreach ($items as $key => $value) {
239
+ if (is_array($value)) {
240
+ $groupAttrs = isset($groups[$key]) ? $groups[$key] : [];
241
+ if (!isset($groupAttrs['label'])) {
242
+ $groupAttrs['label'] = $key;
243
+ }
244
+ $attrs = ['options' => $options, 'groups' => $groups, 'encodeSpaces' => $encodeSpaces, 'encode' => $encode];
245
+ $content = static::renderSelectOptions($selection, $value, $attrs);
246
+ $lines[] = static::tag('optgroup', "\n" . $content . "\n", $groupAttrs);
247
+ } else {
248
+ $attrs = isset($options[$key]) ? $options[$key] : [];
249
+ $attrs['value'] = (string)$key;
250
+ if (!array_key_exists('selected', $attrs)) {
251
+ $attrs['selected'] = $selection !== null &&
252
+ (!is_array($selection) && !strcmp($key, $selection)
253
+ || is_array($selection) && in_array((string)$key, $selection));
254
+ }
255
+ $text = $encode ? static::encode($value) : $value;
256
+ if ($encodeSpaces) {
257
+ $text = str_replace(' ', '&nbsp;', $text);
258
+ }
259
+ $lines[] = static::tag('option', $text, $attrs);
260
+ }
261
+ }
262
+
263
+ return implode("\n", $lines);
264
+ }
265
+
266
+ /**
267
+ * @param string $name
268
+ * @param string|array|null $selection
269
+ * @param array $items
270
+ * @param array $options
271
+ * @return string
272
+ */
273
+ public static function dropDownList($name, $selection = null, $items = [], $options = [])
274
+ {
275
+ if (!empty($options['multiple'])) {
276
+ return static::listBox($name, $selection, $items, $options);
277
+ }
278
+ $options['name'] = $name;
279
+ unset($options['unselect']);
280
+ $selectOptions = static::renderSelectOptions($selection, $items, $options);
281
+ return static::tag('select', "\n" . $selectOptions . "\n", $options);
282
+ }
283
+
284
+ /**
285
+ * @param string $name
286
+ * @param string|array|null $selection
287
+ * @param array $items
288
+ * @param array $options
289
+ * @return string
290
+ */
291
+ public static function listBox($name, $selection = null, $items = [], $options = [])
292
+ {
293
+ if (!array_key_exists('size', $options)) {
294
+ $options['size'] = 4;
295
+ }
296
+ if (!empty($options['multiple']) && !empty($name) && substr_compare($name, '[]', -2, 2)) {
297
+ $name .= '[]';
298
+ }
299
+ $options['name'] = $name;
300
+ if (isset($options['unselect'])) {
301
+ if (!empty($name) && substr_compare($name, '[]', -2, 2) === 0) {
302
+ $name = substr($name, 0, -2);
303
+ }
304
+ $hidden = static::hiddenInput($name, $options['unselect']);
305
+ unset($options['unselect']);
306
+ } else {
307
+ $hidden = '';
308
+ }
309
+ $selectOptions = static::renderSelectOptions($selection, $items, $options);
310
+ return $hidden . static::tag('select', "\n" . $selectOptions . "\n", $options);
311
+ }
312
+
313
+ /**
314
+ * @param Model $model
315
+ * @param string $attribute
316
+ * @return string
317
+ */
318
+ public static function getInputName($model, $attribute)
319
+ {
320
+ $formName = $model->formName();
321
+ return $formName . '[' . $attribute . ']';
322
+ }
323
+ }
core/helpers/Json.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\helpers;
4
+
5
+ class Json
6
+ {
7
+
8
+ /**
9
+ * @see http://php.net/manual/ru/function.json-encode.php
10
+ * @param mixed $value Данные для кодирования
11
+ * @param int $options Настройки кодирования. По-умолчанию, `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE`
12
+ * @return string
13
+ */
14
+ public static function encode($value, $options = 320)
15
+ {
16
+ return wp_json_encode($value, $options);
17
+ }
18
+
19
+ /**
20
+ * @param mixed $value
21
+ * @return string
22
+ */
23
+ public static function htmlEncode($value)
24
+ {
25
+ return static::encode($value, JSON_UNESCAPED_UNICODE | JSON_HEX_QUOT | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS);
26
+ }
27
+ }
core/validators/BooleanValidator.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\validators;
4
+
5
+ class BooleanValidator extends Validator
6
+ {
7
+
8
+ /**
9
+ * @var mixed
10
+ */
11
+ public $trueValue = '1';
12
+
13
+ /**
14
+ * @var mixed
15
+ */
16
+ public $falseValue = '0';
17
+
18
+ /**
19
+ * @var bool
20
+ */
21
+ public $strict = false;
22
+
23
+ public function init()
24
+ {
25
+ parent::init();
26
+ if ($this->message === null) {
27
+ $this->message = __('{attribute} must be either "{true}" or "{false}".', 'lwptoc');
28
+ }
29
+ }
30
+
31
+ public function validateAttribute($model, $attribute)
32
+ {
33
+ $value = $model->$attribute;
34
+
35
+ if ($this->strict) {
36
+ $valid = $value === $this->trueValue || $value === $this->falseValue;
37
+ } else {
38
+ $valid = $value == $this->trueValue || $value == $this->falseValue;
39
+ }
40
+
41
+ if ($valid) {
42
+ return null;
43
+ }
44
+
45
+ return [
46
+ $this->message,
47
+ [
48
+ '{true}' => $this->trueValue,
49
+ '{false}' => $this->falseValue,
50
+ ]
51
+ ];
52
+ }
53
+ }
core/validators/FilterValidator.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\validators;
4
+
5
+ class FilterValidator extends Validator
6
+ {
7
+
8
+ /**
9
+ * @var callable
10
+ */
11
+ public $filter;
12
+
13
+ /**
14
+ * @var bool
15
+ */
16
+ public $skipOnEmpty = false;
17
+
18
+ public function validateAttribute($model, $attribute)
19
+ {
20
+ $value = $model->$attribute;
21
+ $model->$attribute = call_user_func($this->filter, $value);
22
+ }
23
+ }
core/validators/InlineValidator.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\validators;
4
+
5
+ class InlineValidator extends Validator
6
+ {
7
+
8
+ /**
9
+ * @var string|callable
10
+ */
11
+ public $method;
12
+
13
+ /**
14
+ * @var mixed
15
+ */
16
+ public $params;
17
+
18
+ public function validateAttribute($model, $attribute)
19
+ {
20
+ $method = $this->method;
21
+ if (is_string($method)) {
22
+ $method = [$model, $method];
23
+ }
24
+ call_user_func($method, $attribute, $this->params, $this);
25
+ }
26
+ }
core/validators/RangeValidator.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\validators;
4
+
5
+ class RangeValidator extends Validator
6
+ {
7
+
8
+ /**
9
+ * @var array
10
+ */
11
+ public $range;
12
+
13
+ /**
14
+ * @var bool
15
+ */
16
+ public $strict = false;
17
+
18
+ /**
19
+ * @var bool
20
+ */
21
+ public $not = false;
22
+
23
+ public function init()
24
+ {
25
+ parent::init();
26
+ if ($this->message === null) {
27
+ $this->message = __('{attribute} is invalid.', 'lwptoc');
28
+ }
29
+ }
30
+
31
+ protected function validateValue($value)
32
+ {
33
+ $in = in_array($value, $this->range, $this->strict);
34
+ return $this->not !== $in ? null : [$this->message, []];
35
+ }
36
+ }
core/validators/RequiredValidator.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\validators;
4
+
5
+ class RequiredValidator extends Validator
6
+ {
7
+ /**
8
+ * @var bool
9
+ */
10
+ public $skipOnEmpty = false;
11
+
12
+ public function init()
13
+ {
14
+ parent::init();
15
+ if ($this->message === null) {
16
+ $this->message = __('{attribute} cannot be blank.', 'lwptoc');
17
+ }
18
+ }
19
+
20
+ /**
21
+ * @param mixed $value
22
+ * @return array|null
23
+ */
24
+ protected function validateValue($value)
25
+ {
26
+ if (!$this->isEmpty(is_string($value) ? trim($value) : $value)) {
27
+ return null;
28
+ }
29
+ return [$this->message, []];
30
+ }
31
+ }
core/validators/Validator.php ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\validators;
4
+
5
+ use Exception;
6
+ use luckywp\tableOfContents\core\base\BaseObject;
7
+ use luckywp\tableOfContents\core\base\Model;
8
+ use luckywp\tableOfContents\core\Core;
9
+
10
+ abstract class Validator extends BaseObject
11
+ {
12
+
13
+ /**
14
+ * @var array Атрибуты для валидации
15
+ */
16
+ public $attributes;
17
+
18
+ /**
19
+ * @var string Сообщение об ошибке
20
+ */
21
+ public $message;
22
+
23
+ /**
24
+ * @var callable
25
+ */
26
+ public $when;
27
+
28
+ /**
29
+ * @var bool
30
+ */
31
+ public $skipOnError = true;
32
+
33
+ /**
34
+ * @var bool
35
+ */
36
+ public $skipOnEmpty = true;
37
+
38
+ /**
39
+ * @return array
40
+ */
41
+ public static function builtInValidators()
42
+ {
43
+ return [
44
+ 'boolean' => BooleanValidator::className(),
45
+ 'filter' => FilterValidator::className(),
46
+ 'in' => RangeValidator::className(),
47
+ 'required' => RequiredValidator::className(),
48
+ ];
49
+ }
50
+
51
+ /**
52
+ * Создаёт и возвращает валидатор
53
+ * @param string|callable $name
54
+ * @param Model $model
55
+ * @param array $attributes
56
+ * @param array $params
57
+ * @return mixed
58
+ */
59
+ public static function createValidator($name, $model, $attributes, $params = [])
60
+ {
61
+ $params['attributes'] = $attributes;
62
+ if (is_callable($name) || method_exists($model, $name)) {
63
+ $params['class'] = InlineValidator::className();
64
+ $params['method'] = $name;
65
+ } else {
66
+ if (isset(static::builtInValidators()[$name])) {
67
+ $name = static::builtInValidators()[$name];
68
+ }
69
+ $params['class'] = $name;
70
+ }
71
+ return Core::createObject($params);
72
+ }
73
+
74
+ /**
75
+ * @param Model $model
76
+ * @param array|null $attributes
77
+ */
78
+ public function validateAttributes($model, $attributes = null)
79
+ {
80
+ if (is_array($attributes)) {
81
+ $newAttributes = [];
82
+ foreach ($attributes as $attribute) {
83
+ if (in_array($attribute, $this->getAttributeNames(), true)) {
84
+ $newAttributes[] = $attribute;
85
+ }
86
+ }
87
+ $attributes = $newAttributes;
88
+ } else {
89
+ $attributes = $this->getAttributeNames();
90
+ }
91
+
92
+ foreach ($attributes as $attribute) {
93
+ $skip = $this->skipOnError && $model->hasErrors($attribute)
94
+ || $this->skipOnEmpty && $this->isEmpty($model->$attribute);
95
+ if (!$skip) {
96
+ if ($this->when === null || call_user_func($this->when, $model, $attribute)) {
97
+ $this->validateAttribute($model, $attribute);
98
+ }
99
+ }
100
+ }
101
+ }
102
+
103
+ /**
104
+ * @param Model $model
105
+ * @param string
106
+ */
107
+ public function validateAttribute($model, $attribute)
108
+ {
109
+ $result = $this->validateValue($model->$attribute);
110
+ if (!empty($result)) {
111
+ $this->addError($model, $attribute, $result[0], $result[1]);
112
+ }
113
+ }
114
+
115
+ /**
116
+ * @param mixed $value
117
+ */
118
+ protected function validateValue($value)
119
+ {
120
+ throw new Exception(get_class($this) . ' does not support validateValue().');
121
+ }
122
+
123
+ /**
124
+ * @param Model $model
125
+ * @param string $attribute
126
+ * @param string $message
127
+ * @param array $params
128
+ */
129
+ public function addError($model, $attribute, $message, $params = [])
130
+ {
131
+ $params['attribute'] = $model->getAttributeLabel($attribute);
132
+ if (!isset($params['value'])) {
133
+ $value = $model->$attribute;
134
+ if (is_array($value)) {
135
+ $params['value'] = 'array()';
136
+ } elseif (is_object($value) && !method_exists($value, '__toString')) {
137
+ $params['value'] = '(object)';
138
+ } else {
139
+ $params['value'] = $value;
140
+ }
141
+ }
142
+ $model->addError($attribute, $this->formatMessage($message, $params));
143
+ }
144
+
145
+ /**
146
+ * @param string $message
147
+ * @param array $params
148
+ * @return string
149
+ */
150
+ protected function formatMessage($message, $params)
151
+ {
152
+ $placeholders = [];
153
+ foreach ($params as $name => $value) {
154
+ $placeholders['{' . $name . '}'] = $value;
155
+ }
156
+ return ($placeholders === []) ? $message : strtr($message, $placeholders);
157
+ }
158
+
159
+ /**
160
+ * @param mixed $value
161
+ * @return bool
162
+ */
163
+ public function isEmpty($value)
164
+ {
165
+ return $value === null || $value === [] || $value === '';
166
+ }
167
+
168
+ /**
169
+ * @return array
170
+ */
171
+ public function getAttributeNames()
172
+ {
173
+ return $this->attributes;
174
+ }
175
+ }
core/wp/Settings.php ADDED
@@ -0,0 +1,551 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\core\wp;
4
+
5
+ use luckywp\tableOfContents\core\admin\helpers\AdminHtml;
6
+ use luckywp\tableOfContents\core\base\BaseObject;
7
+ use luckywp\tableOfContents\core\Core;
8
+ use luckywp\tableOfContents\core\helpers\ArrayHelper;
9
+ use luckywp\tableOfContents\core\helpers\Html;
10
+
11
+ class Settings extends BaseObject
12
+ {
13
+
14
+ /**
15
+ * Префикс. Добавляется к названию секций и групп
16
+ * @var string
17
+ */
18
+ protected $prefix;
19
+
20
+ /**
21
+ * Массив описывает группы опций.
22
+ * Каждый элемент массива — массив с параметрами группы.
23
+ * Парамтеры:
24
+ * id - идентификатор
25
+ * name - идентификатор без префикса
26
+ * label - заголовок (используется, как заголовок таба)
27
+ * @var array
28
+ */
29
+ protected $groups = [];
30
+
31
+ /**
32
+ * Массив описывает секции ($groupId => $config)
33
+ * Каждый элемент массива — массив с параметрами секции
34
+ * Параметры секций:
35
+ * id - идентификатор (должно быть уникально)
36
+ * title - заголовок
37
+ * desc - описание
38
+ * group - ID группы
39
+ * @var array
40
+ */
41
+ protected $sections = [];
42
+
43
+
44
+ /**
45
+ * Массив описывает поля ($groupId => $config)
46
+ * Каждый элемент массива — массив с параметрами поля
47
+ * Параметры полей:
48
+ * id - идентификатор (должно быть уникально в рамках группы)
49
+ * label - заголовок
50
+ * desc - описание
51
+ * default - значение по-умолчанию
52
+ * widget - виджет для ввода
53
+ * params - параметры виджета
54
+ * sanitizeCallback - функция для валидации и подготовки значения
55
+ * section - ID секции
56
+ * group - ID группы
57
+ * name - атрибут name для виджета
58
+ * @var array
59
+ */
60
+ protected $fields = [];
61
+
62
+ /**
63
+ * Конфиг для инициализации
64
+ * @var array
65
+ */
66
+ public $initGroupsConfig = [];
67
+
68
+ /**
69
+ * Файл с конфигом для инициализации
70
+ * @var array
71
+ */
72
+ public $initGroupsConfigFile;
73
+
74
+ public function init()
75
+ {
76
+ if ($this->prefix === null) {
77
+ $this->prefix = Core::$plugin->prefix;
78
+ }
79
+
80
+ $config = [];
81
+ if ($this->initGroupsConfig) {
82
+ $config = $this->initGroupsConfig;
83
+ } elseif ($this->initGroupsConfigFile !== null) {
84
+ $config = require($this->initGroupsConfigFile);
85
+ }
86
+ if ($config) {
87
+ $this->addGroups($config);
88
+ }
89
+
90
+ if (is_admin() && !wp_doing_ajax()) {
91
+ add_action('admin_init', [$this, 'adminInit']);
92
+ }
93
+ }
94
+
95
+ public function adminInit()
96
+ {
97
+ // Добавляем секции
98
+ foreach ($this->sections as $groupId => $sections) {
99
+ foreach ($sections as $sectionId => $section) {
100
+ add_settings_section(
101
+ $section['id'],
102
+ $section['title'],
103
+ [$this, 'sectionCallback'],
104
+ $section['group']
105
+ );
106
+ }
107
+ }
108
+
109
+ // Добавляем поля
110
+ foreach ($this->fields as $groupId => $fields) {
111
+ foreach ($fields as $fieldId => $field) {
112
+ $callback = null;
113
+ if (is_callable($field['widget'])) {
114
+ $callback = $field['widget'];
115
+ } elseif (is_string($field['widget'])) {
116
+ $methodName = 'field' . ucfirst($field['widget']);
117
+ if (method_exists($this, $methodName)) {
118
+ $callback = [$this, $methodName];
119
+ }
120
+ }
121
+ add_settings_field(
122
+ $field['id'],
123
+ $field['label'],
124
+ $callback,
125
+ $field['group'],
126
+ $field['section'],
127
+ $field
128
+ );
129
+ }
130
+ }
131
+
132
+ // Регистрация настроек
133
+ foreach ($this->groups as $group) {
134
+ register_setting(
135
+ $group['id'],
136
+ $group['id'],
137
+ [$this, 'sanitizeCallback']
138
+ );
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Добавление групп
144
+ * @param array $config
145
+ * @return $this
146
+ */
147
+ public function addGroups($config)
148
+ {
149
+ foreach ($config as $groupId => $groupConfig) {
150
+ $this->addGroup($groupId, $groupConfig);
151
+ }
152
+ return $this;
153
+ }
154
+
155
+ /**
156
+ * Добавление группы
157
+ * @param string $groupId
158
+ * @param array $config
159
+ * @return $this
160
+ */
161
+ public function addGroup($groupId, $config)
162
+ {
163
+ $group = [
164
+ 'id' => $this->prefix . $groupId,
165
+ 'name' => $groupId,
166
+ 'label' => ArrayHelper::getValue($config, 'label', $groupId),
167
+ ];
168
+ $this->groups[$group['id']] = $group;
169
+ $this->addSections($groupId, ArrayHelper::getValue($config, 'sections', []));
170
+ return $this;
171
+ }
172
+
173
+ /**
174
+ * Добавление секций
175
+ * @param string $groupId
176
+ * @param array $config
177
+ * @return $this
178
+ */
179
+ public function addSections($groupId, $config)
180
+ {
181
+ foreach ($config as $sectionId => $sectionConfig) {
182
+ $this->addSection($sectionId, $groupId, $sectionConfig);
183
+ }
184
+ return $this;
185
+ }
186
+
187
+ /**
188
+ * Добавление секции
189
+ * @param string $sectionId
190
+ * @param string $groupId
191
+ * @param array $config
192
+ * @return $this
193
+ */
194
+ public function addSection($sectionId, $groupId, $config)
195
+ {
196
+ $section = [
197
+ 'id' => $this->prefix . $sectionId,
198
+ 'group' => $this->prefix . $groupId,
199
+ 'title' => ArrayHelper::getValue($config, 'title'),
200
+ 'desc' => ArrayHelper::getValue($config, 'desc'),
201
+ ];
202
+ $this->sections[$section['group']][$section['id']] = $section;
203
+ $this->addFields($groupId, $sectionId, ArrayHelper::getValue($config, 'fields', []));
204
+ return $this;
205
+ }
206
+
207
+ /**
208
+ * Добавление полей
209
+ * @param string $groupId
210
+ * @param string $sectionId
211
+ * @param array $config
212
+ * @return $this
213
+ */
214
+ public function addFields($groupId, $sectionId, $config)
215
+ {
216
+ foreach ($config as $fieldId => $fieldConfig) {
217
+ $this->addField($fieldId, $groupId, $sectionId, $fieldConfig);
218
+ }
219
+ return $this;
220
+ }
221
+
222
+ /**
223
+ * Добавление поля
224
+ * @param string $fieldId
225
+ * @param string $groupId
226
+ * @param string $sectionId
227
+ * @param array $config
228
+ * @return $this
229
+ */
230
+ public function addField($fieldId, $groupId, $sectionId, $config)
231
+ {
232
+ $field = [
233
+ 'id' => $fieldId,
234
+ 'group' => $this->prefix . $groupId,
235
+ 'section' => $this->prefix . $sectionId,
236
+ 'label' => ArrayHelper::getValue($config, 'label'),
237
+ 'desc' => ArrayHelper::getValue($config, 'desc'),
238
+ 'default' => ArrayHelper::getValue($config, 'default'),
239
+ 'widget' => ArrayHelper::getValue($config, 'widget', 'textInput'),
240
+ 'params' => ArrayHelper::getValue($config, 'params', []),
241
+ 'sanitizeCallback' => ArrayHelper::getValue($config, 'sanitizeCallback'),
242
+ 'name' => $this->prefix . $groupId . '[' . $fieldId . ']',
243
+ ];
244
+ $this->fields[$field['group']][$field['id']] = $field;
245
+ return $this;
246
+ }
247
+
248
+ public function install()
249
+ {
250
+ $options = [];
251
+ foreach ($this->fields as $groupId => $fields) {
252
+ foreach ($fields as $fieldId => $field) {
253
+ if (!is_null($field['default'])) {
254
+ if (!isset($options[$field['group']])) {
255
+ $options[$field['group']] = [];
256
+ }
257
+ $options[$field['group']][$field['id']] = $field['default'];
258
+ }
259
+ }
260
+ }
261
+ foreach ($options as $option => $value) {
262
+ if (get_option($option) === false) {
263
+ add_option($option, $value);
264
+ }
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Вывод описания секции
270
+ * @param $arg
271
+ */
272
+ public function sectionCallback($arg)
273
+ {
274
+ foreach ($this->sections as $sections) {
275
+ if (array_key_exists($arg['id'], $sections)) {
276
+ echo $sections[$arg['id']]['desc'];
277
+ }
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Подготовка и валидация значений
283
+ * @param $options
284
+ * @return mixed
285
+ */
286
+ public function sanitizeCallback($options)
287
+ {
288
+ if (!is_array($options)) {
289
+ return [];
290
+ }
291
+ $currentGroup = $this->getCurrentGroup();
292
+ if ($currentGroup === false) {
293
+ return [];
294
+ }
295
+ foreach ($options as $id => $value) {
296
+ $oldValue = $this->getValue($currentGroup['id'], $id, null, false);
297
+
298
+ if (is_string($this->fields[$currentGroup['id']][$id]['widget'])) {
299
+ $methodName = 'sanitizeCallback' . ucfirst($this->fields[$currentGroup['id']][$id]['widget']);
300
+ if (method_exists($this, $methodName)) {
301
+ $options[$id] = call_user_func([$this, $methodName], $value, $oldValue, $id);
302
+ }
303
+ }
304
+
305
+ $callable = ArrayHelper::getValue($this->fields[$currentGroup['id']][$id], 'sanitizeCallback');
306
+ if (is_callable($callable)) {
307
+ $options[$id] = call_user_func($callable, $value, $oldValue, $id);
308
+ }
309
+ }
310
+ return $options;
311
+ }
312
+
313
+ /**
314
+ * Возвращает значение опции
315
+ * @param $groupId
316
+ * @param $fieldId
317
+ * @param string $default
318
+ * @param bool $prepareArgs
319
+ * @return string
320
+ */
321
+ public function getValue($groupId, $fieldId, $default = null, $prepareArgs = true)
322
+ {
323
+ if ($prepareArgs) {
324
+ $groupId = $this->prefix . $groupId;
325
+ }
326
+ $options = get_option($groupId);
327
+ if (is_array($options) && array_key_exists($fieldId, $options)) {
328
+ return $options[$fieldId];
329
+ }
330
+ return $default;
331
+ }
332
+
333
+ /**
334
+ * Возвращает все значения опций группы
335
+ * @param string $groupId
336
+ * @return array
337
+ */
338
+ public function getValues($groupId)
339
+ {
340
+ $values = get_option($this->prefix . $groupId);
341
+ return is_array($values) ? $values : [];
342
+ }
343
+
344
+ /**
345
+ * @param string $groupId
346
+ * @param string $fieldId
347
+ * @param mixed $value
348
+ */
349
+ public function setValue($groupId, $fieldId, $value)
350
+ {
351
+ $values = $this->getValues($groupId);
352
+ $values[$fieldId] = $value;
353
+ update_option($this->prefix . $groupId, $values);
354
+ }
355
+
356
+
357
+ /**
358
+ * ---------------------------------------------------------------------------
359
+ * Вывод и обработка формы
360
+ * ---------------------------------------------------------------------------
361
+ */
362
+
363
+ /**
364
+ * Возвращает текущую группу
365
+ * @return array|false
366
+ */
367
+ private function getCurrentGroup()
368
+ {
369
+ if (!count($this->groups)) {
370
+ return false;
371
+ }
372
+
373
+ $key = ArrayHelper::getValue($_POST, 'option_page');
374
+ if ($key === null) {
375
+ $tab = ArrayHelper::getValue($_GET, 'tab');
376
+ if ($tab) {
377
+ $key = $this->prefix . $tab;
378
+ }
379
+ }
380
+
381
+ if ($key && isset($this->groups[$key])) {
382
+ return $this->groups[$key];
383
+ }
384
+
385
+ reset($this->groups);
386
+ $group = current($this->groups);
387
+ return $group;
388
+ }
389
+
390
+ /**
391
+ * Вывод табов
392
+ */
393
+ public function showTabs()
394
+ {
395
+ // Активная группа
396
+ $currentGroup = $this->getCurrentGroup();
397
+ if ($currentGroup === false) {
398
+ return;
399
+ }
400
+
401
+ // Вывод
402
+ echo '<h2 class="nav-tab-wrapper">';
403
+ foreach ($this->groups as $group) {
404
+ echo '<a href="?page=' . $_GET['page'] . '&tab=' . $group['name'] . '" class="nav-tab' . ($group['name'] == $currentGroup['name'] ? ' nav-tab-active' : '') . '">' . $group['label'] . '</a>';
405
+ }
406
+ echo '</h2>';
407
+ }
408
+
409
+ /**
410
+ * Вывод формы
411
+ * @param string|null $groupId
412
+ */
413
+ public function showForm($groupId = null)
414
+ {
415
+ if ($groupId === null) {
416
+ $currentGroup = $this->getCurrentGroup();
417
+ if ($currentGroup === false) {
418
+ return;
419
+ }
420
+ $groupId = $currentGroup['id'];
421
+ } else {
422
+ $groupId = $this->prefix . $groupId;
423
+ }
424
+
425
+ echo '<form action="options.php" method="POST">';
426
+ settings_fields($groupId);
427
+ do_settings_sections($groupId);
428
+ submit_button();
429
+ echo '</form>';
430
+ }
431
+
432
+ /**
433
+ * Вывод страницы с настройками
434
+ * @param bool $showErrors
435
+ */
436
+ public function showPage($showErrors = true)
437
+ {
438
+ if ($showErrors) {
439
+ settings_errors();
440
+ }
441
+ if (count($this->groups) > 1) {
442
+ $this->showTabs();
443
+ }
444
+ $this->showForm();
445
+ }
446
+
447
+
448
+ /**
449
+ * ---------------------------------------------------------------------------
450
+ * Поля
451
+ * ---------------------------------------------------------------------------
452
+ */
453
+
454
+ /**
455
+ * @param array $field
456
+ */
457
+ public function fieldTextInput($field)
458
+ {
459
+ $value = $this->getValue($field['group'], $field['id'], '', false);
460
+
461
+ // Параметры
462
+ $params = $field['params'];
463
+ $inputOptions = isset($params['inputOptions']) ? $params['inputOptions'] : [];
464
+ $before = ArrayHelper::getValue($params, 'before');
465
+ if (is_callable($before)) {
466
+ $before = call_user_func($before);
467
+ }
468
+ $after = ArrayHelper::getValue($params, 'after');
469
+ if (is_callable($after)) {
470
+ $after = call_user_func($after);
471
+ }
472
+
473
+ // Вывод
474
+ echo $before . AdminHtml::textInput($field['name'], $value, $inputOptions) . $after;
475
+ if ($field['desc'] != '') {
476
+ echo '<p class="description">' . $field['desc'] . '</p>';
477
+ }
478
+ }
479
+
480
+ /**
481
+ * @param $field
482
+ */
483
+ public function fieldSelect($field)
484
+ {
485
+ $value = $this->getValue($field['group'], $field['id'], '', false);
486
+
487
+ // Параметры
488
+ $params = $field['params'];
489
+ $selectOptions = ArrayHelper::getValue($params, 'selectOptions', []);
490
+ $items = ArrayHelper::getValue($params, 'items', []);
491
+
492
+ // Вывод
493
+ echo Html::dropDownList($field['name'], $value, $items, $selectOptions);
494
+ if ($field['desc'] != '') {
495
+ echo '<p class="description">' . $field['desc'] . '</p>';
496
+ }
497
+ }
498
+
499
+ /**
500
+ * @param array $field
501
+ */
502
+ public function fieldCheckbox($field)
503
+ {
504
+ $checked = (bool)$this->getValue($field['group'], $field['id'], false, false);
505
+
506
+ // Параметры
507
+ $params = $field['params'];
508
+ $checkboxOptions = isset($params['checkboxOptions']) ? $params['checkboxOptions'] : [];
509
+ $checkboxOptions['value'] = 1;
510
+ $checkboxOptions['uncheck'] = 0;
511
+
512
+ // Вывод
513
+ echo Html::checkbox($field['name'], $checked, $checkboxOptions);
514
+ if ($field['desc'] != '') {
515
+ echo '<p class="description">' . $field['desc'] . '</p>';
516
+ }
517
+ }
518
+
519
+ /**
520
+ * @param mixed $value
521
+ * @return bool
522
+ */
523
+ public function sanitizeCallbackCheckbox($value)
524
+ {
525
+ return (bool)$value;
526
+ }
527
+
528
+ /**
529
+ * @param array $field
530
+ */
531
+ public function fieldColor($field)
532
+ {
533
+ $value = $this->getValue($field['group'], $field['id'], '', false);
534
+
535
+ // Вывод
536
+ echo Html::textInput($field['name'], $value, ['class' => Core::$plugin->prefix . 'colorPicker']);
537
+ if ($field['desc'] != '') {
538
+ echo '<p class="description">' . $field['desc'] . '</p>';
539
+ }
540
+ }
541
+
542
+ /**
543
+ * @param mixed $value
544
+ * @return string|null
545
+ */
546
+ public function sanitizeCallbackColor($value)
547
+ {
548
+ $value = strtolower((string)$value);
549
+ return preg_match('/^#[0-9a-f]{6}$/', $value) ? $value : null;
550
+ }
551
+ }
front/Front.php ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\front;
4
+
5
+ use luckywp\tableOfContents\core\Core;
6
+ use luckywp\tableOfContents\core\front\BaseFront;
7
+ use luckywp\tableOfContents\core\helpers\ArrayHelper;
8
+ use luckywp\tableOfContents\plugin\PostSettings;
9
+ use luckywp\tableOfContents\plugin\Shortcode;
10
+
11
+ class Front extends BaseFront
12
+ {
13
+
14
+ public $defaultThemeViewsDir = 'luckywp-table-of-contents';
15
+
16
+ public function init()
17
+ {
18
+ parent::init();
19
+ if (Core::isFront()) {
20
+ add_action('wp_enqueue_scripts', [$this, 'assets']);
21
+ add_action('init', function () {
22
+ if (Core::$plugin->settings->autoInsertEnable) {
23
+ add_filter('the_content', [$this, 'autoInsert'], 998);
24
+ }
25
+ });
26
+ add_action('wp_footer', [$this, 'overrideColors']);
27
+ }
28
+ }
29
+
30
+ public function assets()
31
+ {
32
+ wp_register_style('lwptoc-main', Core::$plugin->url . '/front/assets/main.min.css', [], Core::$plugin->version);
33
+ if (apply_filters('lwptoc_enqueue_style', true)) {
34
+ wp_enqueue_style('lwptoc-main');
35
+ }
36
+ wp_register_script('lwptoc-main', Core::$plugin->url . '/front/assets/main.min.js', ['jquery'], Core::$plugin->version);
37
+ if (apply_filters('lwptoc_enqueue_script', true)) {
38
+ wp_enqueue_script('lwptoc-main');
39
+ }
40
+ }
41
+
42
+ public function overrideColors()
43
+ {
44
+ if (Core::$plugin->shortcode->overrideColors) {
45
+ $styles = [];
46
+
47
+ $iStyles = [];
48
+ if ($color = ArrayHelper::getValue(Core::$plugin->shortcode->overrideColors, 'backgroundColor')) {
49
+ $iStyles[] = 'background-color:' . $color . ';';
50
+ }
51
+ if ($color = ArrayHelper::getValue(Core::$plugin->shortcode->overrideColors, 'borderColor')) {
52
+ $iStyles[] = 'border:1px solid ' . $color . ';';
53
+ }
54
+ if ($iStyles) {
55
+ $styles[] = '.lwptoc .lwptoc_i{' . implode($iStyles) . '}';
56
+ }
57
+
58
+ if ($color = ArrayHelper::getValue(Core::$plugin->shortcode->overrideColors, 'titleColor')) {
59
+ $styles[] = '.lwptoc_header{color:' . $color . ';}';
60
+ }
61
+
62
+ if ($color = ArrayHelper::getValue(Core::$plugin->shortcode->overrideColors, 'linkColor')) {
63
+ $styles[] = '.lwptoc .lwptoc_i A{color:' . $color . ';}';
64
+ }
65
+ if ($color = ArrayHelper::getValue(Core::$plugin->shortcode->overrideColors, 'hoverLinkColor')) {
66
+ $styles[] = '.lwptoc .lwptoc_i A:hover,.lwptoc .lwptoc_i A:focus,.lwptoc .lwptoc_i A:active{color:' . $color . ';border-color:' . $color . ';}';
67
+ }
68
+ if ($color = ArrayHelper::getValue(Core::$plugin->shortcode->overrideColors, 'visitedLinkColor')) {
69
+ $styles[] = '.lwptoc .lwptoc_i A:visited{color:' . $color . ';}';
70
+ }
71
+
72
+ if ($styles) {
73
+ echo '<style>' . implode('', $styles) . '</style>';
74
+ }
75
+ }
76
+ }
77
+
78
+ /**
79
+ * @param string $content
80
+ * @return string
81
+ */
82
+ public function autoInsert($content)
83
+ {
84
+ global $post;
85
+
86
+ if (!is_single($post) && !is_page($post)) {
87
+ return $content;
88
+ }
89
+
90
+ if (!in_array($post->post_type, Core::$plugin->settings->autoInsertPostTypes)) {
91
+ return $content;
92
+ }
93
+
94
+ if (has_shortcode($content, Shortcode::TAG)) {
95
+ return $content;
96
+ }
97
+
98
+ $settings = new PostSettings($post->ID);
99
+
100
+ if (!$settings->enabled) {
101
+ return $content;
102
+ }
103
+
104
+ $attrs = [];
105
+ $attrs['min'] = $settings->min;
106
+ $attrs['depth'] = $settings->depth;
107
+ $attrs['hierarchical'] = $settings->hierarchical;
108
+ $attrs['numeration'] = $settings->numeration;
109
+ $attrs['title'] = $settings->title;
110
+ $attrs['toggle'] = $settings->toggle;
111
+ $attrs['labelShow'] = $settings->labelShow;
112
+ $attrs['labelHide'] = $settings->labelHide;
113
+ $attrs['hideItems'] = $settings->hideItems;
114
+ $attrs['smoothScroll'] = $settings->smoothScroll;
115
+ $attrs['smoothScrollOffset'] = $settings->smoothScrollOffset;
116
+ $attrs['width'] = $settings->width;
117
+ $attrs['float'] = $settings->float;
118
+ $attrs['titleFontSize'] = $settings->titleFontSize;
119
+ $attrs['titleFontWeight'] = $settings->titleFontWeight;
120
+ $attrs['itemsFontSize'] = $settings->itemsFontSize;
121
+ $attrs['colorScheme'] = $settings->colorScheme;
122
+ $attrs['backgroundColor'] = $settings->backgroundColor;
123
+ $attrs['borderColor'] = $settings->borderColor;
124
+ $attrs['titleColor'] = $settings->titleColor;
125
+ $attrs['linkColor'] = $settings->linkColor;
126
+ $attrs['hoverLinkColor'] = $settings->hoverLinkColor;
127
+ $attrs['visitedLinkColor'] = $settings->visitedLinkColor;
128
+
129
+ $shortcode = Core::$plugin->shortcode->make($attrs);
130
+
131
+ $position = $settings->position ? $settings->position : Core::$plugin->settings->autoInsertPosition;
132
+ switch ($position) {
133
+ case 'beforefirstheading':
134
+ default:
135
+ $result = preg_replace($this->generateRegexp('h[1-6]'), $shortcode . ' $1', $content, 1, $count);
136
+ return $count ? $result : ($shortcode . $content);
137
+
138
+ case 'afterfirstheading':
139
+ $result = preg_replace($this->generateRegexp('h[1-6]'), '$1 ' . $shortcode, $content, 1, $count);
140
+ return $count ? $result : ($shortcode . $content);
141
+
142
+ case 'afterfirstblock':
143
+ $result = preg_replace($this->generateRegexp('[^ ]+'), '$1 ' . $shortcode, $content, 1, $count);
144
+ return $count ? $result : ($shortcode . $content);
145
+
146
+ case 'bottom':
147
+ return $content . $shortcode;
148
+
149
+ case 'top':
150
+ return $shortcode . $content;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * @param string $tagsRe
156
+ * @return string
157
+ */
158
+ protected function generateRegexp($tagsRe)
159
+ {
160
+ return '#(<(' . $tagsRe . ')[^>]*>.*?(<\2[^>]*>.*?</\2>)*.*?</\2>)#imsu';
161
+ }
162
+ }
front/assets/main.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .lwptoc{margin:32px 0}.lwptoc:first-child{margin-top:16px}.lwptoc_i{padding:14px 18px 18px}.lwptoc_i DIV A{box-shadow:none!important;border:none!important;text-decoration:none!important}.lwptoc_i DIV A:active,.lwptoc_i DIV A:focus,.lwptoc_i DIV A:hover{box-shadow:none!important;border:none!important;text-decoration:none!important}.lwptoc_i DIV A:hover{border-bottom:1px dotted!important}.lwptoc_header{margin-bottom:6px}.lwptoc_toggle{margin-left:4px;font-size:80%}.lwptoc_toggle:before{content:'['}.lwptoc_toggle:after{content:']'}.lwptoc_toggle_label{margin:0 1px}.lwptoc_item_number{margin-right:2px}.lwptoc UL{list-style:none;padding:0;margin:0}.lwptoc UL UL{margin:2px 0 0 14px}.lwptoc LI{margin:2px 0 0 0;padding:0}.lwptoc LI:first-child{margin-top:0}.lwptoc-autoWidth .lwptoc_i{display:inline-block}.lwptoc-left{float:left;margin-top:0;margin-right:32px}.lwptoc-right{float:right;margin-top:0;margin-left:32px}.lwptoc-baseItems .lwptoc_items{font-size:90%}.lwptoc-light .lwptoc_i{color:#333;background:#fafafa}.lwptoc-light .lwptoc_i A{color:#3175e4}.lwptoc-light .lwptoc_i A:active,.lwptoc-light .lwptoc_i A:focus,.lwptoc-light .lwptoc_i A:hover{color:#3175e4;border-color:#3175e4}.lwptoc-light .lwptoc_items A:visited{color:#000394}.lwptoc-dark .lwptoc_i{color:#d3d5d8;background:#2b2b2b}.lwptoc-dark .lwptoc_i A{color:#96c6ff}.lwptoc-dark .lwptoc_i A:active,.lwptoc-dark .lwptoc_i A:focus,.lwptoc-dark .lwptoc_i A:hover{color:#96c6ff;border-color:#71b2ff}.lwptoc-dark .lwptoc_items A:visited{color:#53a1ff}.lwptoc-white .lwptoc_i{color:#333;background:#fff}.lwptoc-white .lwptoc_i A{color:#3175e4}.lwptoc-white .lwptoc_i A:active,.lwptoc-white .lwptoc_i A:focus,.lwptoc-white .lwptoc_i A:hover{color:#3175e4;border-color:#3175e4}.lwptoc-white .lwptoc_items A:visited{color:#000394}.lwptoc-transparent .lwptoc_i{border:1px solid #eee}
front/assets/main.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(a){var t=function(t){t.each(function(){var i=a(this);i.data("smoothScroll")&&i.find(".lwptoc_items").on("click",'A[href^="#"]',function(t){t.preventDefault();var l=a("A[name="+this.hash.slice(1)+"]");if(l.length){var o=a(this).attr("href"),n=l.offset().top-i.data("smoothScrollOffset");a("html, body").animate({scrollTop:n<0?0:n},500,function(){if(l.focus(),l.is(":focus"))return!1;l.attr("tabindex","-1"),l.focus(),window.history.pushState&&window.history.pushState(null,null,o)})}})}),t.find(".lwptoc_toggle_label").click(function(){var t=a(this),l=t.closest(".lwptoc").find(".lwptoc_items"),o=t.data("label");return"none"==l.css("display")?(l.stop(!0).slideDown(300),t.data("label",t.html()).html(o)):l.stop(!0).slideUp(300,function(){t.data("label",t.html()).html(o)}),!1})};a(function(){t(a(".lwptoc"))})}(jQuery);
front/views/body.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var $title string
4
+ * @var $toggle bool
5
+ * @var $labelShow string
6
+ * @var $labelHide string
7
+ * @var $hideItems bool
8
+ * @var $containerOptions array
9
+ * @var $headerStyles array
10
+ * @var $titleStyles array
11
+ * @var $itemsStyles array
12
+ * @var $items array
13
+ */
14
+
15
+ use luckywp\tableOfContents\core\helpers\Html;
16
+
17
+ echo Html::beginTag('div', $containerOptions);
18
+ ?>
19
+ <div class="lwptoc_i">
20
+ <?php if ($toggle || $title) { ?>
21
+ <div class="lwptoc_header"<?= $headerStyles ? ' style="' . implode('', $headerStyles) . '"' : '' ?>>
22
+ <?= $title ? '<b class="lwptoc_title"' . ($titleStyles ? ' style="' . implode('', $titleStyles) . '"' : '') . '>' . $title . '</b>' : '' ?>
23
+ <?php if ($toggle) { ?>
24
+ <span class="lwptoc_toggle">
25
+ <a href="#" class="lwptoc_toggle_label" data-label="<?= $hideItems ? $labelHide : $labelShow ?>">
26
+ <?= $hideItems ? $labelShow : $labelHide ?>
27
+ </a>
28
+ </span>
29
+ <?php } ?>
30
+ </div>
31
+ <?php } ?>
32
+ <div class="lwptoc_items"<?= $itemsStyles ? ' style="' . implode('', $itemsStyles) . '"' : '' ?>>
33
+ <?php lwptoc_items($items) ?>
34
+ </div>
35
+ </div>
36
+ <?= '</div>' ?>
front/views/items.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var $items array
4
+ * @var $depth int
5
+ */
6
+ ?>
7
+ <ul>
8
+ <?php foreach ($items as $item) { ?>
9
+ <li>
10
+ <a href="#<?= $item['id'] ?>" class="lwptoc_item">
11
+ <?php if ($item['number']) { ?>
12
+ <span class="lwptoc_item_number"><?= $item['number'] ?></span>
13
+ <?php } ?>
14
+ <span class="lwptoc_item_label"><?= $item['label'] ?></span>
15
+ </a>
16
+ <?php lwptoc_items($item['childrens']) ?>
17
+ </li>
18
+ <?php } ?>
19
+ </ul>
functions.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ use luckywp\tableOfContents\core\Core;
4
+
5
+ /**
6
+ * @param array $items
7
+ * @param bool $echo
8
+ * @return string|null
9
+ */
10
+ function lwptoc_items($items, $echo = true)
11
+ {
12
+ $html = '';
13
+ if ($items) {
14
+ Core::$plugin->shortcode->currentOutputDepth++;
15
+ $html = Core::$plugin->front->render('items', [
16
+ 'items' => $items,
17
+ 'depth' => Core::$plugin->shortcode->currentOutputDepth,
18
+ ]);
19
+ Core::$plugin->shortcode->currentOutputDepth--;
20
+ }
21
+ if ($echo) {
22
+ echo $html;
23
+ return null;
24
+ }
25
+ return $html;
26
+ }
languages/lwptoc-ru_RU.mo ADDED
Binary file
languages/lwptoc.pot ADDED
@@ -0,0 +1,468 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: \n"
4
+ "POT-Creation-Date: 2018-11-14 12:23+0300\n"
5
+ "PO-Revision-Date: 2018-11-14 12:24+0300\n"
6
+ "Last-Translator: \n"
7
+ "Language-Team: \n"
8
+ "Language: ru_RU\n"
9
+ "MIME-Version: 1.0\n"
10
+ "Content-Type: text/plain; charset=UTF-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "X-Generator: Poedit 2.1.1\n"
13
+ "X-Poedit-Basepath: ..\n"
14
+ "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
15
+ "%10<=4 && (n%100<12 || n%100>14) ? 1 : 2);\n"
16
+ "X-Poedit-KeywordsList: __;_;esc_html__\n"
17
+ "X-Poedit-SearchPath-0: .\n"
18
+ "X-Poedit-SearchPathExcluded-0: plugin/editorBlock/src\n"
19
+
20
+ #: admin/Admin.php:32
21
+ msgid "Settings"
22
+ msgstr ""
23
+
24
+ #: admin/Admin.php:47 admin/Admin.php:48 admin/Admin.php:72 admin/Admin.php:98
25
+ #: admin/controllers/EditorBlockController.php:65
26
+ #: admin/controllers/ShortcodeController.php:68
27
+ msgid "Table of Contents"
28
+ msgstr ""
29
+
30
+ #: admin/Admin.php:99
31
+ msgid "Edit"
32
+ msgstr ""
33
+
34
+ #: admin/Admin.php:126 admin/forms/CustomizeForm.php:207
35
+ #: admin/widgets/customizeModal/views/modal.php:47 config/settings.php:282
36
+ msgid "Position"
37
+ msgstr ""
38
+
39
+ #: admin/Admin.php:129 admin/widgets/customizeModal/views/modal.php:70
40
+ #: config/settings.php:19
41
+ msgid "Minimal Count of Headers"
42
+ msgstr ""
43
+
44
+ #: admin/Admin.php:132 admin/forms/CustomizeForm.php:203
45
+ #: admin/widgets/customizeModal/views/modal.php:93 config/settings.php:31
46
+ msgid "Depth"
47
+ msgstr ""
48
+
49
+ #: admin/Admin.php:135 admin/widgets/customizeModal/views/modal.php:115
50
+ #: config/settings.php:40
51
+ msgid "Hierarchical View"
52
+ msgstr ""
53
+
54
+ #: admin/Admin.php:135 admin/Admin.php:144 admin/Admin.php:153
55
+ #: admin/Admin.php:156 admin/widgets/customizeModal/views/modal.php:131
56
+ #: admin/widgets/customizeModal/views/modal.php:202
57
+ #: admin/widgets/customizeModal/views/modal.php:265
58
+ #: admin/widgets/customizeModal/views/modal.php:288
59
+ msgid "Enabled"
60
+ msgstr ""
61
+
62
+ #: admin/Admin.php:135 admin/Admin.php:144 admin/Admin.php:153
63
+ #: admin/Admin.php:156 admin/widgets/customizeModal/views/modal.php:131
64
+ #: admin/widgets/customizeModal/views/modal.php:202
65
+ #: admin/widgets/customizeModal/views/modal.php:265
66
+ #: admin/widgets/customizeModal/views/modal.php:288
67
+ msgid "Disabled"
68
+ msgstr ""
69
+
70
+ #: admin/Admin.php:138 admin/forms/CustomizeForm.php:204
71
+ #: admin/widgets/customizeModal/views/modal.php:138 config/settings.php:51
72
+ msgid "Numeration"
73
+ msgstr ""
74
+
75
+ #: admin/Admin.php:141 admin/widgets/customizeModal/views/modal.php:160
76
+ #: config/settings.php:66
77
+ msgid "Title"
78
+ msgstr ""
79
+
80
+ #: admin/Admin.php:144 admin/widgets/customizeModal/views/modal.php:186
81
+ #: config/settings.php:72
82
+ msgid "Toggle Show/Hide"
83
+ msgstr ""
84
+
85
+ #: admin/Admin.php:147 admin/forms/CustomizeForm.php:205
86
+ #: admin/widgets/customizeModal/views/modal.php:209 config/settings.php:84
87
+ msgid "Label Show"
88
+ msgstr ""
89
+
90
+ #: admin/Admin.php:150 admin/forms/CustomizeForm.php:206
91
+ #: admin/widgets/customizeModal/views/modal.php:229 config/settings.php:95
92
+ msgid "Label Hide"
93
+ msgstr ""
94
+
95
+ #: admin/Admin.php:153
96
+ msgid "Hide Items"
97
+ msgstr ""
98
+
99
+ #: admin/Admin.php:156 admin/widgets/customizeModal/views/modal.php:272
100
+ #: config/settings.php:123
101
+ msgid "Smooth Scroll"
102
+ msgstr ""
103
+
104
+ #: admin/Admin.php:159 admin/widgets/customizeModal/views/modal.php:295
105
+ msgid "Smooth Scroll Offset Top"
106
+ msgstr ""
107
+
108
+ #: admin/Admin.php:162 admin/widgets/customizeModal/views/modal.php:319
109
+ #: config/settings.php:159
110
+ msgid "Width"
111
+ msgstr ""
112
+
113
+ #: admin/Admin.php:165 admin/forms/CustomizeForm.php:208
114
+ #: admin/widgets/customizeModal/views/modal.php:342 config/settings.php:171
115
+ msgid "Float"
116
+ msgstr ""
117
+
118
+ #: admin/Admin.php:168 admin/widgets/customizeModal/views/modal.php:364
119
+ #: config/settings.php:180
120
+ msgid "Title Font Size"
121
+ msgstr ""
122
+
123
+ #: admin/Admin.php:171 admin/forms/CustomizeForm.php:209
124
+ #: admin/widgets/customizeModal/views/modal.php:388 config/settings.php:193
125
+ msgid "Title Font Weight"
126
+ msgstr ""
127
+
128
+ #: admin/Admin.php:174 admin/widgets/customizeModal/views/modal.php:410
129
+ #: config/settings.php:202
130
+ msgid "Items Font Size"
131
+ msgstr ""
132
+
133
+ #: admin/Admin.php:177 admin/forms/CustomizeForm.php:210
134
+ #: admin/widgets/customizeModal/views/modal.php:434 config/settings.php:215
135
+ msgid "Color Scheme"
136
+ msgstr ""
137
+
138
+ #: admin/controllers/EditorBlockController.php:71
139
+ #: admin/controllers/ShortcodeController.php:74
140
+ #: admin/widgets/metabox/views/box.php:26
141
+ msgid "empty"
142
+ msgstr ""
143
+
144
+ #: admin/views/settings/index.php:7
145
+ msgid "Table of Contents Settings"
146
+ msgstr ""
147
+
148
+ #: admin/widgets/OverrideColorBadge.php:17
149
+ msgid "from scheme"
150
+ msgstr ""
151
+
152
+ #: admin/widgets/customizeModal/views/modal.php:23
153
+ #: admin/widgets/customizeModal/views/modal.php:487
154
+ msgid "Cancel"
155
+ msgstr ""
156
+
157
+ #: admin/widgets/customizeModal/views/modal.php:24
158
+ msgid "Customize Table of Contents"
159
+ msgstr ""
160
+
161
+ #: admin/widgets/customizeModal/views/modal.php:38 config/settings.php:13
162
+ msgid "General"
163
+ msgstr ""
164
+
165
+ #: admin/widgets/customizeModal/views/modal.php:39 config/settings.php:153
166
+ msgid "Appearance"
167
+ msgstr ""
168
+
169
+ #: admin/widgets/customizeModal/views/modal.php:48
170
+ #: admin/widgets/customizeModal/views/modal.php:71
171
+ #: admin/widgets/customizeModal/views/modal.php:94
172
+ #: admin/widgets/customizeModal/views/modal.php:116
173
+ #: admin/widgets/customizeModal/views/modal.php:139
174
+ #: admin/widgets/customizeModal/views/modal.php:161
175
+ #: admin/widgets/customizeModal/views/modal.php:187
176
+ #: admin/widgets/customizeModal/views/modal.php:210
177
+ #: admin/widgets/customizeModal/views/modal.php:230
178
+ #: admin/widgets/customizeModal/views/modal.php:250
179
+ #: admin/widgets/customizeModal/views/modal.php:273
180
+ #: admin/widgets/customizeModal/views/modal.php:296
181
+ #: admin/widgets/customizeModal/views/modal.php:320
182
+ #: admin/widgets/customizeModal/views/modal.php:343
183
+ #: admin/widgets/customizeModal/views/modal.php:365
184
+ #: admin/widgets/customizeModal/views/modal.php:389
185
+ #: admin/widgets/customizeModal/views/modal.php:411
186
+ #: admin/widgets/customizeModal/views/modal.php:435
187
+ #: admin/widgets/customizeModal/views/modal.php:465
188
+ msgid "default"
189
+ msgstr ""
190
+
191
+ #: admin/widgets/customizeModal/views/modal.php:51
192
+ #: admin/widgets/customizeModal/views/modal.php:74
193
+ #: admin/widgets/customizeModal/views/modal.php:97
194
+ #: admin/widgets/customizeModal/views/modal.php:119
195
+ #: admin/widgets/customizeModal/views/modal.php:142
196
+ #: admin/widgets/customizeModal/views/modal.php:164
197
+ #: admin/widgets/customizeModal/views/modal.php:190
198
+ #: admin/widgets/customizeModal/views/modal.php:213
199
+ #: admin/widgets/customizeModal/views/modal.php:233
200
+ #: admin/widgets/customizeModal/views/modal.php:253
201
+ #: admin/widgets/customizeModal/views/modal.php:276
202
+ #: admin/widgets/customizeModal/views/modal.php:299
203
+ #: admin/widgets/customizeModal/views/modal.php:323
204
+ #: admin/widgets/customizeModal/views/modal.php:346
205
+ #: admin/widgets/customizeModal/views/modal.php:368
206
+ #: admin/widgets/customizeModal/views/modal.php:392
207
+ #: admin/widgets/customizeModal/views/modal.php:414
208
+ #: admin/widgets/customizeModal/views/modal.php:438
209
+ #: admin/widgets/customizeModal/views/modal.php:468
210
+ msgid "Click for override default value"
211
+ msgstr ""
212
+
213
+ #: admin/widgets/customizeModal/views/modal.php:83 config/settings.php:27
214
+ msgid ""
215
+ "If the count of headers in the post is less, then table of contents is not "
216
+ "displayed."
217
+ msgstr ""
218
+
219
+ #: admin/widgets/customizeModal/views/modal.php:124
220
+ #: admin/widgets/customizeModal/views/modal.php:195
221
+ #: admin/widgets/customizeModal/views/modal.php:258
222
+ #: admin/widgets/customizeModal/views/modal.php:281
223
+ #: admin/widgets/metabox/views/box.php:45 config/settings.php:44
224
+ #: config/settings.php:76 config/settings.php:127 config/settings.php:274
225
+ msgid "Enable"
226
+ msgstr ""
227
+
228
+ #: admin/widgets/customizeModal/views/modal.php:175
229
+ msgid "Without title"
230
+ msgstr ""
231
+
232
+ #: admin/widgets/customizeModal/views/modal.php:249 config/settings.php:110
233
+ msgid "By default, items of contents will be hidden"
234
+ msgstr ""
235
+
236
+ #: admin/widgets/customizeModal/views/modal.php:490
237
+ msgid "Save"
238
+ msgstr ""
239
+
240
+ #: admin/widgets/customizeSuccess/views/widget.php:9
241
+ msgid "Saved!"
242
+ msgstr ""
243
+
244
+ #: admin/widgets/fontSizeField/views/widget.php:16 plugin/Plugin.php:226
245
+ msgid "Default"
246
+ msgstr ""
247
+
248
+ #: admin/widgets/fontSizeField/views/widget.php:17 plugin/Plugin.php:191
249
+ msgid "Custom Value"
250
+ msgstr ""
251
+
252
+ #: admin/widgets/metabox/views/box.php:14
253
+ msgid "The table of contents will be automatic added to this post."
254
+ msgstr ""
255
+
256
+ #: admin/widgets/metabox/views/box.php:20
257
+ msgid "Overridden settings:"
258
+ msgstr ""
259
+
260
+ #: admin/widgets/metabox/views/box.php:32
261
+ msgid "Customize"
262
+ msgstr ""
263
+
264
+ #: admin/widgets/metabox/views/box.php:35
265
+ msgid "Disable"
266
+ msgstr ""
267
+
268
+ #: admin/widgets/metabox/views/box.php:42
269
+ msgid "Click \"Enable\" for automatic add table of contents to this post."
270
+ msgstr ""
271
+
272
+ #: config/settings.php:62
273
+ msgid "Header"
274
+ msgstr ""
275
+
276
+ #: config/settings.php:68 plugin/Settings.php:85
277
+ msgid "Contents"
278
+ msgstr ""
279
+
280
+ #: config/settings.php:91 plugin/Settings.php:101
281
+ msgid "show"
282
+ msgstr ""
283
+
284
+ #: config/settings.php:102 plugin/Settings.php:109
285
+ msgid "hide"
286
+ msgstr ""
287
+
288
+ #: config/settings.php:119
289
+ msgid "Behavior"
290
+ msgstr ""
291
+
292
+ #: config/settings.php:135
293
+ msgid "Scroll Offset Top"
294
+ msgstr ""
295
+
296
+ #: config/settings.php:225
297
+ msgid "Override Color Scheme Colors"
298
+ msgstr ""
299
+
300
+ #: config/settings.php:230
301
+ msgid "Background Color"
302
+ msgstr ""
303
+
304
+ #: config/settings.php:235
305
+ msgid "Border Color"
306
+ msgstr ""
307
+
308
+ #: config/settings.php:240
309
+ msgid "Title Color"
310
+ msgstr ""
311
+
312
+ #: config/settings.php:245
313
+ msgid "Link Color"
314
+ msgstr ""
315
+
316
+ #: config/settings.php:250
317
+ msgid "Hover Link Color"
318
+ msgstr ""
319
+
320
+ #: config/settings.php:255
321
+ msgid "Visited Link Color"
322
+ msgstr ""
323
+
324
+ #: config/settings.php:264
325
+ msgid "Auto Insert"
326
+ msgstr ""
327
+
328
+ #: config/settings.php:270
329
+ msgid "Auto Insert Table of Contents"
330
+ msgstr ""
331
+
332
+ #: config/settings.php:294
333
+ msgid "Post Types"
334
+ msgstr ""
335
+
336
+ #: core/admin/AdminController.php:46
337
+ msgid "Sorry, you are not allowed to access this page."
338
+ msgstr ""
339
+
340
+ #: core/validators/BooleanValidator.php:27
341
+ msgid "{attribute} must be either \"{true}\" or \"{false}\"."
342
+ msgstr ""
343
+
344
+ #: core/validators/RangeValidator.php:27
345
+ msgid "{attribute} is invalid."
346
+ msgstr ""
347
+
348
+ #: core/validators/RequiredValidator.php:16
349
+ msgid "{attribute} cannot be blank."
350
+ msgstr ""
351
+
352
+ #: plugin/Plugin.php:74
353
+ msgid "Without numeration"
354
+ msgstr ""
355
+
356
+ #: plugin/Plugin.php:75
357
+ msgid "Decimal numbers (nested)"
358
+ msgstr ""
359
+
360
+ #: plugin/Plugin.php:76
361
+ msgid "Decimal numbers"
362
+ msgstr ""
363
+
364
+ #: plugin/Plugin.php:77
365
+ msgid "Roman numbers (nested)"
366
+ msgstr ""
367
+
368
+ #: plugin/Plugin.php:78
369
+ msgid "Roman numbers"
370
+ msgstr ""
371
+
372
+ #: plugin/Plugin.php:88
373
+ msgid "Before first heading"
374
+ msgstr ""
375
+
376
+ #: plugin/Plugin.php:89
377
+ msgid "After first heading"
378
+ msgstr ""
379
+
380
+ #: plugin/Plugin.php:90
381
+ msgid "After first block (paragraph, list or heading)"
382
+ msgstr ""
383
+
384
+ #: plugin/Plugin.php:91
385
+ msgid "Top"
386
+ msgstr ""
387
+
388
+ #: plugin/Plugin.php:92
389
+ msgid "Bottom"
390
+ msgstr ""
391
+
392
+ #: plugin/Plugin.php:126
393
+ msgid "Thin"
394
+ msgstr ""
395
+
396
+ #: plugin/Plugin.php:127
397
+ msgid "Extra Light"
398
+ msgstr ""
399
+
400
+ #: plugin/Plugin.php:128
401
+ msgid "Light"
402
+ msgstr ""
403
+
404
+ #: plugin/Plugin.php:129
405
+ msgid "Normal"
406
+ msgstr ""
407
+
408
+ #: plugin/Plugin.php:130
409
+ msgid "Medium"
410
+ msgstr ""
411
+
412
+ #: plugin/Plugin.php:131
413
+ msgid "Semi Bold"
414
+ msgstr ""
415
+
416
+ #: plugin/Plugin.php:132
417
+ msgid "Bold"
418
+ msgstr ""
419
+
420
+ #: plugin/Plugin.php:133
421
+ msgid "Extra Bold"
422
+ msgstr ""
423
+
424
+ #: plugin/Plugin.php:134
425
+ msgid "Heavy"
426
+ msgstr ""
427
+
428
+ #: plugin/Plugin.php:163
429
+ msgid "None"
430
+ msgstr ""
431
+
432
+ #: plugin/Plugin.php:164
433
+ msgid "Left"
434
+ msgstr ""
435
+
436
+ #: plugin/Plugin.php:165
437
+ msgid "Right"
438
+ msgstr ""
439
+
440
+ #: plugin/Plugin.php:175
441
+ msgid "Light Colors"
442
+ msgstr ""
443
+
444
+ #: plugin/Plugin.php:176
445
+ msgid "Dark Colors"
446
+ msgstr ""
447
+
448
+ #: plugin/Plugin.php:177
449
+ msgid "White"
450
+ msgstr ""
451
+
452
+ #: plugin/Plugin.php:178
453
+ msgid "Transparent"
454
+ msgstr ""
455
+
456
+ #: plugin/Plugin.php:189
457
+ msgid "Auto"
458
+ msgstr ""
459
+
460
+ #: plugin/Plugin.php:190
461
+ msgid "Full Width"
462
+ msgstr ""
463
+
464
+ #: plugin/Plugin.php:231
465
+ msgid ""
466
+ "Creates a table of contents for your posts/pages. Works automatically or "
467
+ "manually (via shortcode or Gutenberg block)."
468
+ msgstr ""
luckywp-table-of-contents.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: LuckyWP Table of Contents
4
+ Plugin URI: https://theluckywp.com/product/table-of-contents/
5
+ Description: Creates a table of contents for your posts/pages. Works automatically or manually (via shortcode or Gutenberg block).
6
+ Version: 1.0.0
7
+ Author: LuckyWP
8
+ Author URI: https://theluckywp.com/
9
+ Text Domain: lwptoc
10
+ Domain Path: /languages
11
+
12
+ LuckyWP Table of Contents is free software: you can redistribute it and/or modify
13
+ it under the terms of the GNU General Public License as published by
14
+ the Free Software Foundation, either version 2 of the License, or
15
+ any later version.
16
+
17
+ LuckyWP Table of Contents is distributed in the hope that it will be useful,
18
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ GNU General Public License for more details.
21
+
22
+ You should have received a copy of the GNU General Public License
23
+ along with LuckyWP Table of Contents. If not, see https://www.gnu.org/licenses/gpl-2.0.html.
24
+ */
25
+
26
+ require 'lwptocAutoloader.php';
27
+ $lwptocAutoloader = new lwptocAutoloader();
28
+ $lwptocAutoloader->register();
29
+ $lwptocAutoloader->addNamespace('luckywp\tableOfContents', __DIR__);
30
+
31
+ $config = require(__DIR__ . '/config/plugin.php');
32
+ (new \luckywp\tableOfContents\plugin\Plugin($config))->run('1.0.0', __FILE__, 'lwptoc_');
33
+
34
+ require_once __DIR__ . '/functions.php';
lwptocAutoloader.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class lwptocAutoloader
4
+ {
5
+
6
+ /**
7
+ * @var array
8
+ */
9
+ protected $prefixes = [];
10
+
11
+ /**
12
+ * @param string $prefix
13
+ * @param string $baseDir
14
+ * @param bool $prepend
15
+ * @return void
16
+ */
17
+ public function addNamespace($prefix, $baseDir, $prepend = false)
18
+ {
19
+ $prefix = trim($prefix, '\\') . '\\';
20
+ $baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . '/';
21
+
22
+ if (isset($this->prefixes[$prefix]) === false) {
23
+ $this->prefixes[$prefix] = [];
24
+ }
25
+
26
+ if ($prepend) {
27
+ array_unshift($this->prefixes[$prefix], $baseDir);
28
+ } else {
29
+ array_push($this->prefixes[$prefix], $baseDir);
30
+ }
31
+ }
32
+
33
+ /**
34
+ * @return void
35
+ */
36
+ public function register()
37
+ {
38
+ spl_autoload_register([$this, 'loadClass']);
39
+ }
40
+
41
+ /**
42
+ * @param string $class
43
+ * @return mixed
44
+ */
45
+ public function loadClass($class)
46
+ {
47
+ $prefix = $class;
48
+ while (false !== $pos = strrpos($prefix, '\\')) {
49
+ $prefix = substr($class, 0, $pos + 1);
50
+ $relativeClass = substr($class, $pos + 1);
51
+
52
+ $mappedFile = $this->loadMappedFile($prefix, $relativeClass);
53
+ if ($mappedFile) {
54
+ return $mappedFile;
55
+ }
56
+
57
+ $prefix = rtrim($prefix, '\\');
58
+ }
59
+ return false;
60
+ }
61
+
62
+ /**
63
+ * @param string $prefix
64
+ * @param string $relativeClass
65
+ * @return mixed Boolean
66
+ */
67
+ protected function loadMappedFile($prefix, $relativeClass)
68
+ {
69
+ if (isset($this->prefixes[$prefix]) === false) {
70
+ return false;
71
+ }
72
+ foreach ($this->prefixes[$prefix] as $baseDir) {
73
+ $file = $baseDir
74
+ . str_replace('\\', '/', $relativeClass)
75
+ . '.php';
76
+ if ($this->requireFile($file)) {
77
+ return $file;
78
+ }
79
+ }
80
+ return false;
81
+ }
82
+
83
+ /**
84
+ * @param string $file
85
+ * @return bool True
86
+ */
87
+ protected function requireFile($file)
88
+ {
89
+ if (file_exists($file)) {
90
+ require $file;
91
+ return true;
92
+ }
93
+ return false;
94
+ }
95
+ }
plugin/Activation.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\plugin;
4
+
5
+ use luckywp\tableOfContents\core\base\BaseObject;
6
+ use luckywp\tableOfContents\core\Core;
7
+
8
+ class Activation extends BaseObject
9
+ {
10
+
11
+ public function init()
12
+ {
13
+ register_activation_hook(Core::$plugin->fileName, [$this, 'activate']);
14
+ }
15
+
16
+ public function activate()
17
+ {
18
+ Core::$plugin->settings->install();
19
+ }
20
+ }
plugin/Plugin.php ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\plugin;
4
+
5
+ use luckywp\tableOfContents\admin\Admin;
6
+ use luckywp\tableOfContents\core\base\BasePlugin;
7
+ use luckywp\tableOfContents\core\base\Request;
8
+ use luckywp\tableOfContents\core\base\View;
9
+ use luckywp\tableOfContents\core\helpers\ArrayHelper;
10
+ use luckywp\tableOfContents\front\Front;
11
+ use luckywp\tableOfContents\plugin\editorBlock\EditorBlock;
12
+ use luckywp\tableOfContents\plugin\mcePlugin\McePlugin;
13
+ use WP_Post_Type;
14
+
15
+ /**
16
+ * @property Admin $admin
17
+ * @property Front $front
18
+ * @property Request $request
19
+ * @property Settings $settings
20
+ * @property EditorBlock $editorBlock
21
+ * @property McePlugin $mcePlugin
22
+ * @property Shortcode $shortcode
23
+ * @property View $view
24
+ *
25
+ * @property WP_Post_Type[] $postTypes
26
+ * @property array $depthsList
27
+ * @property array $numerationsList
28
+ * @property array $positionsList
29
+ * @property array $blockSizeUnitsList
30
+ * @property array $fontSizeUnitsList
31
+ * @property array $fontWeightsList
32
+ * @property array $floatsList
33
+ * @property array $colorSchemesList
34
+ */
35
+ class Plugin extends BasePlugin
36
+ {
37
+
38
+ private $_postTypes;
39
+
40
+ /**
41
+ * @return WP_Post_Type[]
42
+ */
43
+ public function getPostTypes()
44
+ {
45
+ if ($this->_postTypes === null) {
46
+ $this->_postTypes = get_post_types([
47
+ 'public' => true,
48
+ ], 'objects');
49
+ }
50
+ return $this->_postTypes;
51
+ }
52
+
53
+ /**
54
+ * @return array
55
+ */
56
+ public function getDepthsList()
57
+ {
58
+ return [
59
+ 1 => '1',
60
+ 2 => '2',
61
+ 3 => '3',
62
+ 4 => '4',
63
+ 5 => '5',
64
+ 6 => '6',
65
+ ];
66
+ }
67
+
68
+ /**
69
+ * @return array
70
+ */
71
+ public function getNumerationsList()
72
+ {
73
+ return [
74
+ 'none' => esc_html__('Without numeration', 'lwptoc'),
75
+ 'decimalnested' => esc_html__('Decimal numbers (nested)', 'lwptoc'),
76
+ 'decimal' => esc_html__('Decimal numbers', 'lwptoc'),
77
+ 'romannested' => esc_html__('Roman numbers (nested)', 'lwptoc'),
78
+ 'roman' => esc_html__('Roman numbers', 'lwptoc'),
79
+ ];
80
+ }
81
+
82
+ /**
83
+ * @return array
84
+ */
85
+ public function getPositionsList()
86
+ {
87
+ return [
88
+ 'beforefirstheading' => esc_html__('Before first heading', 'lwptoc'),
89
+ 'afterfirstheading' => esc_html__('After first heading', 'lwptoc'),
90
+ 'afterfirstblock' => esc_html__('After first block (paragraph, list or heading)', 'lwptoc'),
91
+ 'top' => esc_html__('Top', 'lwptoc'),
92
+ 'bottom' => esc_html__('Bottom', 'lwptoc'),
93
+ ];
94
+ }
95
+
96
+ /**
97
+ * @return array
98
+ */
99
+ public function getBlockSizeUnitsList()
100
+ {
101
+ return [
102
+ 'px' => 'px',
103
+ '%' => '%',
104
+ ];
105
+ }
106
+
107
+ /**
108
+ * @return array
109
+ */
110
+ public function getFontSizeUnitsList()
111
+ {
112
+ return [
113
+ '%' => '%',
114
+ 'em' => 'em',
115
+ 'pt' => 'pt',
116
+ 'px' => 'px',
117
+ ];
118
+ }
119
+
120
+ /**
121
+ * @return array
122
+ */
123
+ public function getFontWeightsList()
124
+ {
125
+ return [
126
+ 'thin' => esc_html__('Thin', 'lwptoc'),
127
+ 'extralight' => esc_html__('Extra Light', 'lwptoc'),
128
+ 'light' => esc_html__('Light', 'lwptoc'),
129
+ 'normal' => esc_html__('Normal', 'lwptoc'),
130
+ 'medium' => esc_html__('Medium', 'lwptoc'),
131
+ 'semibold' => esc_html__('Semi Bold', 'lwptoc'),
132
+ 'bold' => esc_html__('Bold', 'lwptoc'),
133
+ 'extrabold' => esc_html__('Extra Bold', 'lwptoc'),
134
+ 'heavy' => esc_html__('Heavy', 'lwptoc'),
135
+ ];
136
+ }
137
+
138
+ /**
139
+ * @param string $id
140
+ * @return string|null
141
+ */
142
+ public function fontWeightToValue($id)
143
+ {
144
+ return ArrayHelper::getValue([
145
+ 'thin' => '100',
146
+ 'extralight' => '200',
147
+ 'light' => '300',
148
+ 'normal' => 'normal',
149
+ 'medium' => '500',
150
+ 'semibold' => '600',
151
+ 'bold' => 'bold',
152
+ 'extrabold' => '800',
153
+ 'heavy' => '900',
154
+ ], $id);
155
+ }
156
+
157
+ /**
158
+ * @return array
159
+ */
160
+ public function getFloatsList()
161
+ {
162
+ return [
163
+ 'none' => esc_html__('None', 'lwptoc'),
164
+ 'left' => esc_html__('Left', 'lwptoc'),
165
+ 'right' => esc_html__('Right', 'lwptoc'),
166
+ ];
167
+ }
168
+
169
+ /**
170
+ * @return array
171
+ */
172
+ public function getColorSchemesList()
173
+ {
174
+ return [
175
+ 'light' => esc_html__('Light Colors', 'lwptoc'),
176
+ 'dark' => esc_html__('Dark Colors', 'lwptoc'),
177
+ 'white' => esc_html__('White', 'lwptoc'),
178
+ 'transparent' => esc_html__('Transparent', 'lwptoc'),
179
+ ];
180
+ }
181
+
182
+ /**
183
+ * @param bool $withCustom
184
+ * @return array
185
+ */
186
+ public function getWidthsList($withCustom = true)
187
+ {
188
+ $widths = [
189
+ 'auto' => esc_html__('Auto', 'lwptoc'),
190
+ 'full' => esc_html__('Full Width', 'lwptoc'),
191
+ 'custom' => esc_html__('Custom Value', 'lwptoc'),
192
+ ];
193
+ if (!$withCustom) {
194
+ unset($widths['custom']);
195
+ }
196
+ return $widths;
197
+ }
198
+
199
+ /**
200
+ * @param string $width
201
+ * @return bool
202
+ */
203
+ public function isCustomWidth($width)
204
+ {
205
+ return !array_key_exists($width, $this->getWidthsList(false));
206
+ }
207
+
208
+ /**
209
+ * @param string $width
210
+ * @return string
211
+ */
212
+ public function widthToLabel($width)
213
+ {
214
+ if ($this->isCustomWidth($width)) {
215
+ return $width;
216
+ }
217
+ return $this->getWidthsList()[$width];
218
+ }
219
+
220
+ /**
221
+ * @param string $fontSize
222
+ * @return string
223
+ */
224
+ public function fontSizeToLabel($fontSize)
225
+ {
226
+ return $fontSize == 'default' ? esc_html__('Default', 'lwptoc') : $fontSize;
227
+ }
228
+
229
+ private function pluginI18n()
230
+ {
231
+ __('Creates a table of contents for your posts/pages. Works automatically or manually (via shortcode or Gutenberg block).', 'lwptdr');
232
+ }
233
+ }
plugin/PostSettings.php ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\plugin;
4
+
5
+ use luckywp\tableOfContents\core\base\BaseObject;
6
+ use luckywp\tableOfContents\core\Core;
7
+ use luckywp\tableOfContents\core\helpers\ArrayHelper;
8
+
9
+ class PostSettings extends BaseObject
10
+ {
11
+
12
+ /**
13
+ * @var bool
14
+ */
15
+ public $enabled = true;
16
+
17
+ /**
18
+ * @var int|null
19
+ */
20
+ public $min;
21
+
22
+ /**
23
+ * @var int|null
24
+ */
25
+ public $depth;
26
+
27
+ /**
28
+ * @var bool|null
29
+ */
30
+ public $hierarchical;
31
+
32
+ /**
33
+ * @var string|null
34
+ */
35
+ public $numeration;
36
+
37
+ /**
38
+ * @var string|null
39
+ */
40
+ public $title;
41
+
42
+ /**
43
+ * @var bool|null
44
+ */
45
+ public $toggle;
46
+
47
+ /**
48
+ * @var string|null
49
+ */
50
+ public $labelShow;
51
+
52
+ /**
53
+ * @var string|null
54
+ */
55
+ public $labelHide;
56
+
57
+ /**
58
+ * @var bool|null
59
+ */
60
+ public $hideItems;
61
+
62
+ /**
63
+ * @var bool|null
64
+ */
65
+ public $smoothScroll;
66
+
67
+ /**
68
+ * @var int|null
69
+ */
70
+ public $smoothScrollOffset;
71
+
72
+ /**
73
+ * @var string|null
74
+ */
75
+ public $position;
76
+
77
+ /**
78
+ * @var string|null
79
+ */
80
+ public $width;
81
+
82
+ /**
83
+ * @var string|null
84
+ */
85
+ public $float;
86
+
87
+ /**
88
+ * @var string|null
89
+ */
90
+ public $titleFontSize;
91
+
92
+ /**
93
+ * @var string|null
94
+ */
95
+ public $titleFontWeight;
96
+
97
+ /**
98
+ * @var string|null
99
+ */
100
+ public $itemsFontSize;
101
+
102
+ /**
103
+ * @var string|null
104
+ */
105
+ public $colorScheme;
106
+
107
+ /**
108
+ * @var string|null
109
+ */
110
+ public $backgroundColor;
111
+
112
+ /**
113
+ * @var string|null
114
+ */
115
+ public $borderColor;
116
+
117
+ /**
118
+ * @var string|null
119
+ */
120
+ public $titleColor;
121
+
122
+ /**
123
+ * @var string|null
124
+ */
125
+ public $linkColor;
126
+
127
+ /**
128
+ * @var string|null
129
+ */
130
+ public $hoverLinkColor;
131
+
132
+ /**
133
+ * @var string|null
134
+ */
135
+ public $visitedLinkColor;
136
+
137
+ /**
138
+ * @var int
139
+ */
140
+ protected $postId;
141
+
142
+ /**
143
+ * @param int $postId
144
+ */
145
+ public function __construct($postId)
146
+ {
147
+ parent::__construct();
148
+ if (in_array(get_post_type($postId), Core::$plugin->admin->getMetaboxPostTypes())) {
149
+ $this->postId = $postId;
150
+ $data = get_post_meta($postId, '_lwptoc_settings', true);
151
+ if ($data && is_array($data)) {
152
+ $this->enabled = ArrayHelper::getValue($data, 'enabled', true);
153
+ $this->min = ArrayHelper::getValue($data, 'min');
154
+ $this->depth = ArrayHelper::getValue($data, 'depth');
155
+ $this->hierarchical = ArrayHelper::getValue($data, 'hierarchical');
156
+ $this->numeration = ArrayHelper::getValue($data, 'numeration');
157
+ $this->title = ArrayHelper::getValue($data, 'title');
158
+ $this->toggle = ArrayHelper::getValue($data, 'toggle');
159
+ $this->labelShow = ArrayHelper::getValue($data, 'labelShow');
160
+ $this->labelHide = ArrayHelper::getValue($data, 'labelHide');
161
+ $this->hideItems = ArrayHelper::getValue($data, 'hideItems');
162
+ $this->smoothScroll = ArrayHelper::getValue($data, 'smoothScroll');
163
+ $this->smoothScrollOffset = ArrayHelper::getValue($data, 'smoothScrollOffset');
164
+ $this->position = ArrayHelper::getValue($data, 'position');
165
+ $this->width = ArrayHelper::getValue($data, 'width');
166
+ $this->float = ArrayHelper::getValue($data, 'float');
167
+ $this->titleFontSize = ArrayHelper::getValue($data, 'titleFontSize');
168
+ $this->titleFontWeight = ArrayHelper::getValue($data, 'titleFontWeight');
169
+ $this->itemsFontSize = ArrayHelper::getValue($data, 'itemsFontSize');
170
+ $this->colorScheme = ArrayHelper::getValue($data, 'colorScheme');
171
+ $this->backgroundColor = ArrayHelper::getValue($data, 'backgroundColor');
172
+ $this->borderColor = ArrayHelper::getValue($data, 'borderColor');
173
+ $this->titleColor = ArrayHelper::getValue($data, 'titleColor');
174
+ $this->linkColor = ArrayHelper::getValue($data, 'linkColor');
175
+ $this->hoverLinkColor = ArrayHelper::getValue($data, 'hoverLinkColor');
176
+ $this->visitedLinkColor = ArrayHelper::getValue($data, 'visitedLinkColor');
177
+ }
178
+ }
179
+ }
180
+
181
+ public function save()
182
+ {
183
+ $data = [];
184
+ if ($this->min !== null) {
185
+ $data['min'] = (int)$this->min;
186
+ }
187
+ if ($this->depth !== null) {
188
+ $data['depth'] = (int)$this->depth;
189
+ }
190
+ if ($this->hierarchical !== null) {
191
+ $data['hierarchical'] = (bool)$this->hierarchical;
192
+ }
193
+ if ($this->numeration !== null) {
194
+ $data['numeration'] = $this->numeration;
195
+ }
196
+ if ($this->title !== null) {
197
+ $data['title'] = wp_slash($this->title);
198
+ }
199
+ if ($this->toggle !== null) {
200
+ $data['toggle'] = (bool)$this->toggle;
201
+ }
202
+ if ($this->labelShow !== null) {
203
+ $data['labelShow'] = wp_slash($this->labelShow);
204
+ }
205
+ if ($this->labelHide !== null) {
206
+ $data['labelHide'] = wp_slash($this->labelHide);
207
+ }
208
+ if ($this->hideItems !== null) {
209
+ $data['hideItems'] = (bool)$this->hideItems;
210
+ }
211
+ if ($this->smoothScroll !== null) {
212
+ $data['smoothScroll'] = (bool)$this->smoothScroll;
213
+ }
214
+ if ($this->smoothScrollOffset !== null) {
215
+ $data['smoothScrollOffset'] = (int)$this->smoothScrollOffset;
216
+ }
217
+ if ($this->position !== null) {
218
+ $data['position'] = $this->position;
219
+ }
220
+ if ($this->width !== null) {
221
+ $data['width'] = $this->width;
222
+ }
223
+ if ($this->float !== null) {
224
+ $data['float'] = $this->float;
225
+ }
226
+ if ($this->titleFontSize !== null) {
227
+ $data['titleFontSize'] = $this->titleFontSize;
228
+ }
229
+ if ($this->titleFontWeight !== null) {
230
+ $data['titleFontWeight'] = $this->titleFontWeight;
231
+ }
232
+ if ($this->itemsFontSize !== null) {
233
+ $data['itemsFontSize'] = $this->itemsFontSize;
234
+ }
235
+ if ($this->colorScheme !== null) {
236
+ $data['colorScheme'] = $this->colorScheme;
237
+ }
238
+ if ($this->backgroundColor !== null) {
239
+ $data['backgroundColor'] = $this->backgroundColor;
240
+ }
241
+ if ($this->borderColor !== null) {
242
+ $data['borderColor'] = $this->borderColor;
243
+ }
244
+ if ($this->titleColor !== null) {
245
+ $data['titleColor'] = $this->titleColor;
246
+ }
247
+ if ($this->linkColor !== null) {
248
+ $data['linkColor'] = $this->linkColor;
249
+ }
250
+ if ($this->hoverLinkColor !== null) {
251
+ $data['hoverLinkColor'] = $this->hoverLinkColor;
252
+ }
253
+ if ($this->visitedLinkColor !== null) {
254
+ $data['visitedLinkColor'] = $this->visitedLinkColor;
255
+ }
256
+ if ($data || !$this->enabled) {
257
+ $data['enabled'] = $this->enabled;
258
+ update_post_meta($this->postId, '_lwptoc_settings', $data);
259
+ } else {
260
+ delete_post_meta($this->postId, '_lwptoc_settings');
261
+ }
262
+ }
263
+ }
plugin/Settings.php ADDED
@@ -0,0 +1,301 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\plugin;
4
+
5
+ use luckywp\tableOfContents\core\Core;
6
+
7
+ /**
8
+ * @property int $generalMin
9
+ * @property int $generalDepth
10
+ * @property bool $generalHierarchical
11
+ * @property string $generalNumeration
12
+ * @property string $generalTitle
13
+ * @property bool $generalToggle
14
+ * @property string $generalLabelShow
15
+ * @property string $generalLabelHide
16
+ * @property bool $generalHideItems
17
+ * @property bool $generalSmoothScroll
18
+ * @property int $generalSmoothScrollOffset
19
+ *
20
+ * @property string $appearanceWidth
21
+ * @property string $appearanceFloat
22
+ * @property string $appearanceTitleFontSize
23
+ * @property string $appearanceTitleFontWeight
24
+ * @property string $appearanceItemsFontSize
25
+ * @property string $appearanceColorScheme
26
+ * @property string|null $appearanceBackgroundColor
27
+ * @property string|null $appearanceBorderColor
28
+ * @property string|null $appearanceTitleColor
29
+ * @property string|null $appearanceLinkColor
30
+ * @property string|null $appearanceHoverLinkColor
31
+ * @property string|null $appearanceVisitedLinkColor
32
+ *
33
+ * @property bool $autoInsertEnable
34
+ * @property string $autoInsertPosition
35
+ * @property array $autoInsertPostTypes
36
+ */
37
+ class Settings extends \luckywp\tableOfContents\core\wp\Settings
38
+ {
39
+
40
+ /**
41
+ * @return int
42
+ */
43
+ public function getGeneralMin()
44
+ {
45
+ return (int)$this->getValue('general', 'min', 2);
46
+ }
47
+
48
+ /**
49
+ * @return int
50
+ */
51
+ public function getGeneralDepth()
52
+ {
53
+ $depth = (int)$this->getValue('general', 'depth');
54
+ if (!array_key_exists($depth, Core::$plugin->depthsList)) {
55
+ $depth = 6;
56
+ }
57
+ return $depth;
58
+ }
59
+
60
+ /**
61
+ * @return bool
62
+ */
63
+ public function getGeneralHierarchical()
64
+ {
65
+ return (bool)$this->getValue('general', 'hierarchical', true);
66
+ }
67
+
68
+ /**
69
+ * @return string
70
+ */
71
+ public function getGeneralNumeration()
72
+ {
73
+ $numeration = $this->getValue('general', 'numeration');
74
+ if (!array_key_exists($numeration, Core::$plugin->numerationsList)) {
75
+ $numeration = 'decimalnested';
76
+ }
77
+ return $numeration;
78
+ }
79
+
80
+ /**
81
+ * @return string
82
+ */
83
+ public function getGeneralTitle()
84
+ {
85
+ return (string)$this->getValue('general', 'title', __('Contents', 'lwptoc'));
86
+ }
87
+
88
+ /**
89
+ * @return bool
90
+ */
91
+ public function getGeneralToggle()
92
+ {
93
+ return (bool)$this->getValue('general', 'toggle', true);
94
+ }
95
+
96
+ /**
97
+ * @return string
98
+ */
99
+ public function getGeneralLabelShow()
100
+ {
101
+ return (string)$this->getValue('general', 'labelShow', __('show', 'lwptoc'));
102
+ }
103
+
104
+ /**
105
+ * @return string
106
+ */
107
+ public function getGeneralLabelHide()
108
+ {
109
+ return (string)$this->getValue('general', 'labelHide', __('hide', 'lwptoc'));
110
+ }
111
+
112
+ /**
113
+ * @return bool
114
+ */
115
+ public function getGeneralHideItems()
116
+ {
117
+ return (bool)$this->getValue('general', 'hideItems', false);
118
+ }
119
+
120
+ /**
121
+ * @return bool
122
+ */
123
+ public function getGeneralSmoothScroll()
124
+ {
125
+ return (bool)$this->getValue('general', 'smoothScroll', true);
126
+ }
127
+
128
+ /**
129
+ * @return int
130
+ */
131
+ public function getGeneralSmoothScrollOffset()
132
+ {
133
+ return (int)$this->getValue('general', 'smoothScrollOffset', 24);
134
+ }
135
+
136
+ /**
137
+ * @return string
138
+ */
139
+ public function getAppearanceWidth()
140
+ {
141
+ return $this->sanitizeWidth((string)$this->getValue('appearance', 'width', 'auto'));
142
+ }
143
+
144
+ /**
145
+ * @return string
146
+ */
147
+ public function getAppearanceFloat()
148
+ {
149
+ $float = $this->getValue('appearance', 'float');
150
+ if (!array_key_exists($float, Core::$plugin->floatsList)) {
151
+ $float = 'none';
152
+ }
153
+ return $float;
154
+ }
155
+
156
+ /**
157
+ * @return string
158
+ */
159
+ public function getAppearanceTitleFontSize()
160
+ {
161
+ return $this->sanitizeFontSize((string)$this->getValue('appearance', 'titleFontSize', 'default'));
162
+ }
163
+
164
+ /**
165
+ * @return string
166
+ */
167
+ public function getAppearanceTitleFontWeight()
168
+ {
169
+ $weight = $this->getValue('appearance', 'titleFontWeight');
170
+ if (!array_key_exists($weight, Core::$plugin->fontWeightsList)) {
171
+ $weight = 'bold';
172
+ }
173
+ return $weight;
174
+ }
175
+
176
+ /**
177
+ * @return string
178
+ */
179
+ public function getAppearanceItemsFontSize()
180
+ {
181
+ return $this->sanitizeFontSize((string)$this->getValue('appearance', 'itemsFontSize', 'default'));
182
+ }
183
+
184
+ /**
185
+ * @return string
186
+ */
187
+ public function getAppearanceColorScheme()
188
+ {
189
+ $scheme = $this->getValue('appearance', 'colorScheme');
190
+ if (!array_key_exists($scheme, Core::$plugin->colorSchemesList)) {
191
+ $scheme = 'light';
192
+ }
193
+ return $scheme;
194
+ }
195
+
196
+ /**
197
+ * @return string|null
198
+ */
199
+ public function getAppearanceBackgroundColor()
200
+ {
201
+ return $this->getValue('appearance', 'backgroundColor');
202
+ }
203
+
204
+ /**
205
+ * @return string|null
206
+ */
207
+ public function getAppearanceBorderColor()
208
+ {
209
+ return $this->getValue('appearance', 'borderColor');
210
+ }
211
+
212
+ /**
213
+ * @return string|null
214
+ */
215
+ public function getAppearanceTitleColor()
216
+ {
217
+ return $this->getValue('appearance', 'titleColor');
218
+ }
219
+
220
+ /**
221
+ * @return string|null
222
+ */
223
+ public function getAppearanceLinkColor()
224
+ {
225
+ return $this->getValue('appearance', 'linkColor');
226
+ }
227
+
228
+ /**
229
+ * @return string|null
230
+ */
231
+ public function getAppearanceHoverLinkColor()
232
+ {
233
+ return $this->getValue('appearance', 'hoverLinkColor');
234
+ }
235
+
236
+ /**
237
+ * @return string|null
238
+ */
239
+ public function getAppearanceVisitedLinkColor()
240
+ {
241
+ return $this->getValue('appearance', 'visitedLinkColor');
242
+ }
243
+
244
+ /**
245
+ * @return bool
246
+ */
247
+ public function getAutoInsertEnable()
248
+ {
249
+ return (bool)$this->getValue('autoInsert', 'enable', true);
250
+ }
251
+
252
+ /**
253
+ * @return string
254
+ */
255
+ public function getAutoInsertPosition()
256
+ {
257
+ $position = $this->getValue('autoInsert', 'position');
258
+ if (!array_key_exists($position, Core::$plugin->positionsList)) {
259
+ $position = 'beforefirstheading';
260
+ }
261
+ return $position;
262
+ }
263
+
264
+ /**
265
+ * @return array
266
+ */
267
+ public function getAutoInsertPostTypes()
268
+ {
269
+ $postTypes = $this->getValue('autoInsert', 'postTypes', []);
270
+ return is_array($postTypes) ? $postTypes : [];
271
+ }
272
+
273
+ /**
274
+ * @param string $value
275
+ * @param null $matches
276
+ * @return string
277
+ */
278
+ public function sanitizeWidth($value, &$matches = null)
279
+ {
280
+ if (!Core::$plugin->isCustomWidth($value)) {
281
+ return $value;
282
+ }
283
+ if (preg_match('/^(\d+|\d+\.\d+)(' . implode('|', array_keys(Core::$plugin->blockSizeUnitsList)) . ')$/', $value, $matches)) {
284
+ return $value;
285
+ }
286
+ return 'auto';
287
+ }
288
+
289
+ /**
290
+ * @param string $value
291
+ * @param null $matches
292
+ * @return string
293
+ */
294
+ public function sanitizeFontSize($value, &$matches = null)
295
+ {
296
+ if (preg_match('/^(\d+|\d+\.\d+)(' . implode('|', array_keys(Core::$plugin->fontSizeUnitsList)) . ')$/', $value, $matches)) {
297
+ return $value;
298
+ }
299
+ return 'default';
300
+ }
301
+ }
plugin/Shortcode.php ADDED
@@ -0,0 +1,442 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\plugin;
4
+
5
+ use luckywp\tableOfContents\core\base\BaseObject;
6
+ use luckywp\tableOfContents\core\Core;
7
+ use luckywp\tableOfContents\core\helpers\ArrayHelper;
8
+ use luckywp\tableOfContents\core\helpers\Html;
9
+ use WP_Post;
10
+
11
+ class Shortcode extends BaseObject
12
+ {
13
+
14
+ const TAG = 'lwptoc';
15
+
16
+ /**
17
+ * @var int
18
+ */
19
+ protected $idCounter;
20
+
21
+ /**
22
+ * @var array
23
+ */
24
+ protected $headers;
25
+
26
+ /**
27
+ * @var int
28
+ */
29
+ public $currentOutputDepth;
30
+
31
+ /**
32
+ * @var array
33
+ */
34
+ public $overrideColors = [];
35
+
36
+ /**
37
+ * Инициализация
38
+ */
39
+ public function init()
40
+ {
41
+ parent::init();
42
+ add_shortcode(self::TAG, function ($attrs) {
43
+ if (!is_array($attrs)) {
44
+ $attrs = [];
45
+ }
46
+ global $post;
47
+ if ($post instanceof WP_Post) {
48
+ $postSettings = new PostSettings($post->ID);
49
+ foreach ([
50
+ 'min',
51
+ 'depth',
52
+ 'hierarchical',
53
+ 'numeration',
54
+ 'title',
55
+ 'toggle',
56
+ 'labelShow',
57
+ 'labelHide',
58
+ 'hideItems',
59
+ 'smoothScroll',
60
+ 'smoothScrollOffset',
61
+ 'width',
62
+ 'float',
63
+ 'titleFontSize',
64
+ 'titleFontWeight',
65
+ 'itemsFontSize',
66
+ 'colorScheme',
67
+ 'backgroundColor',
68
+ 'borderColor',
69
+ 'titleColor',
70
+ 'linkColor',
71
+ 'hoverLinkColor',
72
+ 'visitedLinkColor',
73
+ ] as $var) {
74
+ if (!array_key_exists($var, $attrs)) {
75
+ $attrs[$var] = $postSettings->$var;
76
+ }
77
+ }
78
+ }
79
+ return $this->make($attrs);
80
+ });
81
+ add_filter('the_content', [$this, 'begin'], 999);
82
+ }
83
+
84
+ /**
85
+ * @param string $content
86
+ * @return string
87
+ */
88
+ public function begin($content)
89
+ {
90
+ if ($this->needToc($content)) {
91
+ $this->idCounter = 0;
92
+ $this->headers = [];
93
+ $content = preg_replace_callback('#<h([1-6])(.*?)>(.*?)</h\d>#', [$this, 'processHeaders'], $content);
94
+ $content = preg_replace_callback('/' . get_shortcode_regex() . '/s', [$this, 'doShortcode'], $content);
95
+ }
96
+ return $content;
97
+ }
98
+
99
+ /**
100
+ * Выводить ли содержание?
101
+ * @param string $content
102
+ * @return bool
103
+ */
104
+ protected function needToc($content)
105
+ {
106
+ return is_singular() &&
107
+ has_shortcode($content, self::TAG);
108
+ }
109
+
110
+ /**
111
+ * Сбор данных и добавление якорей к заголовкам
112
+ * @param array $m
113
+ * @return string
114
+ */
115
+ public function processHeaders($m)
116
+ {
117
+ $header = [
118
+ 'id' => 'lwptoc' . ++$this->idCounter,
119
+ 'index' => (int)$m[1],
120
+ 'label' => strip_tags($m[3]),
121
+ ];
122
+ $this->headers[] = $header;
123
+
124
+ $content = '';
125
+ $htmlOptions = [
126
+ 'name' => $header['id'],
127
+ ];
128
+ $anchor = Html::a($content, null, $htmlOptions);
129
+
130
+ return $anchor . $m[0];
131
+ }
132
+
133
+ /**
134
+ * Заменить шорткод на блок с содержанием
135
+ * @param array $m
136
+ * @return string
137
+ */
138
+ public function doShortcode($m)
139
+ {
140
+ if ($m[2] == self::TAG) {
141
+ return $this->shortcode(shortcode_parse_atts($m[3]));
142
+ }
143
+ return $m[0];
144
+ }
145
+
146
+ /**
147
+ * @param array $attrs
148
+ * @return string
149
+ */
150
+ protected function shortcode($attrs)
151
+ {
152
+ $headerStyles = [];
153
+ $titleStyles = [];
154
+ $itemsStyles = [];
155
+
156
+ $min = (int)ArrayHelper::getValue($attrs, 'min', Core::$plugin->settings->generalMin);
157
+ if ($min < 1) {
158
+ $min = 1;
159
+ }
160
+ if (count($this->headers) < $min) {
161
+ return '';
162
+ }
163
+
164
+ $items = [];
165
+ $tree = [];
166
+ foreach ($this->headers as $header) {
167
+ $node = null;
168
+ while (count($tree) && ($node === null)) {
169
+ end($tree);
170
+ $key = key($tree);
171
+ if ($header['index'] > $tree[$key]['index']) {
172
+ $node = $key;
173
+ } else {
174
+ unset($tree[$key]);
175
+ }
176
+ }
177
+
178
+ $item = [
179
+ 'id' => $header['id'],
180
+ 'index' => $header['index'],
181
+ 'number' => null,
182
+ 'label' => $header['label'],
183
+ 'childrens' => [],
184
+ ];
185
+
186
+ if ($node === null) {
187
+ $items[] = $item;
188
+ $tree[] = &$items[count($items) - 1];
189
+ } else {
190
+ $tree[$node]['childrens'][] = $item;
191
+ $tree[] = &$tree[$node]['childrens'][count($tree[$node]['childrens']) - 1];
192
+ }
193
+ }
194
+ unset($tree);
195
+
196
+ // Вложенность
197
+ $depth = (int)ArrayHelper::getValue($attrs, 'depth', Core::$plugin->settings->generalDepth);
198
+ if (!array_key_exists($depth, Core::$plugin->depthsList)) {
199
+ $depth = 6;
200
+ }
201
+
202
+ // Уберём все элменты, не подходящие по вложенности
203
+ $currentDepth = 0;
204
+ $fn = function (&$items, $depth) use (&$fn, &$currentDepth) {
205
+ $currentDepth++;
206
+ foreach ($items as $key => $item) {
207
+ if ($currentDepth == $depth) {
208
+ $items[$key]['childrens'] = [];
209
+ }
210
+ $fn($items[$key]['childrens'], $depth);
211
+ }
212
+ $currentDepth--;
213
+ };
214
+ $fn($items, $depth);
215
+
216
+ // Нумерация
217
+ $numeration = ArrayHelper::getValue($attrs, 'numeration', Core::$plugin->settings->generalNumeration);
218
+ $numeration = str_replace(['_', ' ', '-'], '', strtolower($numeration));
219
+ if (in_array($numeration, ['decimalnested', 'decimal', 'roman', 'romannested'])) {
220
+ if (in_array($numeration, ['decimalnested', 'romannested'])) {
221
+ $fn = function (&$items, $numbers) use (&$fn, $numeration) {
222
+ foreach ($items as $key => $item) {
223
+ $numbers[count($numbers) - 1]++;
224
+ $items[$key]['number'] = implode('.', $numeration == 'decimalnested' ? $numbers : array_map([$this, 'decimalToRoman'], $numbers));
225
+ $fn($items[$key]['childrens'], array_merge($numbers, [0]));
226
+ }
227
+ };
228
+ $fn($items, [0]);
229
+ } else {
230
+ $number = 0;
231
+ $fn = function (&$items) use (&$fn, &$number, $numeration) {
232
+ foreach ($items as $key => $item) {
233
+ $number++;
234
+ $items[$key]['number'] = $numeration == 'decimal' ? $number : $this->decimalToRoman($number);
235
+ $fn($items[$key]['childrens']);
236
+ }
237
+ };
238
+ $fn($items);
239
+ }
240
+ }
241
+
242
+ // Без иерархии
243
+ $hierarchical = $this->assertBool(ArrayHelper::getValue($attrs, 'hierarchical', Core::$plugin->settings->generalHierarchical));
244
+ if (!$hierarchical) {
245
+ $newItems = [];
246
+ $fn = function ($items) use (&$fn, &$newItems) {
247
+ foreach ($items as $item) {
248
+ $newItem = $item;
249
+ $newItem['childrens'] = [];
250
+ $newItems[] = $newItem;
251
+ $fn($item['childrens']);
252
+ }
253
+ };
254
+ $fn($items);
255
+ $items = $newItems;
256
+ }
257
+
258
+ $toggle = $this->assertBool(ArrayHelper::getValue($attrs, 'toggle', Core::$plugin->settings->generalToggle));
259
+ $labelShow = null;
260
+ $labelHide = null;
261
+ $hideItems = false;
262
+ if ($toggle) {
263
+ $labelShow = ArrayHelper::getValue($attrs, 'labelshow', Core::$plugin->settings->generalLabelShow);
264
+ $labelHide = ArrayHelper::getValue($attrs, 'labelhide', Core::$plugin->settings->generalLabelHide);
265
+ $hideItems = $this->assertBool(ArrayHelper::getValue($attrs, 'hideitems', Core::$plugin->settings->generalHideItems));
266
+ }
267
+ if ($hideItems) {
268
+ $itemsStyles[] = 'display:none;';
269
+ }
270
+
271
+ $containerOptions = [
272
+ 'class' => ['lwptoc'],
273
+ 'data' => [],
274
+ ];
275
+
276
+ // Плавная прокрутка
277
+ $smoothScroll = $this->assertBool(ArrayHelper::getValue($attrs, 'smoothscroll', Core::$plugin->settings->generalSmoothScroll));
278
+ $containerOptions['data']['smooth-scroll'] = $smoothScroll ? 1 : 0;
279
+ if ($smoothScroll) {
280
+ $containerOptions['data']['smooth-scroll-offset'] = (int)ArrayHelper::getValue($attrs, 'smoothscrolloffset', Core::$plugin->settings->generalSmoothScrollOffset);
281
+ }
282
+
283
+ // Ширина
284
+ $width = ArrayHelper::getValue($attrs, 'width');
285
+ if ($width === null) {
286
+ $width = Core::$plugin->settings->appearanceWidth;
287
+ } else {
288
+ $width = Core::$plugin->settings->sanitizeWidth($width);
289
+ }
290
+ if ($width != 'full') {
291
+ if ($width == 'auto') {
292
+ $containerOptions['class'][] = 'lwptoc-autoWidth';
293
+ } else {
294
+ $containerOptions['style'] = 'width:' . $width;
295
+ }
296
+ }
297
+
298
+ // Выравнивание
299
+ $float = ArrayHelper::getValue($attrs, 'float', Core::$plugin->settings->appearanceFloat);
300
+ $float = strtolower($float);
301
+ if (in_array($float, ['left', 'right'])) {
302
+ $containerOptions['class'][] = 'lwptoc-' . $float;
303
+ }
304
+
305
+ // Размер шрифта заголовка
306
+ $titleFontSize = ArrayHelper::getValue($attrs, 'titlefontsize');
307
+ if ($titleFontSize === null) {
308
+ $titleFontSize = Core::$plugin->settings->appearanceTitleFontSize;
309
+ } else {
310
+ $titleFontSize = Core::$plugin->settings->sanitizeFontSize($titleFontSize);
311
+ }
312
+ if ($titleFontSize != 'default') {
313
+ $headerStyles[] = 'font-size:' . $titleFontSize . ';';
314
+ }
315
+
316
+ // Толщина шрифта заголовка
317
+ $titleFontWeight = ArrayHelper::getValue($attrs, 'titlefontweight', Core::$plugin->settings->appearanceTitleFontWeight);
318
+ $titleFontWeight = str_replace(['_', ' ', '-'], '', strtolower($titleFontWeight));
319
+ if ($titleFontWeight != 'bold' && array_key_exists($titleFontWeight, Core::$plugin->fontWeightsList)) {
320
+ $titleStyles[] = 'font-weight:' . Core::$plugin->fontWeightToValue($titleFontWeight) . ';';
321
+ }
322
+
323
+ // Размер шрифта элементов
324
+ $itemsFontSize = ArrayHelper::getValue($attrs, 'itemsfontsize');
325
+ if ($itemsFontSize === null) {
326
+ $itemsFontSize = Core::$plugin->settings->appearanceItemsFontSize;
327
+ } else {
328
+ $itemsFontSize = Core::$plugin->settings->sanitizeFontSize($itemsFontSize);
329
+ }
330
+ if ($itemsFontSize == '90%') {
331
+ $containerOptions['class'][] = 'lwptoc-baseItems';
332
+ } elseif ($itemsFontSize != 'default') {
333
+ $itemsStyles[] = 'font-size:' . $itemsFontSize . ';';
334
+ }
335
+
336
+ // Цветовая схема
337
+ $colorScheme = ArrayHelper::getValue($attrs, 'colorscheme', Core::$plugin->settings->appearanceColorScheme);
338
+ $colorScheme = str_replace(['_', ' ', '-'], '', strtolower($colorScheme));
339
+ if (array_key_exists($colorScheme, Core::$plugin->colorSchemesList)) {
340
+ $containerOptions['class'][] = 'lwptoc-' . $colorScheme;
341
+ }
342
+
343
+ // Запомним цвета для переопределения
344
+ $this->overrideColors = [];
345
+ foreach ([
346
+ 'backgroundColor',
347
+ 'borderColor',
348
+ 'titleColor',
349
+ 'linkColor',
350
+ 'hoverLinkColor',
351
+ 'visitedLinkColor',
352
+ ] as $var) {
353
+ $color = ArrayHelper::getValue($attrs, strtolower($var), Core::$plugin->settings->{'appearance' . ucfirst($var)});
354
+ $color = Core::$plugin->settings->sanitizeCallbackColor($color);
355
+ if ($color) {
356
+ $this->overrideColors[$var] = $color;
357
+ }
358
+ }
359
+
360
+ // Вывод
361
+ $this->currentOutputDepth = -1;
362
+ return Core::$plugin->front->render('body', [
363
+ 'title' => ArrayHelper::getValue($attrs, 'title', Core::$plugin->settings->generalTitle),
364
+ 'toggle' => $toggle,
365
+ 'labelShow' => $labelShow,
366
+ 'labelHide' => $labelHide,
367
+ 'hideItems' => $hideItems,
368
+ 'containerOptions' => $containerOptions,
369
+ 'headerStyles' => $headerStyles,
370
+ 'titleStyles' => $titleStyles,
371
+ 'itemsStyles' => $itemsStyles,
372
+ 'items' => $items,
373
+ ]);
374
+ }
375
+
376
+ /**
377
+ * @param array $attrs
378
+ * @return string
379
+ */
380
+ public function make($attrs)
381
+ {
382
+ $shortcode = '[' . static::TAG;
383
+ foreach ($attrs as $k => $v) {
384
+ if ($v !== null) {
385
+ if (is_string($v)) {
386
+ $v = str_replace('"', '&quot;', $v);
387
+ } elseif (is_bool($v)) {
388
+ $v = $v ? 1 : 0;
389
+ }
390
+ $shortcode .= ' ' . $k . '="' . $v . '"';
391
+ }
392
+ }
393
+ $shortcode .= ']';
394
+ return $shortcode;
395
+ }
396
+
397
+ /**
398
+ * @param $v
399
+ * @return bool
400
+ */
401
+ protected function assertBool($v)
402
+ {
403
+ if (is_bool($v)) {
404
+ return $v;
405
+ }
406
+ $v = strtolower((string)$v);
407
+ if (in_array($v, ['1', 'true', 'yes', 'y'], true)) {
408
+ return true;
409
+ }
410
+ return false;
411
+ }
412
+
413
+ /**
414
+ * @param int $decimal
415
+ * @return string
416
+ */
417
+ protected function decimalToRoman($decimal)
418
+ {
419
+ $roman = '';
420
+ $map = [
421
+ 'M' => 1000,
422
+ 'CM' => 900,
423
+ 'D' => 500,
424
+ 'CD' => 400,
425
+ 'C' => 100,
426
+ 'XC' => 90,
427
+ 'L' => 50,
428
+ 'XL' => 40,
429
+ 'X' => 10,
430
+ 'IX' => 9,
431
+ 'V' => 5,
432
+ 'IV' => 4,
433
+ 'I' => 1
434
+ ];
435
+ foreach ($map as $number => $value) {
436
+ $matches = intval($decimal / $value);
437
+ $roman .= str_repeat($number, $matches);
438
+ $decimal = $decimal % $value;
439
+ }
440
+ return $roman;
441
+ }
442
+ }
plugin/editorBlock/EditorBlock.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\plugin\editorBlock;
4
+
5
+ use luckywp\tableOfContents\core\base\BaseObject;
6
+ use luckywp\tableOfContents\core\Core;
7
+
8
+ class EditorBlock extends BaseObject
9
+ {
10
+
11
+ public function init()
12
+ {
13
+ if (function_exists('register_block_type')) {
14
+ add_action('init', [$this, 'wpInit']);
15
+ add_action('admin_init', [$this, 'adminInit'], 21);
16
+ }
17
+ }
18
+
19
+ public function wpInit()
20
+ {
21
+ register_block_type('luckywp/tableofcontents', [
22
+ 'editor_script' => 'lwptoc-editorBlock',
23
+ 'render_callback' => function ($attrs) {
24
+ return Core::$plugin->shortcode->make($attrs);
25
+ },
26
+ ]);
27
+ }
28
+
29
+ public function adminInit()
30
+ {
31
+ add_action('enqueue_block_editor_assets', [$this, 'blockEditorAssets']);
32
+ }
33
+
34
+ public function blockEditorAssets()
35
+ {
36
+ wp_register_script('lwptoc-editorBlock', $this->getUrl() . '/editorBlock.min.js', ['wp-blocks', 'wp-element', 'wp-editor', 'wp-components'], Core::$plugin->version);
37
+ }
38
+
39
+ /**
40
+ * @return string
41
+ */
42
+ protected function getUrl()
43
+ {
44
+ return Core::$plugin->url . '/plugin/editorBlock';
45
+ }
46
+ }
plugin/editorBlock/editorBlock.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(t){var e={};function o(n){if(e[n])return e[n].exports;var r=e[n]={i:n,l:!1,exports:{}};return t[n].call(r.exports,r,r.exports,o),r.l=!0,r.exports}o.m=t,o.c=e,o.d=function(t,e,n){o.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},o.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)o.d(n,r,function(e){return t[e]}.bind(null,r));return n},o.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(e,"a",e),e},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o.p="",o(o.s=0)}([function(t,e){var o=wp.blocks.registerBlockType,n=wp.element,r=n.createElement,i=n.Fragment,l=wp.editor.BlockControls,c=wp.components,a=c.Toolbar,s=c.IconButton,p=0,u={};o("luckywp/tableofcontents",{title:lwptocMain.tableOfContents,icon:"list-view",category:"common",supports:{customClassName:!1,html:!1},attributes:{min:{type:"integer"},depth:{type:"integer"},hierarchical:{type:"boolean"},numeration:{type:"string"},title:{type:"string"},toggle:{type:"boolean"},labelShow:{type:"string"},labelHide:{type:"string"},hideItems:{type:"boolean"},smoothScroll:{type:"boolean"},smoothScrollOffset:{type:"integer"},width:{type:"string"},float:{type:"string"},titleFontSize:{type:"string"},titleFontWeight:{type:"string"},itemsFontSize:{type:"string"},colorScheme:{type:"string"},backgroundColor:{type:"string"},borderColor:{type:"string"},titleColor:{type:"string"},linkColor:{type:"string"},hoverLinkColor:{type:"string"},visitedLinkColor:{type:"string"}},edit:function(t){var e=t.attributes,o=t.setAttributes;var n="lwptocEditorBlock"+ ++p,c=JSON.stringify(e);return void 0===u[c]&&($.ajax({url:lwptocMain.ajaxUrl,data:{_ajax_nonce:lwptocMain.nonce,action:"lwptoc_block_view",attrs:e},success:function(t){u[c]=t,$("#"+n).replaceWith(t)}}),u[c]='<div class="lwptocEditorBlock_title lwptocEditorBlock_title-loading" id="'+n+'">'+lwptocMain.tableOfContents+"</div>"),r(i,null,r(l,null,r(a,null,r(s,{label:lwptocMain.Edit,icon:"edit",onClick:function(){$(document).one("lwptocEditorBlockChanged",function(t,e){_.each(e,function(t,o){null===t&&(e[o]=void 0)}),o(e)}),$.lwptocCustomize.show({action:"lwptoc_block_edit",attrs:e,postId:lwptocMain.postId},function(){$(document).off("lwptocEditorBlockChanged")})}}))),r("div",{class:"lwptocEditorBlock",dangerouslySetInnerHTML:{__html:u[c]}}))},save:function(t){return t.attributes.shortcode}})}]);
plugin/editorBlock/src/editorBlock.js ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const {
2
+ registerBlockType,
3
+ } = wp.blocks;
4
+ const {
5
+ createElement,
6
+ Fragment,
7
+ } = wp.element;
8
+ const {
9
+ BlockControls,
10
+ } = wp.editor;
11
+ const {
12
+ Toolbar,
13
+ IconButton,
14
+ } = wp.components;
15
+
16
+ let viewCounter = 0,
17
+ viewCache = {};
18
+
19
+ registerBlockType('luckywp/tableofcontents', {
20
+ title: lwptocMain.tableOfContents,
21
+ icon: 'list-view',
22
+ category: 'common',
23
+ supports: {
24
+ customClassName: false,
25
+ html: false,
26
+ },
27
+ attributes: {
28
+ min: {type: 'integer'},
29
+ depth: {type: 'integer'},
30
+ hierarchical: {type: 'boolean'},
31
+ numeration: {type: 'string'},
32
+ title: {type: 'string'},
33
+ toggle: {type: 'boolean'},
34
+ labelShow: {type: 'string'},
35
+ labelHide: {type: 'string'},
36
+ hideItems: {type: 'boolean'},
37
+ smoothScroll: {type: 'boolean'},
38
+ smoothScrollOffset: {type: 'integer'},
39
+ width: {type: 'string'},
40
+ float: {type: 'string'},
41
+ titleFontSize: {type: 'string'},
42
+ titleFontWeight: {type: 'string'},
43
+ itemsFontSize: {type: 'string'},
44
+ colorScheme: {type: 'string'},
45
+ backgroundColor: {type: 'string'},
46
+ borderColor: {type: 'string'},
47
+ titleColor: {type: 'string'},
48
+ linkColor: {type: 'string'},
49
+ hoverLinkColor: {type: 'string'},
50
+ visitedLinkColor: {type: 'string'},
51
+ },
52
+ edit: function({attributes, setAttributes}) {
53
+ function onEdit() {
54
+ $(document).one('lwptocEditorBlockChanged', function(e, data) {
55
+ _.each(data, function(el, idx) {
56
+ if (el === null) {
57
+ data[idx] = undefined;
58
+ }
59
+ });
60
+ setAttributes(data);
61
+ });
62
+ $.lwptocCustomize.show({
63
+ action: 'lwptoc_block_edit',
64
+ attrs: attributes,
65
+ postId: lwptocMain.postId,
66
+ }, function() {
67
+ $(document).off('lwptocEditorBlockChanged');
68
+ });
69
+ };
70
+
71
+ let id = 'lwptocEditorBlock' + (++viewCounter),
72
+ key = JSON.stringify(attributes);
73
+ if (typeof viewCache[key] === 'undefined') {
74
+ $.ajax({
75
+ url: lwptocMain.ajaxUrl,
76
+ data: {
77
+ _ajax_nonce: lwptocMain.nonce,
78
+ action: 'lwptoc_block_view',
79
+ attrs: attributes,
80
+ },
81
+ success: function(response) {
82
+ viewCache[key] = response;
83
+ $('#' + id).replaceWith(response);
84
+ },
85
+ });
86
+ viewCache[key] = '<div class="lwptocEditorBlock_title lwptocEditorBlock_title-loading" id="' + id + '">' + lwptocMain.tableOfContents + '</div>';
87
+ }
88
+
89
+ return <Fragment>
90
+ <BlockControls>
91
+ <Toolbar>
92
+ <IconButton
93
+ label={lwptocMain.Edit}
94
+ icon="edit"
95
+ onClick={onEdit}
96
+ />
97
+ </Toolbar>
98
+ </BlockControls>
99
+ <div class="lwptocEditorBlock" dangerouslySetInnerHTML={{__html: viewCache[key]}}></div>
100
+ </Fragment>;
101
+ },
102
+ save: function(props) {
103
+ return props.attributes.shortcode;
104
+ },
105
+ });
plugin/mcePlugin/McePlugin.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace luckywp\tableOfContents\plugin\mcePlugin;
4
+
5
+ use luckywp\tableOfContents\core\base\BaseObject;
6
+ use luckywp\tableOfContents\core\Core;
7
+
8
+ class McePlugin extends BaseObject
9
+ {
10
+
11
+ public function init()
12
+ {
13
+ parent::init();
14
+ add_action('admin_init', [$this, 'adminInit'], 20);
15
+ }
16
+
17
+ public function adminInit()
18
+ {
19
+ if (current_user_can('edit_posts') || current_user_can('edit_pages')) {
20
+ add_filter('mce_css', [$this, 'css']);
21
+ add_filter('mce_external_plugins', [$this, 'plugin']);
22
+ add_filter('mce_buttons', [$this, 'button']);
23
+ }
24
+ }
25
+
26
+ /**
27
+ * @param string $css
28
+ * @return string
29
+ */
30
+ public function css($css)
31
+ {
32
+ if (!empty($css)) {
33
+ $css .= ',';
34
+ }
35
+ $css .= $this->getUrl() . '/mce.min.css';
36
+ return $css;
37
+ }
38
+
39
+ /**
40
+ * @param array $plugins
41
+ * @return array
42
+ */
43
+ public function plugin($plugins)
44
+ {
45
+ $plugins['lwptoc'] = $this->getUrl() . '/plugin.min.js';
46
+ return $plugins;
47
+ }
48
+
49
+ /**
50
+ * @param array $buttons
51
+ * @return array
52
+ */
53
+ public function button($buttons)
54
+ {
55
+ array_push($buttons, 'lwptocButton');
56
+ return $buttons;
57
+ }
58
+
59
+ /**
60
+ * @return string
61
+ */
62
+ protected function getUrl()
63
+ {
64
+ return Core::$plugin->url . '/plugin/mcePlugin';
65
+ }
66
+ }
plugin/mcePlugin/mce.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .lwptocShortcode{display:block;padding:12px 16px;border:2px dashed #ddd;background:#fff;color:#333;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",sans-serif}.lwptocShortcode-loading{color:#aaa}.lwptocShortcode_title{font-size:14px;line-height:18px;font-weight:700}.lwptocShortcode_items{margin-top:4px;font-size:12px;line-height:16px}.lwptocShortcode_item{margin-top:2px}.lwptocShortcode_item:first-child{margin-top:0}.lwptocShortcode_item_label{color:#888}.lwptocShortcode .lwptocColorBadge B{position:relative;top:1px;margin-left:2px;display:inline-block;margin-right:4px;width:12px;height:12px;border-radius:6px}
plugin/mcePlugin/plugin.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(n){lwptocMain.postId&&(tinymce.PluginManager.add("lwptoc",function(t){t.addButton("lwptocButton",{icon:"lwptocButton",tooltip:lwptocMain.tableOfContents,onclick:function(){wp.mce.lwptoc.popupWindow(t,!1,{})}})}),wp.mce.lwptoc={viewCounter:0,cache:{},getContent:function(){var t=this,c="lwptocShortcode"+ ++wp.mce.lwptoc.viewCounter,e=JSON.stringify(this.shortcode.attrs.named);return void 0===wp.mce.lwptoc[e]?(n.ajax({url:lwptocMain.ajaxUrl,data:{_ajax_nonce:lwptocMain.nonce,action:"lwptoc_shortcode_view",attrs:this.shortcode.attrs.named},success:function(o){wp.mce.lwptoc[e]=o,t.getEditors(function(t){n(t.getBody()).find("#"+c).replaceWith(o)})}}),'<div class="lwptocShortcode lwptocShortcode-loading" id="'+c+'"><div class="lwptocShortcode_title">'+lwptocMain.tableOfContents+"</div></div>"):wp.mce.lwptoc[e]},edit:function(t,o){wp.mce.lwptoc.popupWindow(tinyMCE.activeEditor,o,wp.shortcode.next("lwptoc",t).shortcode.attrs.named)},popupWindow:function(c,e,t){n(document).one("lwptocShortcodeGenerated",function(t,o){!1===e?c.insertContent(o.shortcode):e(o.shortcode)}),n.lwptocCustomize.show({action:"lwptoc_shortcode_customize",attrs:t,postId:lwptocMain.postId},function(){n(document).off("lwptocShortcodeGenerated")})}},wp.mce.views.register("lwptoc",wp.mce.lwptoc))}(jQuery);
readme.txt ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === LuckyWP Table of Contents ===
2
+ Contributors: theluckywp
3
+ Donate link: https://theluckywp.com/product/table-of-contents/
4
+ Tags: table of contents, toc, navigation, links, heading
5
+ Requires at least: 4.7
6
+ Tested up to: 5.0
7
+ Stable tag: 1.0.0
8
+ Requires PHP: 5.4.45
9
+ License: GPLv2 or later
10
+ License URI: https://www.gnu.org/licenses/gpl-2.0.html
11
+
12
+ Creates a table of contents for your posts/pages. Works automatically or manually (via shortcode or Gutenberg block).
13
+
14
+ == Description ==
15
+
16
+ Creates a table of contents for your posts, pages or custom post types. Great customizable appearance.
17
+
18
+ #### Features
19
+
20
+ * Automatical insertion a table of contents (configure post types and position).
21
+ * Insert by shortcode or Gutenberg block.
22
+ * Button on toolbar of the classic editor.
23
+ * Gutenberg block into "Common Blocks".
24
+ * Setting the minimum number of headings to display table of contents.
25
+ * Setting the depth of headings for table of contents.
26
+ * Hierarchical or linear view.
27
+ * Numeration items: decimal or roman numbers in order or nested.
28
+ * Customizable appearance: width, float, title font size and weight, items font size, colors.
29
+ * Color schemes (dark, light, white, transparent) and the ability to override colors.
30
+ * Toggle Show/Hide (optionally)
31
+ * Customizalbe labels.
32
+ * Smooth scroll (optionally).
33
+ * Setting offset top for smooth scroll.
34
+ * Available override global settings for a particular post.
35
+ * Highly compatible with WordPress themes and plugins.
36
+
37
+ #### Auto Insert
38
+
39
+ For automatical insertion a table of contents in a posts, select option "Auto Insert Table of Contents" in the plugin settings (tab "Auto Insert").
40
+
41
+ Supported positions:
42
+
43
+ * before first heading;
44
+ * after first heading;
45
+ * after first block (paragraph, list or heading);
46
+ * top of post content;
47
+ * bottom of post content.
48
+
49
+ You can also select post types to which the table of contents will be automatically added.
50
+
51
+ == Installation ==
52
+
53
+ #### Installing from the WordPress control panel
54
+
55
+ 1. Go to the page "Plugins > Add New".
56
+ 2. Input the name "LuckyWP Table of Contents" in the search field
57
+ 3. Find the "LuckyWP Table of Contents" plugin in the search result and click on the "Install Now" button, the installation process of plugin will begin.
58
+ 4. Click "Activate" when the installation is complete.
59
+
60
+ #### Installing with the archive
61
+
62
+ 1. Go to the page "Plugins > Add New" on the WordPress control panel
63
+ 2. Click on the "Upload Plugin" button, the form to upload the archive will be opened.
64
+ 3. Select the archive with the plugin and click "Install Now".
65
+ 4. Click on the "Activate Plugin" button when the installation is complete.
66
+
67
+ #### Manual installation
68
+
69
+ 1. Upload the folder `luckywp-table-of-contents` to a directory with the plugin, usually it is `/wp-content/plugins/`.
70
+ 2. Go to the page "Plugins > Add New" on the WordPress control panel
71
+ 3. Find "LuckyWP Table of Contents" in the plugins list and click "Activate".
72
+
73
+ ### After activation
74
+
75
+ Into classic editor will appear button "Table of Contents" (available on edit post/page screen).
76
+ Into Gutenberg editor will appear block "Table of Contents" (see "Common Blocks").
77
+ The menu item "Table of Contents" will appear in the menu "Settings" of the WordPress control panel.
78
+
79
+ == Screenshots ==
80
+
81
+ 1. Table of Contents
82
+ 2. Gutenberg Support
83
+ 3. Classic Editor Support
84
+ 4. Customize Window
85
+ 5. Examples of Color Solutions
86
+ 6. General Settings
87
+ 7. Appearance Settings
88
+ 8. Auto Insert Settings
uninstall.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if (!defined('WP_UNINSTALL_PLUGIN')) {
4
+ die;
5
+ }
6
+
7
+ delete_option('lwptoc_general');
8
+ delete_option('lwptoc_appearance');
9
+ delete_option('lwptoc_autoInsert');