MediaWiki:Common.js: відмінності між версіями

нема опису редагування
Немає опису редагування
Немає опису редагування
Рядок 1: Рядок 1:
// ============================================================
// ============================================================
// MediaWiki:Common.js — Mafia Closed Circle — ФІНАЛЬНА ВЕРСІЯ
// MediaWiki:Common.js — Mafia Closed Circle
// ============================================================
// ============================================================


Рядок 20: Рядок 20:
     return false;
     return false;
}
}


// ============================================================
// ============================================================
// 1. RANDOM ARTICLES
// 1. RANDOM ARTICLES
// ============================================================
// ============================================================
$(document).ready(function () {
$(function () {
    if (!$('#random-articles-container').length) return;
 
     var apiUrl = mw.config.get('wgScriptPath') + '/api.php';
     var apiUrl = mw.config.get('wgScriptPath') + '/api.php';
     $.getJSON(apiUrl, {
     $.getJSON(apiUrl, {
Рядок 33: Рядок 34:
         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/'
             html += '<div class="random-article-preview"><h2><a href="/index.php/'
                 + encodeURIComponent(article.title) + '">' + article.title + '</a></h2></div>';
                 + encodeURIComponent(article.title) + '">' + article.title + '</a></h2></div>';
         });
         });
Рядок 49: Рядок 50:
     var $items = $lbox.find('.l-box-item');
     var $items = $lbox.find('.l-box-item');
     var HEADER_OFFSET = 80;
     var HEADER_OFFSET = 80;
     var DURATION = 320;         // фіксована тривалість, мс
     var DURATION = 300;             // фіксована тривалість, мс
    var isAnimating = false;
     var rafId = null;
     var rafId = null;
    var suppressSpyUntil = 0;        // час, до якого скрол-шпигун мовчить


     function getTargetEl(target) {
     function getTargetEl(target) {
         if (target === 'top') return null;
         if (!target || target === 'top') return null;
         var el = document.getElementById(target);
         var el = document.getElementById(target);
         if (!el) return null;
         if (!el) return null;
Рядок 66: Рядок 67:
         if (!el) return null;
         if (!el) return null;
         var top = el.getBoundingClientRect().top + window.pageYOffset - HEADER_OFFSET;
         var top = el.getBoundingClientRect().top + window.pageYOffset - HEADER_OFFSET;
        // не даємо вийти за межі документа
         var max = Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
         var max = document.documentElement.scrollHeight - window.innerHeight;
         return Math.max(0, Math.min(top, max));
         return Math.max(0, Math.min(top, max));
     }
     }
Рядок 73: Рядок 73:
     function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); }
     function easeOutCubic(t) { return 1 - Math.pow(1 - t, 3); }


    function scrollToTarget(target) {
    function scrollToTarget(target) {
        // Перериваємо попередню анімацію, якщо вона ще йде
        if (rafId) { cancelAnimationFrame(rafId); rafId = null; }
 
        var start = window.pageYOffset;
         var end = getTargetTop(target);
         var end = getTargetTop(target);
         if (end === null) return;
         if (end === null) return;
         window.scrollTo(0, end);
 
         setTimeout(updateActive, 50);
        if (Math.abs(end - start) < 2) return;
 
        suppressSpyUntil = Date.now() + DURATION + 200;
         var startTime = null;
 
        function frame(now) {
            if (startTime === null) startTime = now;
            var t = Math.min(1, (now - startTime) / DURATION);
 
            // Переміряємо ціль щокадру — layout міг зсунутись
            var currentEnd = getTargetTop(target);
            if (currentEnd === null) currentEnd = end;
 
            window.scrollTo(0, Math.round(start + (currentEnd - start) * easeOutCubic(t)));
 
            if (t < 1) {
                rafId = requestAnimationFrame(frame);
            } else {
                rafId = null;
                // Фінальна корекція, якщо контент зсунувся під час руху
                var finalTop = getTargetTop(target);
                if (finalTop !== null && Math.abs(finalTop - window.pageYOffset) > 3) {
                    window.scrollTo(0, finalTop);
                }
                suppressSpyUntil = Date.now() + 100;
            }
        }
 
         rafId = requestAnimationFrame(frame);
     }
     }


     function updateActive() {
     function updateActive() {
         if (isAnimating) return;
         if (Date.now() < suppressSpyUntil) return;
 
         var scrollY = window.pageYOffset;
         var scrollY = window.pageYOffset;
         var current = null;
         var current = null;
Рядок 87: Рядок 120:
         $items.each(function () {
         $items.each(function () {
             var top = getTargetTop($(this).data('target'));
             var top = getTargetTop($(this).data('target'));
             if (top !== null && top <= scrollY + 10) current = $(this);
             if (top !== null && top <= scrollY + 12) current = $(this);
         });
         });


Рядок 95: Рядок 128:


     $items.on('click', function () {
     $items.on('click', function () {
         // Блокуємо повторний клік на вже активному пункті
         var target = $(this).data('target');
        if ($(this).hasClass('active')) return;
        if (isAnimating) return;


        var target = $(this).data('target');
         $items.removeClass('active');
         $items.removeClass('active');
         $(this).addClass('active');
         $(this).addClass('active');
Рядок 126: Рядок 156:


     $(window).on('resize', updateActive);
     $(window).on('resize', updateActive);
    $(window).on('load', updateActive);
     $(document).on('mcc:content-loaded', updateActive);
     $(document).on('mcc:content-loaded', updateActive);
    $(window).on('load', updateActive);
     if (document.fonts && document.fonts.ready) {
     if (document.fonts && document.fonts.ready) {
         document.fonts.ready.then(updateActive);
         document.fonts.ready.then(updateActive);
Рядок 138: Рядок 168:
// 3. PROFILE ICONS — клік + inactive + розгортання <p>
// 3. PROFILE ICONS — клік + inactive + розгортання <p>
// ============================================================
// ============================================================
$(document).ready(function () {
$(function () {
    // Розгортаємо <p> яку MediaWiki вставляє всередину grid
     function fixProfileGrid() {
     function fixProfileGrid() {
         $('.profile-links-icons').each(function () {
         $('.profile-links-icons').each(function () {
Рядок 169: Рядок 198:
     setTimeout(fixProfileGrid, 400);
     setTimeout(fixProfileGrid, 400);


    // Клік + inactive
     $('.profile-icon').each(function () {
     $('.profile-icon').each(function () {
         var $icon = $(this);
         var $icon = $(this);
Рядок 218: Рядок 246:
// 5. ДИНАМІЧНІ ПОСИЛАННЯ НА ГОЛОВНІЙ
// 5. ДИНАМІЧНІ ПОСИЛАННЯ НА ГОЛОВНІЙ
// ============================================================
// ============================================================
$(document).ready(function () {
$(function () {
     if (!$('body').hasClass('page-Головна_сторінка')) return;
     if (!$('body').hasClass('page-Головна_сторінка')) return;


Рядок 246: Рядок 274:
// 6. PLAYER TABS + LAZY LOADING
// 6. PLAYER TABS + LAZY LOADING
// ============================================================
// ============================================================
// Глобальна функція перемикання табу (використовується і в l-box)
function activatePlayerTab(tabId) {
function activatePlayerTab(tabId) {
     var $tabs    = $('.player-tab');
     var $tabs    = $('.player-tab');
Рядок 272: Рядок 298:
         activatePlayerTab(tabId);
         activatePlayerTab(tabId);


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


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


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


            $.ajax({
        $.ajax({
                url: mw.config.get('wgScriptPath') + '/api.php',
            url: mw.config.get('wgScriptPath') + '/api.php',
                data: {
            data: {
                    action: 'expandtemplates', format: 'json',
                action: 'expandtemplates', format: 'json',
                    text: '{{#invoke:FetchData|player_games|player=' + playerName + '}}',
                text: '{{#invoke:FetchData|player_games|player=' + playerName + '}}',
                    prop: 'wikitext'
                prop: 'wikitext'
                },
            },
                dataType: 'json',
            dataType: 'json',
                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',
                        data: {
                    data: {
                            action: 'parse', format: 'json',
                        action: 'parse', format: 'json',
                            text: response.expandtemplates.wikitext,
                        text: response.expandtemplates.wikitext,
                            contentmodel: 'wikitext',
                        contentmodel: 'wikitext',
                            disablelimitreport: true
                        disablelimitreport: true
                        },
                    },
                        dataType: 'json',
                    dataType: 'json',
                        success: function (r) {
                    success: function (r) {
                            if (!r.parse || !r.parse.text) return;
                        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);


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


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


Рядок 340: Рядок 365:


// ============================================================
// ============================================================
// 7. ФІЛЬТРИ НАД ТАБЛИЦЕЮ ІГОР
// 7. ФІЛЬТРИ НАД ТАБЛИЦЕЮ ІГОР ГРАВЦЯ
// ============================================================
// 7b. BUILD GAMES FILTERS
// ============================================================
// ============================================================
function buildGamesFilters($container, $table) {
function buildGamesFilters($container, $table) {
    // Already added?
     if ($container.find('.mcc-games-filters').length) return;
     if ($container.find('.mcc-games-filters').length) return;


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


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


    // Collect unique events from first td
     var events = [];
     var events = [];
     $rows.each(function () {
     $rows.each(function () {
Рядок 369: Рядок 384:
     });
     });


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


    // Build the filter bar HTML
     var barHTML = '<div class="mcc-games-filters">'
     var barHTML = '<div class="mcc-games-filters">'
         + '<div class="mcc-filter-group">'
         + '<div class="mcc-filter-group">'
Рядок 409: Рядок 422:
         + '</div>';
         + '</div>';


    // Insert at the very top of the container
     $container.prepend(barHTML);
     $container.prepend(barHTML);
    // Get reference to the just-added bar
     var $bar = $container.find('.mcc-games-filters').first();
     var $bar = $container.find('.mcc-games-filters').first();


Рядок 419: Рядок 429:
         var m1 = s.match(/^(\d+):(\d+)$/);
         var m1 = s.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 = s.match(/(\d+)\s*[хh]\s*[вv]\s*(\d+)/);
         var m2 = s.match(/(\d+)\s*хв\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;
Рядок 433: Рядок 443:
         $rows.each(function () {
         $rows.each(function () {
             var $tds = $(this).find('td');
             var $tds = $(this).find('td');
             var evVal  = $tds.eq(0).text().trim();
             var show = (ev === '' || $tds.eq(0).text().trim().indexOf(ev) !== -1)
            var rolVal = $tds.eq(1).text().trim();
                     && (rol === '' || $tds.eq(1).text().trim().indexOf(rol) !== -1)
            var timeVal = $tds.eq(2).text().trim();
                     && (res === '' || $tds.eq(3).text().trim().indexOf(res) !== -1)
            var resVal  = $tds.eq(3).text().trim();
                     && toMins($tds.eq(2).text()) <= dur;
 
            var show = (ev  === '' || evVal.indexOf(ev)   !== -1)
                     && (rol === '' || rolVal.indexOf(rol) !== -1)
                     && (res === '' || resVal.indexOf(res) !== -1)
                     && toMins(timeVal) <= dur;
 
             $(this).toggle(show);
             $(this).toggle(show);
             if (show) n++;
             if (show) n++;
Рядок 459: Рядок 463:
     $bar.find('.mcc-fcnt').text($rows.length + '\u00a0ігор');
     $bar.find('.mcc-fcnt').text($rows.length + '\u00a0ігор');
     $bar.find('.mcc-dur-rng').css('background-size', '100% 100%');
     $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%');
}
}


Рядок 554: Рядок 471:
     var $root = $context || $(document);
     var $root = $context || $(document);
     var roleMap = {
     var roleMap = {
        // Мирний — light red (city team color)
         'Мир': { bg:'rgba(200,70,70,0.10)',  color:'#e89090', border:'rgba(200,70,70,0.20)'  },
         'Мир': { 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)' },
         'Шер': { 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)'},
         'Маф': { 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)'}
         'Дон': { 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 $span = $(this).find('span').filter(function () {
        var $span = $cell.find('span').filter(function () {
             return ($(this).attr('style') || '').indexOf('color') !== -1;
             return ($(this).attr('style') || '').indexOf('color') !== -1;
         });
         });
         if (!$span.length) return;
         if (!$span.length) return;
         var text  = $span.text().trim();
         var style = roleMap[$span.text().trim()];
        var style = roleMap[text];
         if (!style || $span.hasClass('mcc-pill-done')) return;
         if (!style || $span.hasClass('mcc-pill-done')) return;
         $span.addClass('mcc-pill-done').css({
         $span.addClass('mcc-pill-done').css({
             display:'inline-block', padding:'2px 10px', borderRadius:'4px',
             display:'inline-block', padding:'2px 10px', borderRadius:'4px',
             fontSize:'12.5px', fontWeight:'600', fontFamily:"'Manrope',sans-serif",
             fontSize:'12.5px', fontWeight:'600', fontFamily:"'Manrope',sans-serif",
             background: style.bg, color: style.color, border:'1px solid '+style.border
             background: style.bg, color: style.color, border:'1px solid ' + style.border
         });
         });
     });
     });
Рядок 633: Рядок 544:


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


Рядок 689: Рядок 595:


// ============================================================
// ============================================================
// 13b. DYNAMIC R-BOX TOP — відступ під реальну висоту header
// 14. DYNAMIC R-BOX TOP — відступ під реальну висоту header
// ============================================================
// ============================================================
$(function () {
$(function () {
Рядок 703: Рядок 609:


// ============================================================
// ============================================================
// 14. NAV <pre> — розгортаємо pre всередині навігації
// 15. NAV <pre> — розгортаємо pre всередині навігації
// ============================================================
// ============================================================
$(function () {
$(function () {
Рядок 717: Рядок 623:


// ============================================================
// ============================================================
// 15. МОБІЛЬНИЙ ПОШУК
// 16. МОБІЛЬНИЙ ПОШУК
// ============================================================
// ============================================================
$(function () {
$(function () {
Рядок 756: Рядок 662:


// ============================================================
// ============================================================
// 16. БЛОКУВАННЯ MINERVA SEARCH OVERLAY (ДЕСКТОП)
// 17. БЛОКУВАННЯ MINERVA SEARCH OVERLAY (ДЕСКТОП)
// ============================================================
// ============================================================
$(function () {
$(function () {
Рядок 802: Рядок 708:
         history.replaceState(null, null, window.location.pathname);
         history.replaceState(null, null, window.location.pathname);
});
});


// ============================================================
// ============================================================
// FILTERS FOR SEASON "Запис ігор" WIDE TABLE
// 18. ФІЛЬТРИ ДЛЯ ШИРОКОЇ ТАБЛИЦІ "ЗАПИС ІГОР" НА СТОРІНКАХ СЕЗОНІВ
// Only runs on season/tournament pages (not full-width stat pages)
// ============================================================
// ============================================================
$(function () {
$(function () {
    // Skip on full-width stat pages
     if (isMccFullwidth()) return;
     if (isMccFullwidth()) return;


Рядок 832: Рядок 735:
         resOpts += '<option value="' + r + '">' + r + '</option>';
         resOpts += '<option value="' + r + '">' + r + '</option>';
     });
     });
    var MIN_DUR = 25, MAX_DUR = 75;


     var $bar = $(
     var $bar = $(
Рядок 842: Рядок 747:
         '<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 sg-dur" min="30" max="65" value="65" step="1">' +
         '<input type="range" class="mcc-duration-range sg-dur" min="' + MIN_DUR + '" max="' + MAX_DUR + '" value="' + MAX_DUR + '" step="1">' +
         '<span class="mcc-slider-val sg-durv">65:00</span>' +
         '<span class="mcc-slider-val sg-durv">' + MAX_DUR + ':00</span>' +
         '</div>' +
         '</div>' +
         '</div>' +
         '</div>' +
Рядок 868: Рядок 773:
         $rows.each(function () {
         $rows.each(function () {
             var tds = $(this).find('td');
             var tds = $(this).find('td');
             var timeVal = tds.eq(0).text().trim();
             var show = (!res || tds.eq(tds.length - 2).text().trim() === res)
            var resVal  = tds.eq(tds.length - 2).text().trim();
                    && toMins(tds.eq(0).text()) <= dur;
            var show = (!res || resVal === res) && toMins(timeVal) <= dur;
             $(this).toggle(show);
             $(this).toggle(show);
             if (show) n++;
             if (show) n++;
Рядок 880: Рядок 784:
         var v = parseInt($(this).val());
         var v = parseInt($(this).val());
         $bar.find('.sg-durv').text(v + ':00');
         $bar.find('.sg-durv').text(v + ':00');
         $(this).css('background-size', ((v - 30) / 35 * 100) + '% 100%');
         $(this).css('background-size', ((v - MIN_DUR) / (MAX_DUR - MIN_DUR) * 100) + '% 100%');
         doFilter();
         doFilter();
     });
     });
Рядок 887: Рядок 791:
     $bar.find('.sg-dur').css('background-size', '100% 100%');
     $bar.find('.sg-dur').css('background-size', '100% 100%');
});
});


// ============================================================
// ============================================================
// 17. BANNER SEARCH
// 19. МЕДАЛІ ТА АВТОНУМЕРАЦІЯ В ТАБЛИЦЯХ
 
// ============================================================
// ============================================================
$(function () {
$(function () {


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


     // ── Tables that need medals: closedcups, rank, getnames, wide rank (Період/Фіналіст) ──
     // Таблиці з медалями: closedcups, rank, широкі рейтингові (Період / Фіналіст)
     $('.content .mw-parser-output .wikitable').each(function () {
     $('.content .mw-parser-output .wikitable').each(function () {
         var $t = $(this);
         var $t = $(this);
Рядок 916: Рядок 816:
         var $ths = $t.find('tr').first().find('th');
         var $ths = $t.find('tr').first().find('th');
         if (!$ths.length) return;
         if (!$ths.length) return;
        var h0 = $ths.eq(0).text().trim();


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


     // ── Get Names tables auto-number ──
     // Get Names — автонумерація
     $('.content .mw-parser-output .wikitable.getnames').each(function () {
     $('.content .mw-parser-output .wikitable.getnames').each(function () {
         var $t = $(this);
         var $t = $(this);
Рядок 943: Рядок 837:
             var badge = n <= 3
             var badge = n <= 3
                 ? '<span class="mcc-rank-' + n + '">' + n + '</span>'
                 ? '<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>');
                 : '<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>');
             $(this).prepend('<td style="text-align:center;padding:10px 8px">' + badge + '</td>');
             n++;
             n++;
Рядок 949: Рядок 843:
     });
     });


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


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


// ============================================================
// ============================================================
// SEASON RATING TABLE medals + color extremes for №|Пан/Пані|Σ|І|%
// 20. РЕЙТИНГОВА ТАБЛИЦЯ СЕЗОНУ медалі та екстремуми
// ============================================================
// ============================================================
$(function () {
$(function () {
Рядок 988: Рядок 880:
             headers.push($(this).text().trim());
             headers.push($(this).text().trim());
         });
         });
         if (headers[0] !== '№') return;
         if (headers[0] !== '№') return;
         if (headers.length < 4) return;
         if (headers.length < 4 || headers.length > 8) return;
         var hasSigma = (headers[2] && headers[2].indexOf('Σ') !== -1);
         if (!headers[2] || headers[2].indexOf('Σ') === -1) return;
        if (!hasSigma) return;
        if (headers.length > 8) return; // wide tables handled elsewhere


         var iGames = 3;
         var iGames = 3, iPct = 4;
        var iPct   = 4;
         var hasI  = headers[iGames] === 'І';
         var hasI  = headers[iGames] === 'І';
         var hasPct = headers[iPct]  === '%';
         var hasPct = headers[iPct]  === '%';
Рядок 1006: Рядок 896:
         if (!$rows.length) return;
         if (!$rows.length) return;


        // Medals top 3
         $rows.each(function () {
         $rows.each(function () {
             var $tds = $(this).find('td');
             var $tds = $(this).find('td');
Рядок 1013: Рядок 902:
                 $tds.eq(0).html('<span class="mcc-rank-' + n + '">' + n + '</span>');
                 $tds.eq(0).html('<span class="mcc-rank-' + n + '">' + n + '</span>');
             }
             }
            $tds.eq(2).css({ color: '#ffffff', fontWeight: '700' });
         });
         });


        // Σ white
        $rows.each(function () {
            $(this).find('td').eq(2).css({ color: '#ffffff', fontWeight: '700' });
        });
        // Games extremes
         if (hasI) {
         if (hasI) {
             var gVals = [];
             var gVals = [];
Рядок 1040: Рядок 924:
         }
         }


        // WR extremes
         if (hasPct) {
         if (hasPct) {
             var pVals = [];
             var pVals = [];
             $rows.each(function () {
             $rows.each(function () {
                 var v = parseFloat($(this).find('td').eq(iPct).text().trim().replace('%',''));
                 var v = parseFloat($(this).find('td').eq(iPct).text().trim().replace('%', ''));
                 if (!isNaN(v)) pVals.push(v);
                 if (!isNaN(v)) pVals.push(v);
             });
             });
Рядок 1052: Рядок 935:
                 $rows.each(function () {
                 $rows.each(function () {
                     var $td = $(this).find('td').eq(iPct);
                     var $td = $(this).find('td').eq(iPct);
                     var v = parseFloat($td.text().trim().replace('%',''));
                     var v = parseFloat($td.text().trim().replace('%', ''));
                     if (isNaN(v)) return;
                     if (isNaN(v)) return;
                     if (v === pMax) $td.addClass('mcc-wr-hi');
                     if (v === pMax) $td.addClass('mcc-wr-hi');
Рядок 1067: Рядок 950:


// ============================================================
// ============================================================
// PLAYER GAMES FILTERS pre-rendered tab (server-side FetchData)
// 21. ВКЛАДКА "ЗАПИСИ ІГОР" якщо відрендерена сервером
// ============================================================
// ============================================================
$(function () {
$(function () {
Рядок 1074: Рядок 957:
     var $table = $content.find('table').first();
     var $table = $content.find('table').first();
     if (!$table.length) return;
     if (!$table.length) return;
     applyWinrateColors($content);
     applyWinrateColors($content);
     applyRolePills($content);
     applyRolePills($content);
Рядок 1080: Рядок 964:


// ============================================================
// ============================================================
// 17. BANNER SEARCH
// 22. ГОЛОВНА СТОРІНКА — панелі та клікабельні рядки
// ============================================================
// ============================================================
$(function () {
$(function () {
     var $searchInput = $('#searchInput');
     var GRID_PANELS = ['seasons-panel', 'gn-panel', 'closed-panel'];
    if (!$searchInput.length) return;
 
    $('[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;
        var collapsed = $bd.hasClass('collapsed');


    $searchInput.on('keydown', function (e) {
        if (isGridPanel && isDesktop) {
        if (e.key === 'Enter') {
            GRID_PANELS.forEach(function (id) {
            var q = $(this).val().trim();
                $('#' + id).toggleClass('collapsed', !collapsed);
             if (q) window.location.href = '/index.php?search=' + encodeURIComponent(q);
                $('[data-toggle="' + id + '"]').toggleClass('collapsed', !collapsed);
            });
        } else {
            $bd.toggleClass('collapsed', !collapsed);
             $hd.toggleClass('collapsed', !collapsed);
         }
         }
     });
     });
});
// ============================================================
// 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) {
     $(document).on('click', '.mcc-nav', function (e) {
         if ($(e.target).closest('a').length) return; // let wiki links work normally
         if ($(e.target).closest('a').length) return;
         var href = $(this).data('href');
         var href = $(this).data('href');
         if (href) window.location.href = href;
         if (href) window.location.href = href;
     });
     });


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