| Server IP : 65.109.86.83 / Your IP : 216.73.217.84 Web Server : nginx/1.14.1 System : Linux libra 4.18.0-553.51.1.el8_10.x86_64 #1 SMP Wed Apr 30 20:24:04 UTC 2025 x86_64 User : root ( 0) PHP Version : 8.3.31 Disable Function : exec,passthru,shell_exec,system,proc_open,popen,parse_ini_file,show_source MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/html/wp-content/plugins/luckywp-table-of-contents/front/ |
Upload File : |
<?php
namespace luckywp\tableOfContents\front;
use luckywp\tableOfContents\core\Core;
use luckywp\tableOfContents\core\helpers\ArrayHelper;
use luckywp\tableOfContents\core\helpers\ValueHelper;
use luckywp\tableOfContents\plugin\Settings;
use function count;
class Toc
{
/**
* @var int
*/
public static $currentOutputDepth;
/**
* @var array
*/
public static $overrideColors = [];
/**
* @param array $headings
* @param array|string|null $attrs
* @return string
*/
public static function render($headings, $attrs)
{
$attrs = ValueHelper::assertArray($attrs);
$headerStyles = [];
$titleStyles = [];
$itemsStyles = [];
$min = (int)ArrayHelper::getValue($attrs, 'min', Core::$plugin->settings->getGeneralMin());
if ($min < 1) {
$min = 1;
}
if (count($headings) < $min) {
return '';
}
$skipHeadingLevels = Core::$plugin->skipHeadingLevelToArray(ArrayHelper::getValue($attrs, 'skipheadinglevel', Core::$plugin->settings->getMiscSkipHeadingLevel()));
$skipHeadingRegex = Core::$plugin->skipHeadingTextToRegex(ArrayHelper::getValue($attrs, 'skipheadingtext', Core::$plugin->settings->getMiscSkipHeadingText()));
$headings = array_filter($headings, function ($heading) use ($skipHeadingLevels, $skipHeadingRegex) {
if (in_array('h' . $heading['index'], $skipHeadingLevels)) {
return false;
}
if ($skipHeadingRegex && preg_match($skipHeadingRegex, $heading['label'])) {
return false;
}
return true;
});
if (count($headings) < $min) {
return '';
}
if (ValueHelper::assertBool(ArrayHelper::getValue($attrs, 'usenofollow', Core::$plugin->settings->getMiscUseNofollow()))) {
$relNofollow = ' rel="nofollow"';
} else {
$relNofollow = '';
}
$items = [];
$tree = [];
foreach ($headings as $heading) {
$node = null;
while (count($tree) && ($node === null)) {
end($tree);
$key = key($tree);
if ($heading['index'] > $tree[$key]['index']) {
$node = $key;
} else {
unset($tree[$key]);
}
}
$item = [
'id' => $heading['id'],
'index' => $heading['index'],
'number' => null,
'numberSuffix' => null,
'label' => $heading['label'],
'relNofollow' => $relNofollow,
'childrens' => [],
];
if ($node === null) {
$items[] = $item;
$tree[] = &$items[count($items) - 1];
} else {
$tree[$node]['childrens'][] = $item;
$tree[] = &$tree[$node]['childrens'][count($tree[$node]['childrens']) - 1];
}
}
unset($tree);
// Вложенность
$depth = (int)ArrayHelper::getValue($attrs, 'depth', Core::$plugin->settings->getGeneralDepth());
if (!array_key_exists($depth, Core::$plugin->getDepthsList())) {
$depth = 6;
}
// Уберём все элменты, не подходящие по вложенности
$currentDepth = 0;
$fn = function (&$items, $depth) use (&$fn, &$currentDepth) {
$currentDepth++;
foreach ($items as $key => $item) {
if ($currentDepth == $depth) {
$items[$key]['childrens'] = [];
}
$fn($items[$key]['childrens'], $depth);
}
$currentDepth--;
};
$fn($items, $depth);
// Нумерация
$numeration = ArrayHelper::getValue($attrs, 'numeration', Core::$plugin->settings->getGeneralNumeration());
$numeration = str_replace(['_', ' ', '-'], '', strtolower($numeration));
if (in_array($numeration, ['decimalnested', 'decimal', 'roman', 'romannested'])) {
if (in_array($numeration, ['decimalnested', 'romannested'])) {
$fn = function (&$items, $numbers) use (&$fn, $numeration) {
foreach ($items as $key => $item) {
$numbers[count($numbers) - 1]++;
$items[$key]['number'] = implode('.', $numeration == 'decimalnested' ? $numbers : array_map([Toc::class, 'decimalToRoman'], $numbers));
$fn($items[$key]['childrens'], array_merge($numbers, [0]));
}
};
$fn($items, [0]);
} else {
$number = 0;
$fn = function (&$items) use (&$fn, &$number, $numeration) {
foreach ($items as $key => $item) {
$number++;
$items[$key]['number'] = $numeration == 'decimal' ? $number : static::decimalToRoman($number);
$fn($items[$key]['childrens']);
}
};
$fn($items);
}
// Суффикс
$suffix = ArrayHelper::getValue([
'dot' => '.',
'roundbracket' => ')',
], ArrayHelper::getValue($attrs, 'numerationsuffix', Core::$plugin->settings->getGeneralNumerationSuffix()), '');
$fn = function (&$items) use (&$fn, $suffix) {
foreach ($items as $key => $item) {
$items[$key]['numberSuffix'] = $suffix;
$fn($items[$key]['childrens']);
}
};
$fn($items);
}
// Без иерархии
$hierarchical = ValueHelper::assertBool(ArrayHelper::getValue($attrs, 'hierarchical', Core::$plugin->settings->getGeneralHierarchical()));
if (!$hierarchical) {
$newItems = [];
$fn = function ($items) use (&$fn, &$newItems) {
foreach ($items as $item) {
$newItem = $item;
$newItem['childrens'] = [];
$newItems[] = $newItem;
$fn($item['childrens']);
}
};
$fn($items);
$items = $newItems;
}
$toggle = ValueHelper::assertBool(ArrayHelper::getValue($attrs, 'toggle', Core::$plugin->settings->getGeneralToggle()));
$labelShow = null;
$labelHide = null;
$hideItems = false;
if ($toggle) {
$labelShow = ArrayHelper::getValue($attrs, 'labelshow', Core::$plugin->settings->getGeneralLabelShow());
$labelHide = ArrayHelper::getValue($attrs, 'labelhide', Core::$plugin->settings->getGeneralLabelHide());
$hideItems = ValueHelper::assertBool(ArrayHelper::getValue($attrs, 'hideitems', Core::$plugin->settings->getGeneralHideItems()));
}
if ($hideItems) {
$itemsStyles[] = 'display:none;';
}
$containerOptions = [
'class' => ['lwptoc'],
'data' => [],
];
$innerContainerOptions = [
'class' => ['lwptoc_i'],
];
// Дополнительные классы
$classes = trim((string)ArrayHelper::getValue($attrs, 'containerclass', Core::$plugin->settings->getMiscContainerClass()));
if ($classes) {
$containerOptions['class'][] = $classes;
}
// Плавная прокрутка
$smoothScroll = ValueHelper::assertBool(ArrayHelper::getValue($attrs, 'smoothscroll', Core::$plugin->settings->getGeneralSmoothScroll()));
$containerOptions['data']['smooth-scroll'] = $smoothScroll ? 1 : 0;
if ($smoothScroll) {
$containerOptions['data']['smooth-scroll-offset'] = (int)ArrayHelper::getValue($attrs, 'smoothscrolloffset', Core::$plugin->settings->getGeneralSmoothScrollOffset());
}
// Выравнивание
$float = ArrayHelper::getValue($attrs, 'float', Core::$plugin->settings->getAppearanceFloat());
$float = strtolower($float);
// Ширина
$width = ArrayHelper::getValue($attrs, 'width');
if ($width === null) {
$width = Core::$plugin->settings->getAppearanceWidth();
} else {
$width = Settings::sanitizeWidth($width);
}
// Опции для выравнивания и ширины
if (in_array($float, ['left', 'right', 'center', 'rightwithoutflow']) &&
(!in_array($float, ['left', 'right', 'rightwithoutflow']) || $width != 'full')) {
$containerOptions['class'][] = 'lwptoc-' . $float;
}
if ($width != 'full') {
if ($width == 'auto') {
$containerOptions['class'][] = 'lwptoc-autoWidth';
} else {
if (in_array($float, ['left', 'right'])) {
$containerOptions['style'] = 'width:' . $width;
} else {
$innerContainerOptions['style'] = 'width:' . $width;
}
}
}
// Размер шрифта заголовка
$titleFontSize = ArrayHelper::getValue($attrs, 'titlefontsize');
if ($titleFontSize === null) {
$titleFontSize = Core::$plugin->settings->getAppearanceTitleFontSize();
} else {
$titleFontSize = Settings::sanitizeFontSize($titleFontSize);
}
if ($titleFontSize != 'default') {
$headerStyles[] = 'font-size:' . $titleFontSize . ';';
}
// Толщина шрифта заголовка
$titleFontWeight = ArrayHelper::getValue($attrs, 'titlefontweight', Core::$plugin->settings->getAppearanceTitleFontWeight());
$titleFontWeight = str_replace(['_', ' ', '-'], '', strtolower($titleFontWeight));
if ($titleFontWeight != 'bold' && array_key_exists($titleFontWeight, Core::$plugin->getFontWeightsList())) {
$titleStyles[] = 'font-weight:' . Core::$plugin->fontWeightToValue($titleFontWeight) . ';';
}
// Размер шрифта элементов
$itemsFontSize = ArrayHelper::getValue($attrs, 'itemsfontsize');
if ($itemsFontSize === null) {
$itemsFontSize = Core::$plugin->settings->getAppearanceItemsFontSize();
} else {
$itemsFontSize = Settings::sanitizeFontSize($itemsFontSize);
}
if ($itemsFontSize == '90%') {
$containerOptions['class'][] = 'lwptoc-baseItems';
} elseif ($itemsFontSize != 'default') {
$itemsStyles[] = 'font-size:' . $itemsFontSize . ';';
}
// Цветовая схема
$colorScheme = ArrayHelper::getValue($attrs, 'colorscheme', Core::$plugin->settings->getAppearanceColorScheme());
$colorScheme = str_replace(['_', ' ', '-'], '', strtolower($colorScheme));
if (array_key_exists($colorScheme, Core::$plugin->getColorSchemesList())) {
$containerOptions['class'][] = 'lwptoc-' . $colorScheme;
if ($colorScheme != 'inherit') {
$containerOptions['class'][] = 'lwptoc-notInherit';
}
}
// Запомним цвета для переопределения
static::$overrideColors = [];
foreach ([
'backgroundColor',
'borderColor',
'titleColor',
'linkColor',
'hoverLinkColor',
'visitedLinkColor',
] as $var) {
$color = ArrayHelper::getValue($attrs, strtolower($var), Core::$plugin->settings->{'appearance' . ucfirst($var)});
$color = Core::$plugin->settings->sanitizeCallbackColor($color);
if ($color) {
static::$overrideColors[$var] = $color;
}
}
$before = (string)apply_filters('lwptoc_before', '');
$after = (string)apply_filters('lwptoc_after', '');
if (ValueHelper::assertBool(ArrayHelper::getValue($attrs, 'wrapnoindex', Core::$plugin->settings->getMiscWrapNoindex()))) {
$before = '<!--noindex-->' . $before;
$after = $after . '<!--/noindex-->';
}
// Подключаем CSS/JS
Core::$plugin->front->enqueueAssets();
// Вывод
static::$currentOutputDepth = -1;
return Core::$plugin->front->render('body', [
'title' => ArrayHelper::getValue($attrs, 'title', Core::$plugin->settings->getGeneralTitle()),
'titleTag' => apply_filters('lwptoc_title_tag', 'b'),
'toggle' => $toggle,
'labelShow' => $labelShow,
'labelHide' => $labelHide,
'hideItems' => $hideItems,
'containerOptions' => $containerOptions,
'innerContainerOptions' => $innerContainerOptions,
'headerStyles' => $headerStyles,
'titleStyles' => $titleStyles,
'itemsStyles' => $itemsStyles,
'items' => $items,
'before' => $before,
'after' => $after,
]);
}
/**
* @param int $decimal
* @return string
*/
protected static function decimalToRoman($decimal)
{
$roman = '';
$map = [
'M' => 1000,
'CM' => 900,
'D' => 500,
'CD' => 400,
'C' => 100,
'XC' => 90,
'L' => 50,
'XL' => 40,
'X' => 10,
'IX' => 9,
'V' => 5,
'IV' => 4,
'I' => 1
];
foreach ($map as $number => $value) {
$matches = intval($decimal / $value);
$roman .= str_repeat($number, $matches);
$decimal = $decimal % $value;
}
return $roman;
}
}