Немає опису редагування
Немає опису редагування
 
(Не показані 24 проміжні версії цього користувача)
Рядок 1: Рядок 1:
// ============================================================
// ============================================================
// MediaWiki:Common.js — Mafia Closed Circle
// MediaWiki:Common.js — Mafia Closed Circle — ФІНАЛЬНА ВЕРСІЯ
// Фінальна версія
// ============================================================
// ============================================================


// ============================================================
// ============================================================
// 1. RANDOM ARTICLES
// FULL-WIDTH PAGE DETECTION
// ============================================================
$(document).ready(function () {
    var apiUrl = mw.config.get('wgScriptPath') + '/api.php';
    $.getJSON(apiUrl, {
        action: 'query', format: 'json', list: 'random',
        rnnamespace: '0', rnlimit: '5',
        prop: 'extracts', exchars: '250', exlimit: 'max', explaintext: true
    }, function (data) {
        var html = '';
        $.each(data.query.random, function (i, article) {
            html += '<div class="random-article-preview">';
            html += '<h2><a href="/wiki/' + encodeURIComponent(article.title) + '">' + article.title + '</a></h2>';
            html += '</div>';
        });
        $('#random-articles-container').html(html);
    });
});
 
// ============================================================
// 2. L-BOX NAVIGATION
// ============================================================
// ============================================================
$(function () {
var MCC_FULLWIDTH_PAGES = [
     var $lbox = $('.l-box');
     'page-Ігри', 'page-Статистика', 'page-Перша_статистика',
     if ($lbox.length === 0) return;
     'page-Фіналіст', 'page-Сезони', 'page-Гравці',
 
     'page-Фундація', 'page-Призовий_фонд', 'page-Призові',
    var $items = $lbox.find('.l-box-item');
    'page-Титули', 'page-Період'
 
];
     function buildSectionMap() {
        var map = [];
        $items.each(function () {
            var target = $(this).data('target');
            if (target === 'top') {
                map.push({ $item: $(this), top: 0 });
            } else {
                var $el = $('#' + target);
                if ($el.length) {
                    map.push({ $item: $(this), top: $el.offset().top });
                }
            }
        });
        map.sort(function (a, b) { return a.top - b.top; });
        return map;
    }


    function updateActive(map) {
function isMccFullwidth() {
        var scrollY = $(window).scrollTop() + 110;
    var bodyClasses = document.body.className;
        var current = null;
    for (var i = 0; i < MCC_FULLWIDTH_PAGES.length; i++) {
        for (var i = 0; i < map.length; i++) {
        if (bodyClasses.indexOf(MCC_FULLWIDTH_PAGES[i]) !== -1) return true;
            if (map[i].top <= scrollY) current = map[i].$item;
        }
        $items.removeClass('active');
        if (current) current.addClass('active');
     }
     }
 
     return false;
     $items.on('click', function () {
        var target = $(this).data('target');
        if (target === 'top') {
            $('html, body').animate({ scrollTop: 0 }, 280);
        } else {
            var $el = $('#' + target);
            if ($el.length) {
                $('html, body').animate({ scrollTop: $el.offset().top - 75 }, 280);
            }
        }
    });
 
    var sectionMap = buildSectionMap();
 
    $(document).on('mcc:content-loaded', function () {
        sectionMap = buildSectionMap();
        updateActive(sectionMap);
    });
 
    var ticking = false;
    $(window).on('scroll', function () {
        if (!ticking) {
            window.requestAnimationFrame(function () {
                updateActive(sectionMap);
                ticking = false;
            });
            ticking = true;
        }
    });
 
    $(window).on('resize', function () {
        sectionMap = buildSectionMap();
        updateActive(sectionMap);
    });
 
    updateActive(sectionMap);
});
 
// ============================================================
// 3. PROFILE ICONS — клік + inactive якщо немає URL
// ============================================================
$(document).ready(function () {
    $('.profile-icon').each(function () {
        var $icon = $(this);
        var url = ($icon.attr('data-url') || '').trim();
 
        if (url) {
            $icon.css('cursor', 'pointer');
            $icon.on('click', function () {
                window.open(url, '_blank');
            });
        } else {
            $icon.addClass('inactive');
        }
    });
});
 
// ============================================================
// 4. ДИНАМІЧНІ ПОСИЛАННЯ НА ГОЛОВНІЙ
// ============================================================
$(document).ready(function () {
    if (!$('body').hasClass('page-Головна_сторінка')) return;
 
    var block1Links = [{ title: 'Фінал Року', url: '/index.php/Фінал_Року' }];
    var block2Links = [
        { title: 'Перша статистика', url: '/index.php/Перша_статистика' },
        { title: 'Період',          url: '/index.php/Період' },
        { title: 'Друга статистика', url: '/index.php/Статистика' }
    ];
    var block3Links = [
        { title: 'Mafia Closed Cup I',        url: '/index.php/Mafia_Closed_Cup_I' },
        { title: 'Mafia Closed Cup I Online', url: '/index.php/Mafia_Closed_Cup_I_Online' },
        { title: 'My Closest Circle I',      url: '/index.php/My_Closest_Circle_I' }
    ];
 
    function rand(arr) { return arr[Math.floor(Math.random() * arr.length)]; }
 
    var $blocks = $('.home__block-image-block p');
    if ($blocks.length >= 3) {
        var l1 = rand(block1Links), l2 = rand(block2Links), l3 = rand(block3Links);
        $blocks.eq(0).html('<a href="' + l1.url + '">' + l1.title + '</a>');
        $blocks.eq(1).html('<a href="' + l2.url + '">' + l2.title + '</a>');
        $blocks.eq(2).html('<a href="' + l3.url + '">' + l3.title + '</a>');
    }
});
 
// ============================================================
// 5. PLAYER TABS + LAZY LOADING ІГОР
// ============================================================
$(function () {
    var $tabs    = $('.player-tab');
    var $contents = $('.player-tab-content');
    var $lbox    = $('.l-box');
    var $rbox    = $('.r-box');
 
    if ($tabs.length === 0) return;
 
    function isMobile() { return $(window).width() <= 768; }
 
    $tabs.on('click', function () {
        var $tab  = $(this);
        var tabId  = $tab.data('tab');
        var $content = $('#tab-' + tabId);
 
        $tabs.removeClass('active');
        $tab.addClass('active');
        $contents.removeClass('active');
        $content.addClass('active');
 
        // L-box — ховаємо на вкладці ігор
        if (tabId === 'games') {
            $lbox.fadeOut(200);
        } else {
            $lbox.fadeIn(200);
        }
 
        // R-box — на мобільних ховаємо при іграх
        if (isMobile()) {
            if (tabId === 'games') $rbox.slideUp(200);
            else                  $rbox.slideDown(200);
        }
 
        // Lazy load
        if (tabId === 'games' && !$content.data('loaded')) {
            var playerName = $content.data('player');
            if (!playerName) return;
 
            $content.html('<div class="tab-loader">Завантаження...</div>');
 
            $.ajax({
                url: mw.config.get('wgScriptPath') + '/api.php',
                data: {
                    action: 'expandtemplates', format: 'json',
                    text: '{{#invoke:FetchData|player_games|player=' + playerName + '}}',
                    prop: 'wikitext'
                },
                dataType: 'json',
                success: function (response) {
                    if (!response.expandtemplates || !response.expandtemplates.wikitext) return;
 
                    $.ajax({
                        url: mw.config.get('wgScriptPath') + '/api.php',
                        data: {
                            action: 'parse', format: 'json',
                            text: response.expandtemplates.wikitext,
                            contentmodel: 'wikitext',
                            disablelimitreport: true
                        },
                        dataType: 'json',
                        success: function (r) {
                            if (r.parse && r.parse.text) {
                                $content.html(r.parse.text['*']);
                                $content.data('loaded', true);
                                $content.find('table.sortable').tablesorter();
 
                                // Запускаємо пост-обробку для нового контенту
                                applyWinrateColors($content);
                                wrapWideTables($content);
                                $(document).trigger('mcc:content-loaded');
                            }
                        },
                        error: function () {
                            $content.html('<p style="color:#ff7777;text-align:center">Помилка завантаження.</p>');
                        }
                    });
                },
                error: function () {
                    $content.html('<p style="color:#ff7777;text-align:center">Помилка завантаження.</p>');
                }
            });
        }
    });
 
    $(window).on('resize', function () {
        if (!isMobile()) $rbox.show();
    });
});
 
// ============================================================
// 6. АНІМАЦІЯ РЯДКІВ ПРИ СОРТУВАННІ
// ============================================================
$(function () {
    $(document).on('sortEnd', 'table.wikitable', function () {
        var $t = $(this);
        $t.addClass('mcc-sorting').removeClass('mcc-sorted');
        setTimeout(function () {
            $t.removeClass('mcc-sorting').addClass('mcc-sorted');
            setTimeout(function () { $t.removeClass('mcc-sorted'); }, 600);
        }, 20);
    });
});
 
// ============================================================
// 7. ГОРИЗОНТАЛЬНИЙ СКРОЛ ДЛЯ ШИРОКИХ ТАБЛИЦЬ
// ============================================================
function wrapWideTables($context) {
    var $root = $context || $(document);
 
    function wrap($table) {
        if ($table.closest('.mcc-scroll-inner').length) return;
 
        $table.wrap('<div class="mcc-scroll-inner"></div>');
        $table.closest('.mcc-scroll-inner').wrap('<div class="mcc-scroll-outer"></div>');
 
        var $inner = $table.closest('.mcc-scroll-inner');
        var $outer = $inner.closest('.mcc-scroll-outer');
 
        function check() {
            var atEnd = $inner[0].scrollLeft + $inner[0].clientWidth >= $inner[0].scrollWidth - 4;
            $outer.toggleClass('mcc-no-fade', atEnd);
        }
 
        $inner.on('scroll', check);
        check();
    }
 
    // Явно задані широкі класи
    $root.find('.wikitable.wide-table, .wikitable.mcc-wide, .wikitable.full-width').each(function () {
        wrap($(this));
    });
 
    // Таблиці з >8 колонок (Запис ігор)
    $root.find('.wikitable').each(function () {
        var $t = $(this);
        if ($t.closest('.mcc-scroll-inner').length) return;
        if ($t.find('thead tr:first th').length > 8) {
            $t.addClass('mcc-wide-table');
            wrap($t);
        }
    });
}
}


$(function () { wrapWideTables(); });
// ============================================================
// 8. ВІНРЕЙТ — кольорове підсвічення клітинок з %
// ============================================================
// ============================================================
// REPLACE applyWinrateColors у Common.js
// (знайди функцію applyWinrateColors і замінь повністю)
// ============================================================
function applyWinrateColors($context) {
    var $root = $context || $(document);
    $root.find('.wikitable tbody td').each(function () {
        var $cell = $(this);
        // Вже оброблено
        if ($cell.hasClass('wr-hi') || $cell.hasClass('wr-lo')) return;
        // Беремо текстовий вміст (без HTML тегів)
        var text = $cell.text().trim();
        // Підтримуємо: "53%", "46.95%", "33.33%", "100%"
        var match = text.match(/^(\d+(?:\.\d+)?)%$/);
        if (!match) return;
        var val = parseFloat(match[1]);
        if      (val >= 55) $cell.addClass('wr-hi');
        else if (val <= 33) $cell.addClass('wr-lo');
    });
}
// ============================================================
// 9. МОБІЛЬНИЙ ПОШУК
// ============================================================
$(function () {
    if ($(window).width() > 768) return;
    if ($('.mobile-search-btn').length > 0) return;
    var $btn = $('<div class="mobile-search-btn">' +
        '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">' +
        '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg></div>');
    var $overlay = $('<div class="mobile-search-overlay">' +
        '<input type="text" placeholder="Пошук..." autocomplete="off">' +
        '<div class="mobile-search-close">×</div></div>');
    $('.minerva-header .branding-box').after($btn);
    $('body').append($overlay);
    var $input = $overlay.find('input');
    var $close = $overlay.find('.mobile-search-close');
    $btn.on('click', function (e) {
        e.preventDefault(); e.stopPropagation();
        $overlay.addClass('active');
        setTimeout(function () { $input.focus(); }, 100);
    });
    $close.on('click', function () {
        $overlay.removeClass('active'); $input.val('');
    });
    $input.on('keydown', function (e) {
        if (e.keyCode === 13) {
            var q = $input.val().trim();
            if (q) window.location.href = '/index.php?title=Спеціальна:Пошук&search=' + encodeURIComponent(q);
        }
        if (e.keyCode === 27) { $overlay.removeClass('active'); $input.val(''); }
    });
    $overlay.on('click', function (e) {
        if (e.target === this) { $overlay.removeClass('active'); $input.val(''); }
    });
});
// ============================================================
// 10. БЛОКУВАННЯ MINERVA SEARCH OVERLAY (ДЕСКТОП)
// ============================================================
$(function () {
    if ($(window).width() <= 768) return;
    var $searchInput = $('#searchInput');
    if ($searchInput.length === 0) return;
    setTimeout(function () {
        $searchInput.attr('placeholder', 'Пошук...');
        var $newInput = $searchInput.clone(false);
        $searchInput.replaceWith($newInput);
        $searchInput = $newInput;
        $searchInput.prop('readonly', false).removeAttr('readonly')
            .removeClass('skin-minerva-search-trigger');
        $searchInput.on('focus click', function (e) {
            e.stopPropagation();
            $('body').removeClass('overlay-enabled search-enabled');
            $('.overlay, .search-overlay').hide();
            if (window.location.hash === '#/search') {
                history.replaceState(null, null, window.location.pathname);
            }
        });
        $searchInput.on('keydown', function (e) {
            if (e.which === 13) {
                e.preventDefault();
                var q = $(this).val().trim();
                if (q) window.location.href = '/index.php?title=Спеціальна:Пошук&search=' + encodeURIComponent(q);
                return false;
            }
        });
    }, 100);
    $(window).on('hashchange', function () {
        if (window.location.hash === '#/search') {
            history.replaceState(null, null, window.location.pathname);
            $('body').removeClass('overlay-enabled search-enabled');
            $('.overlay, .search-overlay').hide();
        }
    });
    if (window.location.hash === '#/search') {
        history.replaceState(null, null, window.location.pathname);
    }
});
// ============================================================
// 11. BANNER SEARCH
// ============================================================
$(function () {
    var $container = $('#bannerSearchContainer');
    if ($container.length === 0) return;
    var $input = $('<input>', { type: 'text', id: 'bannerSearchInput', placeholder: 'Пошук MCC...' });
    var $btn  = $('<button>', { id: 'bannerSearchBtn', text: 'Пошук' });
    $container.append($input).append($btn);
    function doSearch() {
        var q = $input.val().trim();
        if (q) window.location.href = '/index.php?title=Спеціальна:Пошук&search=' + encodeURIComponent(q);
    }
    $btn.on('click', doSearch);
    $input.on('keydown', function (e) { if (e.keyCode === 13) { e.preventDefault(); doSearch(); } });
});
// ============================================================
// MediaWiki:Common.js — Mafia Closed Circle — ФІНАЛЬНА ВЕРСІЯ
// ============================================================


// ============================================================
// ============================================================
Рядок 455: Рядок 33:
         var html = '';
         var html = '';
         $.each(data.query.random, function (i, article) {
         $.each(data.query.random, function (i, article) {
             html += '<div class="random-article-preview"><h2><a href="/wiki/' + encodeURIComponent(article.title) + '">' + article.title + '</a></h2></div>';
             html += '<div class="random-article-preview"><h2><a href="/wiki/'
                + encodeURIComponent(article.title) + '">' + article.title + '</a></h2></div>';
         });
         });
         $('#random-articles-container').html(html);
         $('#random-articles-container').html(html);
Рядок 463: Рядок 42:
// ============================================================
// ============================================================
// 2. L-BOX NAVIGATION
// 2. L-BOX NAVIGATION
// ФІКС: використовуємо document.getElementById для кирилиці
// document.getElementById — коректно обробляє кирилицю
// ============================================================
// ============================================================
$(function () {
$(function () {
Рядок 478: Рядок 57:
                 map.push({ $item: $(this), top: 0 });
                 map.push({ $item: $(this), top: 0 });
             } else {
             } else {
                // ФІКС: document.getElementById коректно обробляє кирилицю
                 var el = document.getElementById(target);
                 var el = document.getElementById(target);
                 if (el) {
                 if (el) {
                     var $el = $(el);
                     var $el = $(el);
                    // Якщо el — це span.mw-headline, беремо батьківський h2
                     var $heading = $el.closest('h2, h3, h4').length
                     var $heading = $el.closest('h2, h3, h4').length
                         ? $el.closest('h2, h3, h4')
                         ? $el.closest('h2, h3, h4') : $el;
                        : $el;
                     map.push({ $item: $(this), top: $heading.offset().top });
                     map.push({ $item: $(this), top: $heading.offset().top });
                 }
                 }
Рядок 506: Рядок 82:
     $items.on('click', function () {
     $items.on('click', function () {
         var target = $(this).data('target');
         var target = $(this).data('target');
         if (target === 'top') {
         $items.removeClass('active');
             $('html, body').animate({ scrollTop: 0 }, 280);
        $(this).addClass('active');
 
        // If games tab is active — switch to profile first, then scroll
        var $profileTab = $('.player-tab[data-tab="profile"], .player-tab[data-tab="info"]');
        var $gamesTab  = $('.player-tab[data-tab="games"]');
        var gamesActive = $gamesTab.length && $gamesTab.hasClass('active');
 
        function doScroll() {
            if (target === 'top') {
                $('html, body').animate({ scrollTop: 0 }, 280);
             } else {
                var el = document.getElementById(target);
                if (el) {
                    var $el = $(el);
                    var $heading = $el.closest('h2, h3, h4').length
                        ? $el.closest('h2, h3, h4') : $el;
                    $('html, body').animate({ scrollTop: $heading.offset().top - 78 }, 280);
                }
            }
        }
 
        if (gamesActive) {
            // Click the profile tab to switch
            $profileTab.trigger('click');
            // Wait for tab switch + DOM
            setTimeout(doScroll, 120);
         } else {
         } else {
             var el = document.getElementById(target);
             doScroll();
            if (el) {
                var $el = $(el);
                var $heading = $el.closest('h2, h3, h4').length
                    ? $el.closest('h2, h3, h4')
                    : $el;
                var offset = $heading.offset().top - 78;
                $('html, body').animate({ scrollTop: offset }, 280);
            }
         }
         }
        // Активуємо одразу при кліку, не чекаємо скрол
        $items.removeClass('active');
        $(this).addClass('active');
     });
     });


Рядок 547: Рядок 137:
     });
     });


    // Перший виклик — після мікро-затримки (DOM точно готовий)
     setTimeout(function () {
     setTimeout(function () {
         sectionMap = buildSectionMap();
         sectionMap = buildSectionMap();
Рядок 555: Рядок 144:


// ============================================================
// ============================================================
// 3. PROFILE ICONS
// 3. PROFILE ICONS — клік + inactive + розгортання <p>
// ============================================================
// ============================================================
$(document).ready(function () {
$(document).ready(function () {
    // Розгортаємо <p> яку MediaWiki вставляє всередину grid
    function fixProfileGrid() {
        $('.profile-links-icons').each(function () {
            var $wrap = $(this);
            var $p = $wrap.find('> p');
            if ($p.length) {
                $p.children().appendTo($wrap);
                $p.remove();
            }
            $wrap.css({ display: 'grid', 'grid-template-columns': 'repeat(4, 1fr)', gap: '0' });
            $wrap.find('.profile-icon').css({
                display: 'flex', 'align-items': 'center', 'justify-content': 'center',
                padding: '13px 4px', float: 'none', width: 'auto',
                'min-width': '0', overflow: 'hidden',
                'border-right': '1px solid rgba(255,255,255,0.06)'
            });
            $wrap.find('.profile-icon:last-child').css('border-right', 'none');
            $wrap.find('.profile-icon img, .profile-icon .mw-file-element').css({
                width: '22px', height: '22px', 'max-width': '22px',
                'object-fit': 'contain', display: 'block'
            });
            $wrap.find('.profile-icon > span, .profile-icon .mw-default-size').css({
                display: 'flex', 'align-items': 'center',
                'justify-content': 'center', 'line-height': '0'
            });
        });
    }
    fixProfileGrid();
    setTimeout(fixProfileGrid, 400);
    // Клік + inactive
     $('.profile-icon').each(function () {
     $('.profile-icon').each(function () {
         var $icon = $(this);
         var $icon = $(this);
Рядок 572: Рядок 192:
// ============================================================
// ============================================================
// 4. STATS OVERLAY НА ФОТО ГРАВЦЯ В R-BOX
// 4. STATS OVERLAY НА ФОТО ГРАВЦЯ В R-BOX
// Читає ігри/перемоги/поразки з параметрів шаблону і додає overlay
// ============================================================
// ============================================================
$(function () {
$(function () {
Рядок 578: Рядок 197:
     if ($rbox.length === 0) return;
     if ($rbox.length === 0) return;


    // Шукаємо рядки таблиці в r-box
     var wins = '', losses = '', games = '';
     var games = '', wins = '', losses = '';
 
     $rbox.find('table tr').each(function () {
     $rbox.find('table tr').each(function () {
         var $th = $(this).find('th');
         var label = $(this).find('th').text().trim().toLowerCase();
        var $td = $(this).find('td');
         var val  = $(this).find('td').text().trim();
        if (!$th.length || !$td.length) return;
        var label = $th.text().trim().toLowerCase();
         var val  = $td.text().trim();
 
         if (label.indexOf('офіційних') !== -1 || label.indexOf('ігор') !== -1) {
         if (label.indexOf('офіційних') !== -1 || label.indexOf('ігор') !== -1) {
            // "46.95% (208 - 235)" → витягуємо числа
             var m = val.match(/\((\d+)[^\d]+(\d+)\)/);
             var m = val.match(/\((\d+)[^\d]+(\d+)\)/);
             if (m) { wins = m[1]; losses = m[2]; }
             if (m) { wins = m[1]; losses = m[2]; }
Рядок 597: Рядок 209:
     });
     });


    // Якщо знайшли дані — додаємо overlay на фото
     if (games && wins && losses) {
     if (games && wins && losses) {
         var $figure = $rbox.find('figure');
         var $figure = $rbox.find('figure');
         if ($figure.length && !$figure.find('.rp-stats-overlay').length) {
         if ($figure.length && !$figure.find('.rp-stats-overlay').length) {
             var html = '<div class="rp-stats-overlay">'
             $figure.append(
                '<div class="rp-stats-overlay">'
                 + '<div class="rp-stat-box"><span class="rp-sv">' + games  + '</span><span class="rp-sl">Ігор</span></div>'
                 + '<div class="rp-stat-box"><span class="rp-sv">' + games  + '</span><span class="rp-sl">Ігор</span></div>'
                 + '<div class="rp-stat-box"><span class="rp-sv">' + wins  + '</span><span class="rp-sl">Перемог</span></div>'
                 + '<div class="rp-stat-box"><span class="rp-sv">' + wins  + '</span><span class="rp-sl">Перемог</span></div>'
                 + '<div class="rp-stat-box"><span class="rp-sv">' + losses + '</span><span class="rp-sl">Поразок</span></div>'
                 + '<div class="rp-stat-box"><span class="rp-sv">' + losses + '</span><span class="rp-sl">Поразок</span></div>'
                 + '</div>';
                 + '</div>'
             $figure.append(html);
             );
         }
         }
     }
     }
Рядок 617: Рядок 229:
     if (!$('body').hasClass('page-Головна_сторінка')) return;
     if (!$('body').hasClass('page-Головна_сторінка')) return;


     var block1Links = [{ title: 'Фінал Року', url: '/index.php/Фінал_Року' }];
     var block1 = [{ title: 'Фінал Року', url: '/index.php/Фінал_Року' }];
     var block2Links = [
     var block2 = [
         { title: 'Перша статистика', url: '/index.php/Перша_статистика' },
         { title: 'Перша статистика', url: '/index.php/Перша_статистика' },
         { title: 'Період',          url: '/index.php/Період' },
         { title: 'Період',          url: '/index.php/Період' },
         { title: 'Друга статистика', url: '/index.php/Статистика' }
         { title: 'Друга статистика', url: '/index.php/Статистика' }
     ];
     ];
     var block3Links = [
     var block3 = [
         { title: 'Mafia Closed Cup I',        url: '/index.php/Mafia_Closed_Cup_I' },
         { title: 'Mafia Closed Cup I',        url: '/index.php/Mafia_Closed_Cup_I' },
         { title: 'Mafia Closed Cup I Online', url: '/index.php/Mafia_Closed_Cup_I_Online' },
         { title: 'Mafia Closed Cup I Online', url: '/index.php/Mafia_Closed_Cup_I_Online' },
Рядок 631: Рядок 243:
     function rand(arr) { return arr[Math.floor(Math.random() * arr.length)]; }
     function rand(arr) { return arr[Math.floor(Math.random() * arr.length)]; }


     var $blocks = $('.home__block-image-block p');
     var $b = $('.home__block-image-block p');
     if ($blocks.length >= 3) {
     if ($b.length >= 3) {
         var l1 = rand(block1Links), l2 = rand(block2Links), l3 = rand(block3Links);
         [rand(block1), rand(block2), rand(block3)].forEach(function (l, i) {
        $blocks.eq(0).html('<a href="' + l1.url + '">' + l1.title + '</a>');
            $b.eq(i).html('<a href="' + l.url + '">' + l.title + '</a>');
        $blocks.eq(1).html('<a href="' + l2.url + '">' + l2.title + '</a>');
         });
         $blocks.eq(2).html('<a href="' + l3.url + '">' + l3.title + '</a>');
     }
     }
});
});
Рядок 643: Рядок 254:
// 6. PLAYER TABS + LAZY LOADING
// 6. PLAYER TABS + LAZY LOADING
// ============================================================
// ============================================================
$(function () {
 
// Глобальна функція перемикання табу (використовується і в l-box)
function activatePlayerTab(tabId) {
     var $tabs    = $('.player-tab');
     var $tabs    = $('.player-tab');
     var $contents = $('.player-tab-content');
     var $contents = $('.player-tab-content');
    var $lbox    = $('.l-box');
     var $rbox    = $('.r-box');
     var $rbox    = $('.r-box');


    $tabs.removeClass('active');
    $contents.removeClass('active');
    $tabs.filter('[data-tab="' + tabId + '"]').addClass('active');
    $('#tab-' + tabId).addClass('active');
    if ($(window).width() <= 768) {
        if (tabId === 'games') $rbox.slideUp(200);
        else                  $rbox.slideDown(200);
    }
}
$(function () {
    var $tabs = $('.player-tab');
     if ($tabs.length === 0) return;
     if ($tabs.length === 0) return;
    function isMobile() { return $(window).width() <= 768; }


     $tabs.on('click', function () {
     $tabs.on('click', function () {
         var $tab  = $(this);
         var tabId = $(this).data('tab');
        var tabId = $tab.data('tab');
         activatePlayerTab(tabId);
         var $content = $('#tab-' + tabId);
 
        $tabs.removeClass('active');
        $tab.addClass('active');
        $contents.removeClass('active');
        $content.addClass('active');


         if (tabId === 'games') {
         if (tabId === 'games') {
             $lbox.fadeOut(200);
             var $content = $('#tab-games');
        } else {
             if ($content.data('loaded')) return;
             $lbox.fadeIn(200);
        }


        if (isMobile()) {
            if (tabId === 'games') $rbox.slideUp(200);
            else                  $rbox.slideDown(200);
        }
        // Lazy load
        if (tabId === 'games' && !$content.data('loaded')) {
             var playerName = $content.data('player');
             var playerName = $content.data('player');
             if (!playerName) return;
             if (!playerName) return;
Рядок 691: Рядок 299:
                 success: function (response) {
                 success: function (response) {
                     if (!response.expandtemplates || !response.expandtemplates.wikitext) return;
                     if (!response.expandtemplates || !response.expandtemplates.wikitext) return;
                     $.ajax({
                     $.ajax({
                         url: mw.config.get('wgScriptPath') + '/api.php',
                         url: mw.config.get('wgScriptPath') + '/api.php',
Рядок 702: Рядок 309:
                         dataType: 'json',
                         dataType: 'json',
                         success: function (r) {
                         success: function (r) {
                             if (r.parse && r.parse.text) {
                             if (!r.parse || !r.parse.text) return;
                                $content.html(r.parse.text['*']);
                            $content.html(r.parse.text['*']);
                                $content.data('loaded', true);
                            $content.data('loaded', true);
 
                                var $table = $content.find('table.sortable, table.wikitable');
                                $table.tablesorter();
 
                                // Додаємо фільтри над таблицею
                                injectGamesFilters($content, $table, playerName);


                            setTimeout(function () {
                                var $table = $content.find('table').first();
                                if ($table.length) {
                                    $table.addClass('wikitable sortable');
                                    try { $table.tablesorter(); } catch(e) {}
                                }
                                 applyWinrateColors($content);
                                 applyWinrateColors($content);
                                applyRolePills($content);
                                 wrapWideTables($content);
                                 wrapWideTables($content);
                                 applyRolePills($content);
 
                                 // Inject filters directly at top of content
                                if (!$content.find('.mcc-games-filters').length) {
                                    buildGamesFilters($content, $table);
                                }
                                 $(document).trigger('mcc:content-loaded');
                                 $(document).trigger('mcc:content-loaded');
                             }
                             }, 100);
                         },
                         },
                         error: function () {
                         error: function () {
Рядок 731: Рядок 343:


     $(window).on('resize', function () {
     $(window).on('resize', function () {
         if (!isMobile()) $rbox.show();
         if ($(window).width() > 768) $('.r-box').show();
     });
     });
});
});
Рядок 738: Рядок 350:
// 7. ФІЛЬТРИ НАД ТАБЛИЦЕЮ ІГОР
// 7. ФІЛЬТРИ НАД ТАБЛИЦЕЮ ІГОР
// ============================================================
// ============================================================
function injectGamesFilters($container, $table, playerName) {
// 7b. BUILD GAMES FILTERS
// ============================================================
function buildGamesFilters($container, $table) {
 
    // Already added?
    if ($container.find('.mcc-games-filters').length) return;
 
    // Find table if not valid
    if (!$table || !$table.length) {
        $table = $container.find('table').first();
    }
     if (!$table.length) return;
     if (!$table.length) return;
    if ($container.find('.mcc-games-filters').length) return;


     // Збираємо унікальні події з таблиці
     // Use ALL tr rows (with or without tbody)
    var $rows = $table.find('tr').filter(function () {
        return $(this).find('td').length > 0; // only data rows
    });
 
    if (!$rows.length) return;
 
    // Collect unique events from first td
     var events = [];
     var events = [];
     $table.find('tbody tr').each(function () {
     $rows.each(function () {
         var event = $(this).find('td').eq(0).text().trim();
         var ev = $(this).find('td').first().text().trim();
         if (event && events.indexOf(event) === -1) events.push(event);
         if (ev && events.indexOf(ev) === -1) events.push(ev);
     });
     });


     var eventOptions = '<option value="">Всі події</option>';
    // Build event options
     var opts = '<option value="">Всі події</option>';
     events.forEach(function (e) {
     events.forEach(function (e) {
         eventOptions += '<option value="' + e + '">' + e + '</option>';
         opts += '<option value="' + e + '">' + e + '</option>';
     });
     });


     var html = '<div class="mcc-games-filters">'
    // Build the filter bar HTML
     var barHTML = '<div class="mcc-games-filters">'
         + '<div class="mcc-filter-group">'
         + '<div class="mcc-filter-group">'
         +  '<span class="mcc-filter-label">Подія</span>'
         +  '<span class="mcc-filter-label">Подія</span>'
         +  '<select class="mcc-filter-select" id="mcc-f-event">' + eventOptions + '</select>'
         +  '<select class="mcc-filter-select mcc-fe">' + opts + '</select>'
         + '</div>'
         + '</div>'
         + '<div class="mcc-filter-group">'
         + '<div class="mcc-filter-group">'
         +  '<span class="mcc-filter-label">Роль</span>'
         +  '<span class="mcc-filter-label">Роль</span>'
         +  '<select class="mcc-filter-select" id="mcc-f-role">'
         +  '<select class="mcc-filter-select mcc-fr">'
         +    '<option value="">Всі ролі</option>'
         +    '<option value="">Всі ролі</option>'
         +    '<option value="Мир">Мирний</option>'
         +    '<option value="Мир">Мирний</option>'
Рядок 771: Рядок 401:
         + '<div class="mcc-filter-group">'
         + '<div class="mcc-filter-group">'
         +  '<span class="mcc-filter-label">Результат</span>'
         +  '<span class="mcc-filter-label">Результат</span>'
         +  '<select class="mcc-filter-select" id="mcc-f-result">'
         +  '<select class="mcc-filter-select mcc-frr">'
         +    '<option value="">Всі</option>'
         +    '<option value="">Всі</option>'
         +    '<option value="В">Перемога</option>'
         +    '<option value="В">Перемога</option>'
Рядок 780: Рядок 410:
         +  '<span class="mcc-filter-label">Макс. тривалість</span>'
         +  '<span class="mcc-filter-label">Макс. тривалість</span>'
         +  '<div class="mcc-slider-wrap">'
         +  '<div class="mcc-slider-wrap">'
         +    '<input type="range" class="mcc-duration-range" id="mcc-f-dur" min="20" max="70" value="70" step="1">'
         +    '<input type="range" class="mcc-duration-range mcc-dur-rng" min="20" max="75" value="75" step="1">'
         +    '<span class="mcc-slider-val" id="mcc-dur-val">70:00</span>'
         +    '<span class="mcc-slider-val mcc-dur-v">75:00</span>'
         +  '</div>'
         +  '</div>'
         + '</div>'
         + '</div>'
         + '<span class="mcc-filter-count" id="mcc-games-count"></span>'
         + '<span class="mcc-filter-count mcc-fcnt"></span>'
         + '</div>';
         + '</div>';


     $table.before(html);
    // Insert at the very top of the container
     $container.prepend(barHTML);


     var $filters = $container.find('.mcc-games-filters');
    // Get reference to the just-added bar
     var $bar = $container.find('.mcc-games-filters').first();


     function timeToMins(str) {
     function toMins(s) {
         str = str.trim();
         s = (s || '').trim();
        // Формати: "37:57" або "37 хв 26 с"
         var m1 = s.match(/^(\d+):(\d+)$/);
         var m1 = str.match(/^(\d+):(\d+)$/);
         if (m1) return parseInt(m1[1]) + parseInt(m1[2]) / 60;
         if (m1) return parseInt(m1[1]) + parseInt(m1[2]) / 60;
         var m2 = str.match(/(\d+)\s*хв\s*(\d+)/);
         var m2 = s.match(/(\d+)\s*[хh]\s*[вv]\s*(\d+)/);
         if (m2) return parseInt(m2[1]) + parseInt(m2[2]) / 60;
         if (m2) return parseInt(m2[1]) + parseInt(m2[2]) / 60;
         return 999;
         return 999;
     }
     }


     function filterTable() {
     function doFilter() {
         var ev   = $('#mcc-f-event').val();
         var ev = $bar.find('.mcc-fe').val() || '';
         var role = $('#mcc-f-role').val();
         var rol = $bar.find('.mcc-fr').val() || '';
         var res = $('#mcc-f-result').val();
         var res = $bar.find('.mcc-frr').val() || '';
         var dur = parseInt($('#mcc-f-dur').val());
         var dur = parseFloat($bar.find('.mcc-dur-rng').val()) || 75;
        var n = 0;


        var visible = 0;
         $rows.each(function () {
         $table.find('tbody tr').each(function () {
             var $tds = $(this).find('td');
             var $row = $(this);
             var evVal  = $tds.eq(0).text().trim();
            var tds  = $row.find('td');
             var rolVal  = $tds.eq(1).text().trim();
             var evVal  = tds.eq(0).text().trim();
             var timeVal = $tds.eq(2).text().trim();
             var roleVal = tds.eq(1).text().trim();
             var resVal  = $tds.eq(3).text().trim();
             var timeVal = tds.eq(2).text().trim();
             var resVal  = tds.eq(3).text().trim();


             var show = true;
             var show = (ev === '' || evVal.indexOf(ev)   !== -1)
            if (ev   && evVal.indexOf(ev) === -1)   show = false;
                    && (rol === '' || rolVal.indexOf(rol) !== -1)
            if (role && roleVal.indexOf(role) === -1) show = false;
                    && (res === '' || resVal.indexOf(res) !== -1)
            if (res && resVal.indexOf(res) === -1) show = false;
                    && toMins(timeVal) <= dur;
            if (timeToMins(timeVal) > dur)            show = false;


             $row.toggle(show);
             $(this).toggle(show);
             if (show) visible++;
             if (show) n++;
         });
         });
        $bar.find('.mcc-fcnt').text(n + '\u00a0ігор');
    }
    $bar.find('.mcc-dur-rng').on('input', function () {
        var v = parseFloat($(this).val());
        $bar.find('.mcc-dur-v').text(v + ':00');
        $(this).css('background-size', ((v - 20) / 55 * 100) + '% 100%');
        doFilter();
    });


        $('#mcc-games-count').text(visible + ' ігор');
    $bar.find('select').on('change', doFilter);
    $bar.find('.mcc-fcnt').text($rows.length + '\u00a0ігор');
    $bar.find('.mcc-dur-rng').css('background-size', '100% 100%');
}
 
// ============================================================
function injectGamesFilters($container, $table) {
    // Robust table search
    if (!$table || !$table.length) {
        $table = $container.find('table').first();
     }
     }
    if (!$table.length) { return; }
    // Don't add twice
    if ($container.find('.mcc-games-filters').length) { return; }
    // Collect unique events from column 0
    var events = [];
    $table.find('tbody tr').each(function () {
        var ev = $(this).find('td').eq(0).text().trim();
        if (ev && events.indexOf(ev) === -1) events.push(ev);
    });


     // Slider
     var opts = '<option value="">Всі події</option>';
     $('#mcc-f-dur').on('input', function () {
     events.slice(0, 60).forEach(function (e) {
         var v = parseInt($(this).val());
         opts += '<option value="' + e + '">' + e + '</option>';
        $('#mcc-dur-val').text(v + ':00');
        var pct = (v - 20) / (70 - 20) * 100;
        $(this).css('background-size', pct + '% 100%');
        filterTable();
     });
     });


     $filters.find('.mcc-filter-select').on('change', filterTable);
     var $filters = $('<div class="mcc-games-filters">'
        + '<div class="mcc-filter-group"><span class="mcc-filter-label">Подія</span>'
        +  '<select class="mcc-filter-select mcc-fe">' + opts + '</select></div>'
        + '<div class="mcc-filter-group"><span class="mcc-filter-label">Роль</span>'
        +  '<select class="mcc-filter-select mcc-fr">'
        +  '<option value="">Всі ролі</option><option value="Мир">Мирний</option>'
        +  '<option value="Шер">Шериф</option><option value="Маф">Мафія</option>'
        +  '<option value="Дон">Дон</option></select></div>'
        + '<div class="mcc-filter-group"><span class="mcc-filter-label">Результат</span>'
        +  '<select class="mcc-filter-select mcc-frr">'
        +  '<option value="">Всі</option>'
        +  '<option value="В">Перемога</option>'
        +  '<option value="П">Поразка</option></select></div>'
        + '<div class="mcc-filter-group mcc-duration-group">'
        +  '<span class="mcc-filter-label">Макс. тривалість</span>'
        +  '<div class="mcc-slider-wrap">'
        +    '<input type="range" class="mcc-duration-range mcc-dur-range" min="20" max="75" value="75" step="1">'
        +    '<span class="mcc-slider-val mcc-dur-val">75:00</span>'
        +  '</div></div>'
        + '<span class="mcc-filter-count mcc-fcount"></span>'
        + '</div>');


     // Початковий підрахунок
     // Insert: before scroll-outer wrapper if exists, else before table
     var total = $table.find('tbody tr').length;
     var $wrap = $table.closest('.mcc-scroll-outer');
     $('#mcc-games-count').text(total + ' ігор');
     ($wrap.length ? $wrap : $table).before($filters);


     // Початковий slider fill
     function toMins(s) {
     var initPct = (70 - 20) / (70 - 20) * 100;
        s = (s || '').trim();
     $('#mcc-f-dur').css('background-size', initPct + '% 100%');
        var m1 = s.match(/^(\d+):(\d+)$/);  if (m1) return +m1[1] + +m1[2]/60;
        var m2 = s.match(/(\d+)\s*хв\s*(\d+)/); if (m2) return +m2[1] + +m2[2]/60;
        return 999;
    }
 
    function doFilter() {
        var ev  = $filters.find('.mcc-fe').val();
        var rol = $filters.find('.mcc-fr').val();
        var res = $filters.find('.mcc-frr').val();
        var dur = +$filters.find('.mcc-dur-range').val();
        var n = 0;
        $table.find('tbody tr').each(function () {
            var td  = $(this).find('td');
            var show = (!ev  || td.eq(0).text().trim().indexOf(ev)  !== -1)
                    && (!rol || td.eq(1).text().trim().indexOf(rol) !== -1)
                    && (!res || td.eq(3).text().trim().indexOf(res) !== -1)
                    && toMins(td.eq(2).text()) <= dur;
            $(this).toggle(show);
            if (show) n++;
        });
        $filters.find('.mcc-fcount').text(n + '\u00a0ігор');
    }
 
     $filters.find('.mcc-dur-range').on('input', function () {
        var v = +$(this).val();
        $filters.find('.mcc-dur-val').text(v + ':00');
        $(this).css('background-size', ((v - 20) / 55 * 100) + '% 100%');
        doFilter();
    });
 
    $filters.find('select').on('change', doFilter);
 
     $filters.find('.mcc-fcount').text($table.find('tbody tr').length + '\u00a0ігор');
    $filters.find('.mcc-dur-range').css('background-size', '100% 100%');
}
}


// ============================================================
// ============================================================
// 8. ROLE PILLS — замінюємо білий текст на кольорові пілюлі
// 8. ROLE PILLS
// ============================================================
// ============================================================
function applyRolePills($context) {
function applyRolePills($context) {
     var $root = $context || $(document);
     var $root = $context || $(document);
     var roleMap = {
     var roleMap = {
         'Мир': { bg: 'rgba(76,175,125,0.12)', color: '#7dd4a6', border: 'rgba(76,175,125,0.20)'  },
        // Мирний — light red (city team color)
         'Шер': { bg: 'rgba(91,143,255,0.12)',   color: '#7da8ff', border: 'rgba(91,143,255,0.20)' },
         'Мир': { bg:'rgba(200,70,70,0.10)',   color:'#e89090', border:'rgba(200,70,70,0.20)'  },
         'Маф': { bg: 'rgba(200,76,76,0.12)',   color: '#e08888', border: 'rgba(200,76,76,0.20)'   },
        // Шериф — gold/amber
         'Дон': { bg: 'rgba(255,215,0,0.10)',    color: '#ffd700', border: 'rgba(255,215,0,0.20)'   }
         'Шер': { bg:'rgba(210,168,60,0.12)', color:'#d4a843', border:'rgba(210,168,60,0.25)' },
        // Мафія — medium grey
         'Маф': { bg:'rgba(100,100,115,0.18)', color:'#aaa8b8', border:'rgba(120,120,140,0.30)'},
        // Дон — lighter grey
         'Дон': { bg:'rgba(80,80,95,0.14)',    color:'#9896a8', border:'rgba(100,100,120,0.24)'}
     };
     };


     $root.find('.wikitable tbody td').each(function () {
     $root.find('.wikitable tbody td').each(function () {
         var $cell = $(this);
         var $cell = $(this);
        // Перевіряємо span з inline color
         var $span = $cell.find('span').filter(function () {
         var $span = $cell.find('span').filter(function () {
             var s = $(this).attr('style') || '';
             return ($(this).attr('style') || '').indexOf('color') !== -1;
            return s.indexOf('color') !== -1;
         });
         });
         if (!$span.length) return;
         if (!$span.length) return;
 
         var text = $span.text().trim();
         var text = $span.text().trim();
         var style = roleMap[text];
         var style = roleMap[text];
         if (!style) return;
         if (!style || $span.hasClass('mcc-pill-done')) return;
 
         $span.addClass('mcc-pill-done').css({
         $span.css({
             display:'inline-block', padding:'2px 10px', borderRadius:'4px',
             'display': 'inline-block',
             fontSize:'12.5px', fontWeight:'600', fontFamily:"'Manrope',sans-serif",
            'padding': '2px 10px',
             background: style.bg, color: style.color, border:'1px solid '+style.border
            'border-radius': '4px',
             'font-size': '12.5px',
            'font-weight': '600',
             'background': style.bg,
            'color': style.color,
            'border': '1px solid ' + style.border
         });
         });
     });
     });
Рядок 889: Рядок 590:


$(function () { applyRolePills(); });
$(function () { applyRolePills(); });
$(document).on('mcc:content-loaded', function () { applyRolePills(); });


// ============================================================
// ============================================================
Рядок 912: Рядок 614:
     function wrap($table) {
     function wrap($table) {
         if ($table.closest('.mcc-scroll-inner').length) return;
         if ($table.closest('.mcc-scroll-inner').length) return;
         $table.wrap('<div class="mcc-scroll-inner"></div>');
         $table.wrap('<div class="mcc-scroll-inner"></div>');
         $table.closest('.mcc-scroll-inner').wrap('<div class="mcc-scroll-outer"></div>');
         $table.closest('.mcc-scroll-inner').wrap('<div class="mcc-scroll-outer"></div>');
         var $inner = $table.closest('.mcc-scroll-inner');
         var $inner = $table.closest('.mcc-scroll-inner');
         var $outer = $inner.closest('.mcc-scroll-outer');
         var $outer = $inner.closest('.mcc-scroll-outer');
         function check() {
         function check() {
             var atEnd = $inner[0].scrollLeft + $inner[0].clientWidth >= $inner[0].scrollWidth - 4;
             $outer.toggleClass('mcc-no-fade',
            $outer.toggleClass('mcc-no-fade', atEnd);
                $inner[0].scrollLeft + $inner[0].clientWidth >= $inner[0].scrollWidth - 4);
         }
         }
         $inner.on('scroll', check);
         $inner.on('scroll', check);
         check();
         check();
Рядок 942: Рядок 640:
}
}


$(function () { wrapWideTables(); });
$(function () {
    // Full-width stat pages — don't wrap tables, let them fill naturally
    if (!isMccFullwidth()) {
        wrapWideTables();
    } else {
        // On full-width pages: CSS handles width and scroll, no JS needed
    }
});


// ============================================================
// ============================================================
Рядок 950: Рядок 655:
     var $root = $context || $(document);
     var $root = $context || $(document);
     $root.find('.wikitable tbody td').each(function () {
     $root.find('.wikitable tbody td').each(function () {
         var $cell = $(this);
         var $c = $(this);
         if ($cell.hasClass('wr-hi') || $cell.hasClass('wr-lo')) return;
         if ($c.hasClass('wr-hi') || $c.hasClass('wr-lo')) return;
         var text  = $cell.text().trim();
         var m = $c.text().trim().match(/^(\d+(?:\.\d+)?)%$/);
        var match = text.match(/^(\d+(?:\.\d+)?)%$/);
         if (!m) return;
         if (!match) return;
         var v = parseFloat(m[1]);
         var val = parseFloat(match[1]);
         if      (v >= 55) $c.addClass('wr-hi');
         if      (val >= 55) $cell.addClass('wr-hi');
         else if (v <= 33) $c.addClass('wr-lo');
         else if (val <= 33) $cell.addClass('wr-lo');
     });
     });
}
}
Рядок 964: Рядок 668:


// ============================================================
// ============================================================
// 12. МОБІЛЬНИЙ ПОШУК
// 12. PLAYER TABS — "(42)" → стилізований badge
// ============================================================
$(function () {
    $('.player-tab').each(function () {
        var $tab = $(this);
        var m = $tab.text().match(/\((\d+)\)/);
        if (!m) return;
        $tab.text($tab.text().replace(/\s*\(\d+\)/, '').trim());
        $tab.append('<span class="mcc-tab-cnt">' + m[1] + '</span>');
    });
});
 
// ============================================================
// 13. СЕЗОНИ — "Чемпіон" синього кольору
// ============================================================
$(function () {
    function applySeasonColors($ctx) {
        ($ctx || $(document)).find('.wikitable tbody td').each(function () {
            var t = $(this).text().trim();
            if (t === 'Чемпіон' || t === '♔ Чемпіон') {
                $(this).css({ color: '#7ab8ff', fontWeight: '700' });
            }
        });
    }
    applySeasonColors();
    $(document).on('mcc:content-loaded', function () { applySeasonColors(); });
});
 
// ============================================================
// 13b. DYNAMIC R-BOX TOP — відступ під реальну висоту header
// ============================================================
$(function () {
    function setRboxTop() {
        var $hdr = $('.header-container.header-chrome');
        var h = $hdr.length ? ($hdr.outerHeight() + 4) : 66;
        $('.r-box, .tournament-box, .series-box').css('top', h + 'px');
    }
    setRboxTop();
    setTimeout(setRboxTop, 300);
    $(window).on('resize', setRboxTop);
});
 
// ============================================================
// 14. NAV <pre> — розгортаємо pre всередині навігації
// ============================================================
$(function () {
    var sel = '.tournament-nav, .series-nav, .player-nav, .championship-nav';
    $(sel).each(function () {
        var $nav = $(this);
        var $pre = $nav.find('pre');
        if (!$pre.length) return;
        $pre.contents().each(function () { $nav.append($(this).clone()); });
        $pre.remove();
    });
});
 
// ============================================================
// 15. МОБІЛЬНИЙ ПОШУК
// ============================================================
// ============================================================
$(function () {
$(function () {
     if ($(window).width() > 768) return;
     if ($(window).width() > 768) return;
     if ($('.mobile-search-btn').length > 0) return;
     if ($('.mobile-search-btn').length) return;


     var $btn = $('<div class="mobile-search-btn">'
     var $btn = $('<div class="mobile-search-btn">'
Рядок 989: Рядок 750:
         setTimeout(function () { $input.focus(); }, 100);
         setTimeout(function () { $input.focus(); }, 100);
     });
     });
     $close.on('click', function () { $overlay.removeClass('active'); $input.val(''); });
     $close.on('click', function () { $overlay.removeClass('active'); $input.val(''); });
     $input.on('keydown', function (e) {
     $input.on('keydown', function (e) {
         if (e.keyCode === 13) {
         if (e.keyCode === 13) {
Рядок 999: Рядок 758:
         if (e.keyCode === 27) { $overlay.removeClass('active'); $input.val(''); }
         if (e.keyCode === 27) { $overlay.removeClass('active'); $input.val(''); }
     });
     });
     $overlay.on('click', function (e) {
     $overlay.on('click', function (e) {
         if (e.target === this) { $overlay.removeClass('active'); $input.val(''); }
         if (e.target === this) { $overlay.removeClass('active'); $input.val(''); }
Рядок 1006: Рядок 764:


// ============================================================
// ============================================================
// 13. БЛОКУВАННЯ MINERVA SEARCH OVERLAY (ДЕСКТОП)
// 16. БЛОКУВАННЯ MINERVA SEARCH OVERLAY (ДЕСКТОП)
// ============================================================
// ============================================================
$(function () {
$(function () {
Рядок 1016: Рядок 774:
     setTimeout(function () {
     setTimeout(function () {
         $searchInput.attr('placeholder', 'Пошук...');
         $searchInput.attr('placeholder', 'Пошук...');
 
         var $new = $searchInput.clone(false);
         var $newInput = $searchInput.clone(false);
         $searchInput.replaceWith($new);
         $searchInput.replaceWith($newInput);
         $searchInput = $new;
         $searchInput = $newInput;


         $searchInput.prop('readonly', false).removeAttr('readonly')
         $searchInput.prop('readonly', false).removeAttr('readonly')
Рядок 1028: Рядок 785:
             $('body').removeClass('overlay-enabled search-enabled');
             $('body').removeClass('overlay-enabled search-enabled');
             $('.overlay, .search-overlay').hide();
             $('.overlay, .search-overlay').hide();
             if (window.location.hash === '#/search') {
             if (window.location.hash === '#/search')
                 history.replaceState(null, null, window.location.pathname);
                 history.replaceState(null, null, window.location.pathname);
            }
         });
         });


Рядок 1051: Рядок 807:
     });
     });


     if (window.location.hash === '#/search') {
     if (window.location.hash === '#/search')
         history.replaceState(null, null, window.location.pathname);
         history.replaceState(null, null, window.location.pathname);
});
// ============================================================
// FILTERS FOR SEASON "Запис ігор" WIDE TABLE
// Only runs on season/tournament pages (not full-width stat pages)
// ============================================================
$(function () {
    // Skip on full-width stat pages
    if (isMccFullwidth()) return;
    var $table = $('.wikitable.wide-table').first();
    if (!$table.length) $table = $('.wikitable.full-width').first();
    if (!$table.length) return;
    var $rows = $table.find('tr').filter(function () {
        return $(this).find('td').length > 0;
    });
    if (!$rows.length) return;
    var results = [];
    $rows.each(function () {
        var tds = $(this).find('td');
        var r = tds.eq(tds.length - 2).text().trim();
        if (r && results.indexOf(r) === -1) results.push(r);
    });
    var resOpts = '<option value="">Всі результати</option>';
    results.forEach(function (r) {
        resOpts += '<option value="' + r + '">' + r + '</option>';
    });
    var $bar = $(
        '<div class="mcc-games-filters">' +
        '<div class="mcc-filter-group">' +
        '<span class="mcc-filter-label">Результат</span>' +
        '<select class="mcc-filter-select sg-res">' + resOpts + '</select>' +
        '</div>' +
        '<div class="mcc-filter-group mcc-duration-group">' +
        '<span class="mcc-filter-label">Макс. тривалість</span>' +
        '<div class="mcc-slider-wrap">' +
        '<input type="range" class="mcc-duration-range sg-dur" min="30" max="65" value="65" step="1">' +
        '<span class="mcc-slider-val sg-durv">65:00</span>' +
        '</div>' +
        '</div>' +
        '<span class="mcc-filter-count sg-cnt"></span>' +
        '</div>'
    );
    var $target = $table.closest('.mcc-scroll-outer, .mcc-scroll-inner');
    ($target.length ? $target : $table).before($bar);
    function toMins(s) {
        s = (s || '').trim();
        var m = s.match(/(\d+)\s*хв\s*(\d+)/);
        if (m) return parseInt(m[1]) + parseInt(m[2]) / 60;
        m = s.match(/^(\d+):(\d+)$/);
        if (m) return parseInt(m[1]) + parseInt(m[2]) / 60;
        return 999;
    }
    function doFilter() {
        var res = $bar.find('.sg-res').val();
        var dur = parseInt($bar.find('.sg-dur').val());
        var n = 0;
        $rows.each(function () {
            var tds = $(this).find('td');
            var timeVal = tds.eq(0).text().trim();
            var resVal  = tds.eq(tds.length - 2).text().trim();
            var show = (!res || resVal === res) && toMins(timeVal) <= dur;
            $(this).toggle(show);
            if (show) n++;
        });
        $bar.find('.sg-cnt').text(n + '\u00a0ігор');
    }
    $bar.find('.sg-dur').on('input', function () {
        var v = parseInt($(this).val());
        $bar.find('.sg-durv').text(v + ':00');
        $(this).css('background-size', ((v - 30) / 35 * 100) + '% 100%');
        doFilter();
    });
    $bar.find('.sg-res').on('change', doFilter);
    $bar.find('.sg-cnt').text($rows.length + '\u00a0ігор');
    $bar.find('.sg-dur').css('background-size', '100% 100%');
});
// ============================================================
// 17. BANNER SEARCH
// ============================================================
$(function () {
    // ── MEDALS helper — adds gold/silver/bronze circles to first-column cells ──
    function addMedals($t) {
        $t.find('tbody tr, tr').filter(function () {
            return $(this).find('td').length >= 2;
        }).each(function () {
            var $tds = $(this).find('td');
            var raw = $tds.eq(0).text().trim();
            var n = parseInt(raw);
            if (isNaN(n) || n < 1 || n > 3) return;
            if ($tds.eq(0).find('[class^="mcc-rank"]').length) return;
            $tds.eq(0).html('<span class="mcc-rank-' + n + '">' + n + '</span>');
        });
     }
     }
    // ── Tables that need medals: closedcups, rank, getnames, wide rank (Період/Фіналіст) ──
    $('.content .mw-parser-output .wikitable').each(function () {
        var $t = $(this);
        if ($t.hasClass('mcc-rating-table')) return;
        var $ths = $t.find('tr').first().find('th');
        if (!$ths.length) return;
        var h0 = $ths.eq(0).text().trim();
        // Wide rank tables: № + more than 5 columns (Період, Фіналіст)
        if (h0.indexOf('№') !== -1 && $ths.length > 5) {
            addMedals($t);
            return;
        }
        // Explicit class-based
        if ($t.hasClass('closedcups') || $t.hasClass('rank')) {
            addMedals($t);
        }
    });
    // ── Get Names tables — auto-number ──
    $('.content .mw-parser-output .wikitable.getnames').each(function () {
        var $t = $(this);
        var $headerRow = $t.find('tr').first();
        if ($headerRow.find('th').first().text().trim() === '№') return;
        $headerRow.prepend('<th style="width:38px;text-align:center">№</th>');
        var n = 1;
        $t.find('tbody tr, tr').filter(function () {
            return $(this).find('td').length > 0;
        }).each(function () {
            var badge = n <= 3
                ? '<span class="mcc-rank-' + n + '">' + n + '</span>'
                : ('<span style="display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;font-size:12px;font-weight:600">' + n + '</span>');
            $(this).prepend('<td style="text-align:center;padding:10px 8px">' + badge + '</td>');
            n++;
        });
    });
    // ── Season final table — auto-number (Пан/Пані | І | П | ДБ | Дія | Σ) ──
    $('.content .mw-parser-output .wikitable').each(function () {
        var $t = $(this);
        if ($t.hasClass('mcc-rating-table') || $t.hasClass('mcc-final-table')) return;
        var $ths = $t.find('tr').first().find('th');
        if (!$ths.length) return;
        var h0 = $ths.eq(0).text().trim();
        var hLast = $ths.last().text().trim();
        var isFinal = (h0 === 'Пан/Пані' || h0 === 'Пан' || h0 === 'Гравець')
                  && hLast === 'Σ';
        if (!isFinal) return;
        $t.addClass('mcc-final-table');
        $t.find('tr').first().prepend('<th style="width:36px;text-align:center">#</th>');
        var n = 1;
        $t.find('tbody tr').each(function () {
            var $tds = $(this).find('td');
            if (!$tds.length) return;
            var badge = n <= 3
                ? '<span class="mcc-rank-' + n + '">' + n + '</span>'
                : n;
            $(this).prepend('<td style="text-align:center;padding:10px 8px">' + badge + '</td>');
            n++;
        });
    });
});
});


// ============================================================
// ============================================================
// 14. BANNER SEARCH
// SEASON RATING TABLE — medals + color extremes for №|Пан/Пані|Σ|І|%
// ============================================================
// ============================================================
$(function () {
$(function () {
     var $container = $('#bannerSearchContainer');
     if (isMccFullwidth()) return;
    if ($container.length === 0) return;


     var $input = $('<input>', { type: 'text', id: 'bannerSearchInput', placeholder: 'Пошук MCC...' });
     function processRatingTable($t) {
    var $btn   = $('<button>', { id: 'bannerSearchBtn', text: 'Пошук' });
        var headers = [];
        $t.find('tr').first().find('th').each(function () {
            headers.push($(this).text().trim());
        });
        if (headers[0] !== '') return;
        if (headers.length < 4) return;
        var hasSigma = (headers[2] && headers[2].indexOf('Σ') !== -1);
        if (!hasSigma) return;
        if (headers.length > 8) return; // wide tables handled elsewhere
 
        var iGames = 3;
        var iPct  = 4;
        var hasI   = headers[iGames] === 'І';
        var hasPct = headers[iPct]  === '%';
 
        $t.addClass('mcc-rating-table');


    $container.append($input).append($btn);
        var $rows = $t.find('tbody tr').filter(function () {
            return $(this).find('td').length >= 4;
        });
        if (!$rows.length) return;


     function doSearch() {
        // Medals top 3
         var q = $input.val().trim();
        $rows.each(function () {
        if (q) window.location.href = '/index.php?title=Спеціальна:Пошук&search=' + encodeURIComponent(q);
            var $tds = $(this).find('td');
            var n = parseInt($tds.eq(0).text().trim());
            if (n >= 1 && n <= 3) {
                $tds.eq(0).html('<span class="mcc-rank-' + n + '">' + n + '</span>');
            }
        });
 
        // Σ white
        $rows.each(function () {
            $(this).find('td').eq(2).css({ color: '#ffffff', fontWeight: '700' });
        });
 
        // Games extremes
        if (hasI) {
            var gVals = [];
            $rows.each(function () {
                var v = parseInt($(this).find('td').eq(iGames).text().trim());
                if (!isNaN(v) && v > 0) gVals.push(v);
            });
            if (gVals.length > 1) {
                var gMax = Math.max.apply(null, gVals);
                var gMin = Math.min.apply(null, gVals);
                $rows.each(function () {
                    var $td = $(this).find('td').eq(iGames);
                    var v = parseInt($td.text().trim());
                    if (isNaN(v)) return;
                    if (v === gMax) $td.addClass('mcc-games-hi');
                    else if (v === gMin) $td.addClass('mcc-games-lo');
                });
            }
        }
 
        // WR extremes
        if (hasPct) {
            var pVals = [];
            $rows.each(function () {
                var v = parseFloat($(this).find('td').eq(iPct).text().trim().replace('%',''));
                if (!isNaN(v)) pVals.push(v);
            });
            if (pVals.length > 1) {
                var pMax = Math.max.apply(null, pVals);
                var pMin = Math.min.apply(null, pVals);
                $rows.each(function () {
                    var $td = $(this).find('td').eq(iPct);
                    var v = parseFloat($td.text().trim().replace('%',''));
                    if (isNaN(v)) return;
                    if (v === pMax) $td.addClass('mcc-wr-hi');
                    else if (v === pMin) $td.addClass('mcc-wr-lo');
                });
            }
        }
    }
 
    $('.content .mw-parser-output .wikitable').each(function () {
        processRatingTable($(this));
    });
});
 
// ============================================================
// PLAYER GAMES FILTERS — pre-rendered tab (server-side FetchData)
// ============================================================
$(function () {
    var $content = $('#tab-games');
    if (!$content.length) return;
    var $table = $content.find('table').first();
    if (!$table.length) return;
    applyWinrateColors($content);
    applyRolePills($content);
    buildGamesFilters($content, $table);
});
 
// ============================================================
// 17. BANNER SEARCH
// ============================================================
$(function () {
    var $searchInput = $('#searchInput');
     if (!$searchInput.length) return;
 
    $searchInput.on('keydown', function (e) {
         if (e.key === 'Enter') {
            var q = $(this).val().trim();
            if (q) window.location.href = '/index.php?search=' + encodeURIComponent(q);
        }
    });
});
 
// ============================================================
// HOME PAGE — collapsible panels + clickable rows
// ============================================================
$(function () {
    // Expand/collapse
  var GRID_PANELS = ['seasons-panel', 'gn-panel', 'closed-panel'];
 
$('[data-toggle]').on('click', function () {
    var $hd = $(this);
    var targetId = $hd.data('toggle');
    var $bd = $('#' + targetId);
    if (!$bd.length) return;
 
    var isGridPanel = GRID_PANELS.indexOf(targetId) !== -1;
    var isDesktop = $(window).width() >= 1000;
 
    if (isGridPanel && isDesktop) {
        // Collapse/expand all 3 grid panels together
        var collapsed = $bd.hasClass('collapsed');
        GRID_PANELS.forEach(function(id) {
            $('#' + id).toggleClass('collapsed', !collapsed);
            $('[data-toggle="' + id + '"]').toggleClass('collapsed', !collapsed);
        });
    } else {
        var collapsed = $bd.hasClass('collapsed');
        $bd.toggleClass('collapsed', !collapsed);
        $hd.toggleClass('collapsed', !collapsed);
     }
     }
});
    // Clickable rows / cards — navigate on click, ignore inner link clicks
    $(document).on('click', '.mcc-nav', function (e) {
        if ($(e.target).closest('a').length) return; // let wiki links work normally
        var href = $(this).data('href');
        if (href) window.location.href = href;
    });


     $btn.on('click', doSearch);
    // Cursor pointer for nav elements
    $input.on('keydown', function (e) { if (e.keyCode === 13) { e.preventDefault(); doSearch(); } });
     $('.mcc-nav').css('cursor', 'pointer');
});
});

Поточна версія на 02:34, 11 квітня 2026

// ============================================================
// MediaWiki:Common.js — Mafia Closed Circle — ФІНАЛЬНА ВЕРСІЯ
// ============================================================

// ============================================================
// FULL-WIDTH PAGE DETECTION
// ============================================================
var MCC_FULLWIDTH_PAGES = [
    'page-Ігри', 'page-Статистика', 'page-Перша_статистика',
    'page-Фіналіст', 'page-Сезони', 'page-Гравці',
    'page-Фундація', 'page-Призовий_фонд', 'page-Призові',
    'page-Титули', 'page-Період'
];

function isMccFullwidth() {
    var bodyClasses = document.body.className;
    for (var i = 0; i < MCC_FULLWIDTH_PAGES.length; i++) {
        if (bodyClasses.indexOf(MCC_FULLWIDTH_PAGES[i]) !== -1) return true;
    }
    return false;
}


// ============================================================
// 1. RANDOM ARTICLES
// ============================================================
$(document).ready(function () {
    var apiUrl = mw.config.get('wgScriptPath') + '/api.php';
    $.getJSON(apiUrl, {
        action: 'query', format: 'json', list: 'random',
        rnnamespace: '0', rnlimit: '5'
    }, function (data) {
        var html = '';
        $.each(data.query.random, function (i, article) {
            html += '<div class="random-article-preview"><h2><a href="/wiki/'
                + encodeURIComponent(article.title) + '">' + article.title + '</a></h2></div>';
        });
        $('#random-articles-container').html(html);
    });
});

// ============================================================
// 2. L-BOX NAVIGATION
// document.getElementById — коректно обробляє кирилицю
// ============================================================
$(function () {
    var $lbox = $('.l-box');
    if ($lbox.length === 0) return;

    var $items = $lbox.find('.l-box-item');

    function buildSectionMap() {
        var map = [];
        $items.each(function () {
            var target = $(this).data('target');
            if (target === 'top') {
                map.push({ $item: $(this), top: 0 });
            } else {
                var el = document.getElementById(target);
                if (el) {
                    var $el = $(el);
                    var $heading = $el.closest('h2, h3, h4').length
                        ? $el.closest('h2, h3, h4') : $el;
                    map.push({ $item: $(this), top: $heading.offset().top });
                }
            }
        });
        map.sort(function (a, b) { return a.top - b.top; });
        return map;
    }

    function updateActive(map) {
        var scrollY = $(window).scrollTop() + 120;
        var current = null;
        for (var i = 0; i < map.length; i++) {
            if (map[i].top <= scrollY) current = map[i].$item;
        }
        $items.removeClass('active');
        if (current) current.addClass('active');
    }

    $items.on('click', function () {
        var target = $(this).data('target');
        $items.removeClass('active');
        $(this).addClass('active');

        // If games tab is active — switch to profile first, then scroll
        var $profileTab = $('.player-tab[data-tab="profile"], .player-tab[data-tab="info"]');
        var $gamesTab   = $('.player-tab[data-tab="games"]');
        var gamesActive = $gamesTab.length && $gamesTab.hasClass('active');

        function doScroll() {
            if (target === 'top') {
                $('html, body').animate({ scrollTop: 0 }, 280);
            } else {
                var el = document.getElementById(target);
                if (el) {
                    var $el = $(el);
                    var $heading = $el.closest('h2, h3, h4').length
                        ? $el.closest('h2, h3, h4') : $el;
                    $('html, body').animate({ scrollTop: $heading.offset().top - 78 }, 280);
                }
            }
        }

        if (gamesActive) {
            // Click the profile tab to switch
            $profileTab.trigger('click');
            // Wait for tab switch + DOM
            setTimeout(doScroll, 120);
        } else {
            doScroll();
        }
    });

    var sectionMap = buildSectionMap();

    $(document).on('mcc:content-loaded', function () {
        sectionMap = buildSectionMap();
        updateActive(sectionMap);
    });

    var ticking = false;
    $(window).on('scroll', function () {
        if (!ticking) {
            window.requestAnimationFrame(function () {
                updateActive(sectionMap);
                ticking = false;
            });
            ticking = true;
        }
    });

    $(window).on('resize', function () {
        sectionMap = buildSectionMap();
        updateActive(sectionMap);
    });

    setTimeout(function () {
        sectionMap = buildSectionMap();
        updateActive(sectionMap);
    }, 200);
});

// ============================================================
// 3. PROFILE ICONS — клік + inactive + розгортання <p>
// ============================================================
$(document).ready(function () {
    // Розгортаємо <p> яку MediaWiki вставляє всередину grid
    function fixProfileGrid() {
        $('.profile-links-icons').each(function () {
            var $wrap = $(this);
            var $p = $wrap.find('> p');
            if ($p.length) {
                $p.children().appendTo($wrap);
                $p.remove();
            }
            $wrap.css({ display: 'grid', 'grid-template-columns': 'repeat(4, 1fr)', gap: '0' });
            $wrap.find('.profile-icon').css({
                display: 'flex', 'align-items': 'center', 'justify-content': 'center',
                padding: '13px 4px', float: 'none', width: 'auto',
                'min-width': '0', overflow: 'hidden',
                'border-right': '1px solid rgba(255,255,255,0.06)'
            });
            $wrap.find('.profile-icon:last-child').css('border-right', 'none');
            $wrap.find('.profile-icon img, .profile-icon .mw-file-element').css({
                width: '22px', height: '22px', 'max-width': '22px',
                'object-fit': 'contain', display: 'block'
            });
            $wrap.find('.profile-icon > span, .profile-icon .mw-default-size').css({
                display: 'flex', 'align-items': 'center',
                'justify-content': 'center', 'line-height': '0'
            });
        });
    }
    fixProfileGrid();
    setTimeout(fixProfileGrid, 400);

    // Клік + inactive
    $('.profile-icon').each(function () {
        var $icon = $(this);
        var url = ($icon.attr('data-url') || '').trim();
        if (url) {
            $icon.css('cursor', 'pointer');
            $icon.on('click', function () { window.open(url, '_blank'); });
        } else {
            $icon.addClass('inactive');
        }
    });
});

// ============================================================
// 4. STATS OVERLAY НА ФОТО ГРАВЦЯ В R-BOX
// ============================================================
$(function () {
    var $rbox = $('.r-box');
    if ($rbox.length === 0) return;

    var wins = '', losses = '', games = '';
    $rbox.find('table tr').each(function () {
        var label = $(this).find('th').text().trim().toLowerCase();
        var val   = $(this).find('td').text().trim();
        if (label.indexOf('офіційних') !== -1 || label.indexOf('ігор') !== -1) {
            var m = val.match(/\((\d+)[^\d]+(\d+)\)/);
            if (m) { wins = m[1]; losses = m[2]; }
            var total = parseInt(wins || 0) + parseInt(losses || 0);
            if (total > 0) games = total;
        }
    });

    if (games && wins && losses) {
        var $figure = $rbox.find('figure');
        if ($figure.length && !$figure.find('.rp-stats-overlay').length) {
            $figure.append(
                '<div class="rp-stats-overlay">'
                + '<div class="rp-stat-box"><span class="rp-sv">' + games  + '</span><span class="rp-sl">Ігор</span></div>'
                + '<div class="rp-stat-box"><span class="rp-sv">' + wins   + '</span><span class="rp-sl">Перемог</span></div>'
                + '<div class="rp-stat-box"><span class="rp-sv">' + losses + '</span><span class="rp-sl">Поразок</span></div>'
                + '</div>'
            );
        }
    }
});

// ============================================================
// 5. ДИНАМІЧНІ ПОСИЛАННЯ НА ГОЛОВНІЙ
// ============================================================
$(document).ready(function () {
    if (!$('body').hasClass('page-Головна_сторінка')) return;

    var block1 = [{ title: 'Фінал Року', url: '/index.php/Фінал_Року' }];
    var block2 = [
        { title: 'Перша статистика', url: '/index.php/Перша_статистика' },
        { title: 'Період',           url: '/index.php/Період' },
        { title: 'Друга статистика', url: '/index.php/Статистика' }
    ];
    var block3 = [
        { title: 'Mafia Closed Cup I',        url: '/index.php/Mafia_Closed_Cup_I' },
        { title: 'Mafia Closed Cup I Online', url: '/index.php/Mafia_Closed_Cup_I_Online' },
        { title: 'My Closest Circle I',       url: '/index.php/My_Closest_Circle_I' }
    ];

    function rand(arr) { return arr[Math.floor(Math.random() * arr.length)]; }

    var $b = $('.home__block-image-block p');
    if ($b.length >= 3) {
        [rand(block1), rand(block2), rand(block3)].forEach(function (l, i) {
            $b.eq(i).html('<a href="' + l.url + '">' + l.title + '</a>');
        });
    }
});

// ============================================================
// 6. PLAYER TABS + LAZY LOADING
// ============================================================

// Глобальна функція перемикання табу (використовується і в l-box)
function activatePlayerTab(tabId) {
    var $tabs     = $('.player-tab');
    var $contents = $('.player-tab-content');
    var $rbox     = $('.r-box');

    $tabs.removeClass('active');
    $contents.removeClass('active');
    $tabs.filter('[data-tab="' + tabId + '"]').addClass('active');
    $('#tab-' + tabId).addClass('active');

    if ($(window).width() <= 768) {
        if (tabId === 'games') $rbox.slideUp(200);
        else                   $rbox.slideDown(200);
    }
}

$(function () {
    var $tabs = $('.player-tab');
    if ($tabs.length === 0) return;

    $tabs.on('click', function () {
        var tabId = $(this).data('tab');
        activatePlayerTab(tabId);

        if (tabId === 'games') {
            var $content = $('#tab-games');
            if ($content.data('loaded')) return;

            var playerName = $content.data('player');
            if (!playerName) return;

            $content.html('<div class="tab-loader">Завантаження...</div>');

            $.ajax({
                url: mw.config.get('wgScriptPath') + '/api.php',
                data: {
                    action: 'expandtemplates', format: 'json',
                    text: '{{#invoke:FetchData|player_games|player=' + playerName + '}}',
                    prop: 'wikitext'
                },
                dataType: 'json',
                success: function (response) {
                    if (!response.expandtemplates || !response.expandtemplates.wikitext) return;
                    $.ajax({
                        url: mw.config.get('wgScriptPath') + '/api.php',
                        data: {
                            action: 'parse', format: 'json',
                            text: response.expandtemplates.wikitext,
                            contentmodel: 'wikitext',
                            disablelimitreport: true
                        },
                        dataType: 'json',
                        success: function (r) {
                            if (!r.parse || !r.parse.text) return;
                            $content.html(r.parse.text['*']);
                            $content.data('loaded', true);

                            setTimeout(function () {
                                var $table = $content.find('table').first();
                                if ($table.length) {
                                    $table.addClass('wikitable sortable');
                                    try { $table.tablesorter(); } catch(e) {}
                                }
                                applyWinrateColors($content);
                                applyRolePills($content);
                                wrapWideTables($content);

                                // Inject filters directly at top of content
                                if (!$content.find('.mcc-games-filters').length) {
                                    buildGamesFilters($content, $table);
                                }
                                $(document).trigger('mcc:content-loaded');
                            }, 100);
                        },
                        error: function () {
                            $content.html('<p style="color:#ff7777;text-align:center;padding:20px">Помилка завантаження.</p>');
                        }
                    });
                },
                error: function () {
                    $content.html('<p style="color:#ff7777;text-align:center;padding:20px">Помилка завантаження.</p>');
                }
            });
        }
    });

    $(window).on('resize', function () {
        if ($(window).width() > 768) $('.r-box').show();
    });
});

// ============================================================
// 7. ФІЛЬТРИ НАД ТАБЛИЦЕЮ ІГОР
// ============================================================
// 7b. BUILD GAMES FILTERS
// ============================================================
function buildGamesFilters($container, $table) {

    // Already added?
    if ($container.find('.mcc-games-filters').length) return;

    // Find table if not valid
    if (!$table || !$table.length) {
        $table = $container.find('table').first();
    }
    if (!$table.length) return;

    // Use ALL tr rows (with or without tbody)
    var $rows = $table.find('tr').filter(function () {
        return $(this).find('td').length > 0; // only data rows
    });

    if (!$rows.length) return;

    // Collect unique events from first td
    var events = [];
    $rows.each(function () {
        var ev = $(this).find('td').first().text().trim();
        if (ev && events.indexOf(ev) === -1) events.push(ev);
    });

    // Build event options
    var opts = '<option value="">Всі події</option>';
    events.forEach(function (e) {
        opts += '<option value="' + e + '">' + e + '</option>';
    });

    // Build the filter bar HTML
    var barHTML = '<div class="mcc-games-filters">'
        + '<div class="mcc-filter-group">'
        +   '<span class="mcc-filter-label">Подія</span>'
        +   '<select class="mcc-filter-select mcc-fe">' + opts + '</select>'
        + '</div>'
        + '<div class="mcc-filter-group">'
        +   '<span class="mcc-filter-label">Роль</span>'
        +   '<select class="mcc-filter-select mcc-fr">'
        +     '<option value="">Всі ролі</option>'
        +     '<option value="Мир">Мирний</option>'
        +     '<option value="Шер">Шериф</option>'
        +     '<option value="Маф">Мафія</option>'
        +     '<option value="Дон">Дон</option>'
        +   '</select>'
        + '</div>'
        + '<div class="mcc-filter-group">'
        +   '<span class="mcc-filter-label">Результат</span>'
        +   '<select class="mcc-filter-select mcc-frr">'
        +     '<option value="">Всі</option>'
        +     '<option value="В">Перемога</option>'
        +     '<option value="П">Поразка</option>'
        +   '</select>'
        + '</div>'
        + '<div class="mcc-filter-group mcc-duration-group">'
        +   '<span class="mcc-filter-label">Макс. тривалість</span>'
        +   '<div class="mcc-slider-wrap">'
        +     '<input type="range" class="mcc-duration-range mcc-dur-rng" min="20" max="75" value="75" step="1">'
        +     '<span class="mcc-slider-val mcc-dur-v">75:00</span>'
        +   '</div>'
        + '</div>'
        + '<span class="mcc-filter-count mcc-fcnt"></span>'
        + '</div>';

    // Insert at the very top of the container
    $container.prepend(barHTML);

    // Get reference to the just-added bar
    var $bar = $container.find('.mcc-games-filters').first();

    function toMins(s) {
        s = (s || '').trim();
        var m1 = s.match(/^(\d+):(\d+)$/);
        if (m1) return parseInt(m1[1]) + parseInt(m1[2]) / 60;
        var m2 = s.match(/(\d+)\s*[хh]\s*[вv]\s*(\d+)/);
        if (m2) return parseInt(m2[1]) + parseInt(m2[2]) / 60;
        return 999;
    }

    function doFilter() {
        var ev  = $bar.find('.mcc-fe').val()  || '';
        var rol = $bar.find('.mcc-fr').val()  || '';
        var res = $bar.find('.mcc-frr').val() || '';
        var dur = parseFloat($bar.find('.mcc-dur-rng').val()) || 75;
        var n = 0;

        $rows.each(function () {
            var $tds = $(this).find('td');
            var evVal   = $tds.eq(0).text().trim();
            var rolVal  = $tds.eq(1).text().trim();
            var timeVal = $tds.eq(2).text().trim();
            var resVal  = $tds.eq(3).text().trim();

            var show = (ev  === '' || evVal.indexOf(ev)   !== -1)
                    && (rol === '' || rolVal.indexOf(rol)  !== -1)
                    && (res === '' || resVal.indexOf(res)  !== -1)
                    && toMins(timeVal) <= dur;

            $(this).toggle(show);
            if (show) n++;
        });
        $bar.find('.mcc-fcnt').text(n + '\u00a0ігор');
    }

    $bar.find('.mcc-dur-rng').on('input', function () {
        var v = parseFloat($(this).val());
        $bar.find('.mcc-dur-v').text(v + ':00');
        $(this).css('background-size', ((v - 20) / 55 * 100) + '% 100%');
        doFilter();
    });

    $bar.find('select').on('change', doFilter);
    $bar.find('.mcc-fcnt').text($rows.length + '\u00a0ігор');
    $bar.find('.mcc-dur-rng').css('background-size', '100% 100%');
}

// ============================================================
function injectGamesFilters($container, $table) {
    // Robust table search
    if (!$table || !$table.length) {
        $table = $container.find('table').first();
    }
    if (!$table.length) { return; }

    // Don't add twice
    if ($container.find('.mcc-games-filters').length) { return; }

    // Collect unique events from column 0
    var events = [];
    $table.find('tbody tr').each(function () {
        var ev = $(this).find('td').eq(0).text().trim();
        if (ev && events.indexOf(ev) === -1) events.push(ev);
    });

    var opts = '<option value="">Всі події</option>';
    events.slice(0, 60).forEach(function (e) {
        opts += '<option value="' + e + '">' + e + '</option>';
    });

    var $filters = $('<div class="mcc-games-filters">'
        + '<div class="mcc-filter-group"><span class="mcc-filter-label">Подія</span>'
        +   '<select class="mcc-filter-select mcc-fe">' + opts + '</select></div>'
        + '<div class="mcc-filter-group"><span class="mcc-filter-label">Роль</span>'
        +   '<select class="mcc-filter-select mcc-fr">'
        +   '<option value="">Всі ролі</option><option value="Мир">Мирний</option>'
        +   '<option value="Шер">Шериф</option><option value="Маф">Мафія</option>'
        +   '<option value="Дон">Дон</option></select></div>'
        + '<div class="mcc-filter-group"><span class="mcc-filter-label">Результат</span>'
        +   '<select class="mcc-filter-select mcc-frr">'
        +   '<option value="">Всі</option>'
        +   '<option value="В">Перемога</option>'
        +   '<option value="П">Поразка</option></select></div>'
        + '<div class="mcc-filter-group mcc-duration-group">'
        +   '<span class="mcc-filter-label">Макс. тривалість</span>'
        +   '<div class="mcc-slider-wrap">'
        +     '<input type="range" class="mcc-duration-range mcc-dur-range" min="20" max="75" value="75" step="1">'
        +     '<span class="mcc-slider-val mcc-dur-val">75:00</span>'
        +   '</div></div>'
        + '<span class="mcc-filter-count mcc-fcount"></span>'
        + '</div>');

    // Insert: before scroll-outer wrapper if exists, else before table
    var $wrap = $table.closest('.mcc-scroll-outer');
    ($wrap.length ? $wrap : $table).before($filters);

    function toMins(s) {
        s = (s || '').trim();
        var m1 = s.match(/^(\d+):(\d+)$/);   if (m1) return +m1[1] + +m1[2]/60;
        var m2 = s.match(/(\d+)\s*хв\s*(\d+)/); if (m2) return +m2[1] + +m2[2]/60;
        return 999;
    }

    function doFilter() {
        var ev  = $filters.find('.mcc-fe').val();
        var rol = $filters.find('.mcc-fr').val();
        var res = $filters.find('.mcc-frr').val();
        var dur = +$filters.find('.mcc-dur-range').val();
        var n = 0;
        $table.find('tbody tr').each(function () {
            var td   = $(this).find('td');
            var show = (!ev  || td.eq(0).text().trim().indexOf(ev)  !== -1)
                    && (!rol || td.eq(1).text().trim().indexOf(rol) !== -1)
                    && (!res || td.eq(3).text().trim().indexOf(res) !== -1)
                    && toMins(td.eq(2).text()) <= dur;
            $(this).toggle(show);
            if (show) n++;
        });
        $filters.find('.mcc-fcount').text(n + '\u00a0ігор');
    }

    $filters.find('.mcc-dur-range').on('input', function () {
        var v = +$(this).val();
        $filters.find('.mcc-dur-val').text(v + ':00');
        $(this).css('background-size', ((v - 20) / 55 * 100) + '% 100%');
        doFilter();
    });

    $filters.find('select').on('change', doFilter);

    $filters.find('.mcc-fcount').text($table.find('tbody tr').length + '\u00a0ігор');
    $filters.find('.mcc-dur-range').css('background-size', '100% 100%');
}

// ============================================================
// 8. ROLE PILLS
// ============================================================
function applyRolePills($context) {
    var $root = $context || $(document);
    var roleMap = {
        // Мирний — light red (city team color)
        'Мир': { bg:'rgba(200,70,70,0.10)',   color:'#e89090', border:'rgba(200,70,70,0.20)'  },
        // Шериф — gold/amber
        'Шер': { bg:'rgba(210,168,60,0.12)',  color:'#d4a843', border:'rgba(210,168,60,0.25)' },
        // Мафія — medium grey
        'Маф': { bg:'rgba(100,100,115,0.18)', color:'#aaa8b8', border:'rgba(120,120,140,0.30)'},
        // Дон — lighter grey
        'Дон': { bg:'rgba(80,80,95,0.14)',    color:'#9896a8', border:'rgba(100,100,120,0.24)'}
    };

    $root.find('.wikitable tbody td').each(function () {
        var $cell = $(this);
        var $span = $cell.find('span').filter(function () {
            return ($(this).attr('style') || '').indexOf('color') !== -1;
        });
        if (!$span.length) return;
        var text  = $span.text().trim();
        var style = roleMap[text];
        if (!style || $span.hasClass('mcc-pill-done')) return;
        $span.addClass('mcc-pill-done').css({
            display:'inline-block', padding:'2px 10px', borderRadius:'4px',
            fontSize:'12.5px', fontWeight:'600', fontFamily:"'Manrope',sans-serif",
            background: style.bg, color: style.color, border:'1px solid '+style.border
        });
    });
}

$(function () { applyRolePills(); });
$(document).on('mcc:content-loaded', function () { applyRolePills(); });

// ============================================================
// 9. АНІМАЦІЯ РЯДКІВ ПРИ СОРТУВАННІ
// ============================================================
$(function () {
    $(document).on('sortEnd', 'table.wikitable', function () {
        var $t = $(this);
        $t.addClass('mcc-sorting').removeClass('mcc-sorted');
        setTimeout(function () {
            $t.removeClass('mcc-sorting').addClass('mcc-sorted');
            setTimeout(function () { $t.removeClass('mcc-sorted'); }, 600);
        }, 20);
    });
});

// ============================================================
// 10. ГОРИЗОНТАЛЬНИЙ СКРОЛ ДЛЯ ШИРОКИХ ТАБЛИЦЬ
// ============================================================
function wrapWideTables($context) {
    var $root = $context || $(document);

    function wrap($table) {
        if ($table.closest('.mcc-scroll-inner').length) return;
        $table.wrap('<div class="mcc-scroll-inner"></div>');
        $table.closest('.mcc-scroll-inner').wrap('<div class="mcc-scroll-outer"></div>');
        var $inner = $table.closest('.mcc-scroll-inner');
        var $outer = $inner.closest('.mcc-scroll-outer');
        function check() {
            $outer.toggleClass('mcc-no-fade',
                $inner[0].scrollLeft + $inner[0].clientWidth >= $inner[0].scrollWidth - 4);
        }
        $inner.on('scroll', check);
        check();
    }

    $root.find('.wikitable.wide-table, .wikitable.mcc-wide, .wikitable.full-width').each(function () {
        wrap($(this));
    });

    $root.find('.wikitable').each(function () {
        var $t = $(this);
        if ($t.closest('.mcc-scroll-inner, .r-box, .tournament-box').length) return;
        if ($t.find('thead tr:first th').length > 8) {
            $t.addClass('mcc-wide-table');
            wrap($t);
        }
    });
}

$(function () {
    // Full-width stat pages — don't wrap tables, let them fill naturally
    if (!isMccFullwidth()) {
        wrapWideTables();
    } else {
        // On full-width pages: CSS handles width and scroll, no JS needed
    }
});

// ============================================================
// 11. ВІНРЕЙТ — кольорові класи
// ============================================================
function applyWinrateColors($context) {
    var $root = $context || $(document);
    $root.find('.wikitable tbody td').each(function () {
        var $c = $(this);
        if ($c.hasClass('wr-hi') || $c.hasClass('wr-lo')) return;
        var m = $c.text().trim().match(/^(\d+(?:\.\d+)?)%$/);
        if (!m) return;
        var v = parseFloat(m[1]);
        if      (v >= 55) $c.addClass('wr-hi');
        else if (v <= 33) $c.addClass('wr-lo');
    });
}

$(function () { applyWinrateColors(); });

// ============================================================
// 12. PLAYER TABS — "(42)" → стилізований badge
// ============================================================
$(function () {
    $('.player-tab').each(function () {
        var $tab = $(this);
        var m = $tab.text().match(/\((\d+)\)/);
        if (!m) return;
        $tab.text($tab.text().replace(/\s*\(\d+\)/, '').trim());
        $tab.append('<span class="mcc-tab-cnt">' + m[1] + '</span>');
    });
});

// ============================================================
// 13. СЕЗОНИ — "Чемпіон" синього кольору
// ============================================================
$(function () {
    function applySeasonColors($ctx) {
        ($ctx || $(document)).find('.wikitable tbody td').each(function () {
            var t = $(this).text().trim();
            if (t === 'Чемпіон' || t === '♔ Чемпіон') {
                $(this).css({ color: '#7ab8ff', fontWeight: '700' });
            }
        });
    }
    applySeasonColors();
    $(document).on('mcc:content-loaded', function () { applySeasonColors(); });
});

// ============================================================
// 13b. DYNAMIC R-BOX TOP — відступ під реальну висоту header
// ============================================================
$(function () {
    function setRboxTop() {
        var $hdr = $('.header-container.header-chrome');
        var h = $hdr.length ? ($hdr.outerHeight() + 4) : 66;
        $('.r-box, .tournament-box, .series-box').css('top', h + 'px');
    }
    setRboxTop();
    setTimeout(setRboxTop, 300);
    $(window).on('resize', setRboxTop);
});

// ============================================================
// 14. NAV <pre> — розгортаємо pre всередині навігації
// ============================================================
$(function () {
    var sel = '.tournament-nav, .series-nav, .player-nav, .championship-nav';
    $(sel).each(function () {
        var $nav = $(this);
        var $pre = $nav.find('pre');
        if (!$pre.length) return;
        $pre.contents().each(function () { $nav.append($(this).clone()); });
        $pre.remove();
    });
});

// ============================================================
// 15. МОБІЛЬНИЙ ПОШУК
// ============================================================
$(function () {
    if ($(window).width() > 768) return;
    if ($('.mobile-search-btn').length) return;

    var $btn = $('<div class="mobile-search-btn">'
        + '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2">'
        + '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg></div>');

    var $overlay = $('<div class="mobile-search-overlay">'
        + '<input type="text" placeholder="Пошук..." autocomplete="off">'
        + '<div class="mobile-search-close">×</div></div>');

    $('.minerva-header .branding-box').after($btn);
    $('body').append($overlay);

    var $input = $overlay.find('input');
    var $close = $overlay.find('.mobile-search-close');

    $btn.on('click', function (e) {
        e.preventDefault(); e.stopPropagation();
        $overlay.addClass('active');
        setTimeout(function () { $input.focus(); }, 100);
    });
    $close.on('click', function () { $overlay.removeClass('active'); $input.val(''); });
    $input.on('keydown', function (e) {
        if (e.keyCode === 13) {
            var q = $input.val().trim();
            if (q) window.location.href = '/index.php?title=Спеціальна:Пошук&search=' + encodeURIComponent(q);
        }
        if (e.keyCode === 27) { $overlay.removeClass('active'); $input.val(''); }
    });
    $overlay.on('click', function (e) {
        if (e.target === this) { $overlay.removeClass('active'); $input.val(''); }
    });
});

// ============================================================
// 16. БЛОКУВАННЯ MINERVA SEARCH OVERLAY (ДЕСКТОП)
// ============================================================
$(function () {
    if ($(window).width() <= 768) return;

    var $searchInput = $('#searchInput');
    if ($searchInput.length === 0) return;

    setTimeout(function () {
        $searchInput.attr('placeholder', 'Пошук...');
        var $new = $searchInput.clone(false);
        $searchInput.replaceWith($new);
        $searchInput = $new;

        $searchInput.prop('readonly', false).removeAttr('readonly')
            .removeClass('skin-minerva-search-trigger');

        $searchInput.on('focus click', function (e) {
            e.stopPropagation();
            $('body').removeClass('overlay-enabled search-enabled');
            $('.overlay, .search-overlay').hide();
            if (window.location.hash === '#/search')
                history.replaceState(null, null, window.location.pathname);
        });

        $searchInput.on('keydown', function (e) {
            if (e.which === 13) {
                e.preventDefault();
                var q = $(this).val().trim();
                if (q) window.location.href = '/index.php?title=Спеціальна:Пошук&search=' + encodeURIComponent(q);
                return false;
            }
        });
    }, 100);

    $(window).on('hashchange', function () {
        if (window.location.hash === '#/search') {
            history.replaceState(null, null, window.location.pathname);
            $('body').removeClass('overlay-enabled search-enabled');
            $('.overlay, .search-overlay').hide();
        }
    });

    if (window.location.hash === '#/search')
        history.replaceState(null, null, window.location.pathname);
});


// ============================================================
// FILTERS FOR SEASON "Запис ігор" WIDE TABLE
// Only runs on season/tournament pages (not full-width stat pages)
// ============================================================
$(function () {
    // Skip on full-width stat pages
    if (isMccFullwidth()) return;

    var $table = $('.wikitable.wide-table').first();
    if (!$table.length) $table = $('.wikitable.full-width').first();
    if (!$table.length) return;

    var $rows = $table.find('tr').filter(function () {
        return $(this).find('td').length > 0;
    });
    if (!$rows.length) return;

    var results = [];
    $rows.each(function () {
        var tds = $(this).find('td');
        var r = tds.eq(tds.length - 2).text().trim();
        if (r && results.indexOf(r) === -1) results.push(r);
    });

    var resOpts = '<option value="">Всі результати</option>';
    results.forEach(function (r) {
        resOpts += '<option value="' + r + '">' + r + '</option>';
    });

    var $bar = $(
        '<div class="mcc-games-filters">' +
        '<div class="mcc-filter-group">' +
        '<span class="mcc-filter-label">Результат</span>' +
        '<select class="mcc-filter-select sg-res">' + resOpts + '</select>' +
        '</div>' +
        '<div class="mcc-filter-group mcc-duration-group">' +
        '<span class="mcc-filter-label">Макс. тривалість</span>' +
        '<div class="mcc-slider-wrap">' +
        '<input type="range" class="mcc-duration-range sg-dur" min="30" max="65" value="65" step="1">' +
        '<span class="mcc-slider-val sg-durv">65:00</span>' +
        '</div>' +
        '</div>' +
        '<span class="mcc-filter-count sg-cnt"></span>' +
        '</div>'
    );

    var $target = $table.closest('.mcc-scroll-outer, .mcc-scroll-inner');
    ($target.length ? $target : $table).before($bar);

    function toMins(s) {
        s = (s || '').trim();
        var m = s.match(/(\d+)\s*хв\s*(\d+)/);
        if (m) return parseInt(m[1]) + parseInt(m[2]) / 60;
        m = s.match(/^(\d+):(\d+)$/);
        if (m) return parseInt(m[1]) + parseInt(m[2]) / 60;
        return 999;
    }

    function doFilter() {
        var res = $bar.find('.sg-res').val();
        var dur = parseInt($bar.find('.sg-dur').val());
        var n = 0;
        $rows.each(function () {
            var tds = $(this).find('td');
            var timeVal = tds.eq(0).text().trim();
            var resVal  = tds.eq(tds.length - 2).text().trim();
            var show = (!res || resVal === res) && toMins(timeVal) <= dur;
            $(this).toggle(show);
            if (show) n++;
        });
        $bar.find('.sg-cnt').text(n + '\u00a0ігор');
    }

    $bar.find('.sg-dur').on('input', function () {
        var v = parseInt($(this).val());
        $bar.find('.sg-durv').text(v + ':00');
        $(this).css('background-size', ((v - 30) / 35 * 100) + '% 100%');
        doFilter();
    });
    $bar.find('.sg-res').on('change', doFilter);
    $bar.find('.sg-cnt').text($rows.length + '\u00a0ігор');
    $bar.find('.sg-dur').css('background-size', '100% 100%');
});


// ============================================================
// 17. BANNER SEARCH

// ============================================================
$(function () {

    // ── MEDALS helper — adds gold/silver/bronze circles to first-column cells ──
    function addMedals($t) {
        $t.find('tbody tr, tr').filter(function () {
            return $(this).find('td').length >= 2;
        }).each(function () {
            var $tds = $(this).find('td');
            var raw = $tds.eq(0).text().trim();
            var n = parseInt(raw);
            if (isNaN(n) || n < 1 || n > 3) return;
            if ($tds.eq(0).find('[class^="mcc-rank"]').length) return;
            $tds.eq(0).html('<span class="mcc-rank-' + n + '">' + n + '</span>');
        });
    }

    // ── Tables that need medals: closedcups, rank, getnames, wide rank (Період/Фіналіст) ──
    $('.content .mw-parser-output .wikitable').each(function () {
        var $t = $(this);
        if ($t.hasClass('mcc-rating-table')) return;

        var $ths = $t.find('tr').first().find('th');
        if (!$ths.length) return;
        var h0 = $ths.eq(0).text().trim();

        // Wide rank tables: № + more than 5 columns (Період, Фіналіст)
        if (h0.indexOf('№') !== -1 && $ths.length > 5) {
            addMedals($t);
            return;
        }

        // Explicit class-based
        if ($t.hasClass('closedcups') || $t.hasClass('rank')) {
            addMedals($t);
        }
    });

    // ── Get Names tables — auto-number ──
    $('.content .mw-parser-output .wikitable.getnames').each(function () {
        var $t = $(this);
        var $headerRow = $t.find('tr').first();
        if ($headerRow.find('th').first().text().trim() === '№') return;
        $headerRow.prepend('<th style="width:38px;text-align:center">№</th>');

        var n = 1;
        $t.find('tbody tr, tr').filter(function () {
            return $(this).find('td').length > 0;
        }).each(function () {
            var badge = n <= 3
                ? '<span class="mcc-rank-' + n + '">' + n + '</span>'
                : ('<span style="display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;font-size:12px;font-weight:600">' + n + '</span>');
            $(this).prepend('<td style="text-align:center;padding:10px 8px">' + badge + '</td>');
            n++;
        });
    });

    // ── Season final table — auto-number (Пан/Пані | І | П | ДБ | Дія | Σ) ──
    $('.content .mw-parser-output .wikitable').each(function () {
        var $t = $(this);
        if ($t.hasClass('mcc-rating-table') || $t.hasClass('mcc-final-table')) return;
        var $ths = $t.find('tr').first().find('th');
        if (!$ths.length) return;
        var h0 = $ths.eq(0).text().trim();
        var hLast = $ths.last().text().trim();
        var isFinal = (h0 === 'Пан/Пані' || h0 === 'Пан' || h0 === 'Гравець')
                   && hLast === 'Σ';
        if (!isFinal) return;

        $t.addClass('mcc-final-table');
        $t.find('tr').first().prepend('<th style="width:36px;text-align:center">#</th>');
        var n = 1;
        $t.find('tbody tr').each(function () {
            var $tds = $(this).find('td');
            if (!$tds.length) return;
            var badge = n <= 3
                ? '<span class="mcc-rank-' + n + '">' + n + '</span>'
                : n;
            $(this).prepend('<td style="text-align:center;padding:10px 8px">' + badge + '</td>');
            n++;
        });
    });

});

// ============================================================
// SEASON RATING TABLE — medals + color extremes for №|Пан/Пані|Σ|І|%
// ============================================================
$(function () {
    if (isMccFullwidth()) return;

    function processRatingTable($t) {
        var headers = [];
        $t.find('tr').first().find('th').each(function () {
            headers.push($(this).text().trim());
        });
        if (headers[0] !== '№') return;
        if (headers.length < 4) return;
        var hasSigma = (headers[2] && headers[2].indexOf('Σ') !== -1);
        if (!hasSigma) return;
        if (headers.length > 8) return; // wide tables handled elsewhere

        var iGames = 3;
        var iPct   = 4;
        var hasI   = headers[iGames] === 'І';
        var hasPct = headers[iPct]   === '%';

        $t.addClass('mcc-rating-table');

        var $rows = $t.find('tbody tr').filter(function () {
            return $(this).find('td').length >= 4;
        });
        if (!$rows.length) return;

        // Medals top 3
        $rows.each(function () {
            var $tds = $(this).find('td');
            var n = parseInt($tds.eq(0).text().trim());
            if (n >= 1 && n <= 3) {
                $tds.eq(0).html('<span class="mcc-rank-' + n + '">' + n + '</span>');
            }
        });

        // Σ white
        $rows.each(function () {
            $(this).find('td').eq(2).css({ color: '#ffffff', fontWeight: '700' });
        });

        // Games extremes
        if (hasI) {
            var gVals = [];
            $rows.each(function () {
                var v = parseInt($(this).find('td').eq(iGames).text().trim());
                if (!isNaN(v) && v > 0) gVals.push(v);
            });
            if (gVals.length > 1) {
                var gMax = Math.max.apply(null, gVals);
                var gMin = Math.min.apply(null, gVals);
                $rows.each(function () {
                    var $td = $(this).find('td').eq(iGames);
                    var v = parseInt($td.text().trim());
                    if (isNaN(v)) return;
                    if (v === gMax) $td.addClass('mcc-games-hi');
                    else if (v === gMin) $td.addClass('mcc-games-lo');
                });
            }
        }

        // WR extremes
        if (hasPct) {
            var pVals = [];
            $rows.each(function () {
                var v = parseFloat($(this).find('td').eq(iPct).text().trim().replace('%',''));
                if (!isNaN(v)) pVals.push(v);
            });
            if (pVals.length > 1) {
                var pMax = Math.max.apply(null, pVals);
                var pMin = Math.min.apply(null, pVals);
                $rows.each(function () {
                    var $td = $(this).find('td').eq(iPct);
                    var v = parseFloat($td.text().trim().replace('%',''));
                    if (isNaN(v)) return;
                    if (v === pMax) $td.addClass('mcc-wr-hi');
                    else if (v === pMin) $td.addClass('mcc-wr-lo');
                });
            }
        }
    }

    $('.content .mw-parser-output .wikitable').each(function () {
        processRatingTable($(this));
    });
});

// ============================================================
// PLAYER GAMES FILTERS — pre-rendered tab (server-side FetchData)
// ============================================================
$(function () {
    var $content = $('#tab-games');
    if (!$content.length) return;
    var $table = $content.find('table').first();
    if (!$table.length) return;
    applyWinrateColors($content);
    applyRolePills($content);
    buildGamesFilters($content, $table);
});

// ============================================================
// 17. BANNER SEARCH
// ============================================================
$(function () {
    var $searchInput = $('#searchInput');
    if (!$searchInput.length) return;

    $searchInput.on('keydown', function (e) {
        if (e.key === 'Enter') {
            var q = $(this).val().trim();
            if (q) window.location.href = '/index.php?search=' + encodeURIComponent(q);
        }
    });
});

// ============================================================
// HOME PAGE — collapsible panels + clickable rows
// ============================================================
$(function () {
    // Expand/collapse
   var GRID_PANELS = ['seasons-panel', 'gn-panel', 'closed-panel'];

$('[data-toggle]').on('click', function () {
    var $hd = $(this);
    var targetId = $hd.data('toggle');
    var $bd = $('#' + targetId);
    if (!$bd.length) return;

    var isGridPanel = GRID_PANELS.indexOf(targetId) !== -1;
    var isDesktop = $(window).width() >= 1000;

    if (isGridPanel && isDesktop) {
        // Collapse/expand all 3 grid panels together
        var collapsed = $bd.hasClass('collapsed');
        GRID_PANELS.forEach(function(id) {
            $('#' + id).toggleClass('collapsed', !collapsed);
            $('[data-toggle="' + id + '"]').toggleClass('collapsed', !collapsed);
        });
    } else {
        var collapsed = $bd.hasClass('collapsed');
        $bd.toggleClass('collapsed', !collapsed);
        $hd.toggleClass('collapsed', !collapsed);
    }
});

    // Clickable rows / cards — navigate on click, ignore inner link clicks
    $(document).on('click', '.mcc-nav', function (e) {
        if ($(e.target).closest('a').length) return; // let wiki links work normally
        var href = $(this).data('href');
        if (href) window.location.href = href;
    });

    // Cursor pointer for nav elements
    $('.mcc-nav').css('cursor', 'pointer');
});