MediaWiki:Common.js: відмінності між версіями
Admin (обговорення | внесок) Немає опису редагування |
Admin (обговорення | внесок) Немає опису редагування |
||
| Рядок 421: | Рядок 421: | ||
// ============================================================ | // ============================================================ | ||
// 11. BANNER SEARCH | // 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 — ФІНАЛЬНА ВЕРСІЯ | |||
// ============================================================ | |||
// ============================================================ | |||
// 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 { | |||
// ФІКС: document.getElementById коректно обробляє кирилицю | |||
var el = document.getElementById(target); | |||
if (el) { | |||
var $el = $(el); | |||
// Якщо el — це span.mw-headline, беремо батьківський h2 | |||
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'); | |||
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; | |||
var offset = $heading.offset().top - 78; | |||
$('html, body').animate({ scrollTop: offset }, 280); | |||
} | |||
} | |||
// Активуємо одразу при кліку, не чекаємо скрол | |||
$items.removeClass('active'); | |||
$(this).addClass('active'); | |||
}); | |||
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); | |||
}); | |||
// Перший виклик — після мікро-затримки (DOM точно готовий) | |||
setTimeout(function () { | |||
sectionMap = buildSectionMap(); | |||
updateActive(sectionMap); | |||
}, 200); | |||
}); | |||
// ============================================================ | |||
// 3. PROFILE ICONS | |||
// ============================================================ | |||
$(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. STATS OVERLAY НА ФОТО ГРАВЦЯ В R-BOX | |||
// Читає ігри/перемоги/поразки з параметрів шаблону і додає overlay | |||
// ============================================================ | |||
$(function () { | |||
var $rbox = $('.r-box'); | |||
if ($rbox.length === 0) return; | |||
// Шукаємо рядки таблиці в r-box | |||
var games = '', wins = '', losses = ''; | |||
$rbox.find('table tr').each(function () { | |||
var $th = $(this).find('th'); | |||
var $td = $(this).find('td'); | |||
if (!$th.length || !$td.length) return; | |||
var label = $th.text().trim().toLowerCase(); | |||
var val = $td.text().trim(); | |||
if (label.indexOf('офіційних') !== -1 || label.indexOf('ігор') !== -1) { | |||
// "46.95% (208 - 235)" → витягуємо числа | |||
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; | |||
} | |||
}); | |||
// Якщо знайшли дані — додаємо overlay на фото | |||
if (games && wins && losses) { | |||
var $figure = $rbox.find('figure'); | |||
if ($figure.length && !$figure.find('.rp-stats-overlay').length) { | |||
var html = '<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>'; | |||
$figure.append(html); | |||
} | |||
} | |||
}); | |||
// ============================================================ | |||
// 5. ДИНАМІЧНІ ПОСИЛАННЯ НА ГОЛОВНІЙ | |||
// ============================================================ | |||
$(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>'); | |||
} | |||
}); | |||
// ============================================================ | |||
// 6. 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'); | |||
if (tabId === 'games') { | |||
$lbox.fadeOut(200); | |||
} else { | |||
$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'); | |||
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); | |||
var $table = $content.find('table.sortable, table.wikitable'); | |||
$table.tablesorter(); | |||
// Додаємо фільтри над таблицею | |||
injectGamesFilters($content, $table, playerName); | |||
applyWinrateColors($content); | |||
wrapWideTables($content); | |||
applyRolePills($content); | |||
$(document).trigger('mcc:content-loaded'); | |||
} | |||
}, | |||
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 (!isMobile()) $rbox.show(); | |||
}); | |||
}); | |||
// ============================================================ | |||
// 7. ФІЛЬТРИ НАД ТАБЛИЦЕЮ ІГОР | |||
// ============================================================ | |||
function injectGamesFilters($container, $table, playerName) { | |||
if (!$table.length) return; | |||
if ($container.find('.mcc-games-filters').length) return; | |||
// Збираємо унікальні події з таблиці | |||
var events = []; | |||
$table.find('tbody tr').each(function () { | |||
var event = $(this).find('td').eq(0).text().trim(); | |||
if (event && events.indexOf(event) === -1) events.push(event); | |||
}); | |||
var eventOptions = '<option value="">Всі події</option>'; | |||
events.forEach(function (e) { | |||
eventOptions += '<option value="' + e + '">' + e + '</option>'; | |||
}); | |||
var html = '<div class="mcc-games-filters">' | |||
+ '<div class="mcc-filter-group">' | |||
+ '<span class="mcc-filter-label">Подія</span>' | |||
+ '<select class="mcc-filter-select" id="mcc-f-event">' + eventOptions + '</select>' | |||
+ '</div>' | |||
+ '<div class="mcc-filter-group">' | |||
+ '<span class="mcc-filter-label">Роль</span>' | |||
+ '<select class="mcc-filter-select" id="mcc-f-role">' | |||
+ '<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" id="mcc-f-result">' | |||
+ '<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" id="mcc-f-dur" min="20" max="70" value="70" step="1">' | |||
+ '<span class="mcc-slider-val" id="mcc-dur-val">70:00</span>' | |||
+ '</div>' | |||
+ '</div>' | |||
+ '<span class="mcc-filter-count" id="mcc-games-count"></span>' | |||
+ '</div>'; | |||
$table.before(html); | |||
var $filters = $container.find('.mcc-games-filters'); | |||
function timeToMins(str) { | |||
str = str.trim(); | |||
// Формати: "37:57" або "37 хв 26 с" | |||
var m1 = str.match(/^(\d+):(\d+)$/); | |||
if (m1) return parseInt(m1[1]) + parseInt(m1[2]) / 60; | |||
var m2 = str.match(/(\d+)\s*хв\s*(\d+)/); | |||
if (m2) return parseInt(m2[1]) + parseInt(m2[2]) / 60; | |||
return 999; | |||
} | |||
function filterTable() { | |||
var ev = $('#mcc-f-event').val(); | |||
var role = $('#mcc-f-role').val(); | |||
var res = $('#mcc-f-result').val(); | |||
var dur = parseInt($('#mcc-f-dur').val()); | |||
var visible = 0; | |||
$table.find('tbody tr').each(function () { | |||
var $row = $(this); | |||
var tds = $row.find('td'); | |||
var evVal = tds.eq(0).text().trim(); | |||
var roleVal = tds.eq(1).text().trim(); | |||
var timeVal = tds.eq(2).text().trim(); | |||
var resVal = tds.eq(3).text().trim(); | |||
var show = true; | |||
if (ev && evVal.indexOf(ev) === -1) show = false; | |||
if (role && roleVal.indexOf(role) === -1) show = false; | |||
if (res && resVal.indexOf(res) === -1) show = false; | |||
if (timeToMins(timeVal) > dur) show = false; | |||
$row.toggle(show); | |||
if (show) visible++; | |||
}); | |||
$('#mcc-games-count').text(visible + ' ігор'); | |||
} | |||
// Slider | |||
$('#mcc-f-dur').on('input', function () { | |||
var v = parseInt($(this).val()); | |||
$('#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 total = $table.find('tbody tr').length; | |||
$('#mcc-games-count').text(total + ' ігор'); | |||
// Початковий slider fill | |||
var initPct = (70 - 20) / (70 - 20) * 100; | |||
$('#mcc-f-dur').css('background-size', initPct + '% 100%'); | |||
} | |||
// ============================================================ | |||
// 8. ROLE PILLS — замінюємо білий текст на кольорові пілюлі | |||
// ============================================================ | |||
function applyRolePills($context) { | |||
var $root = $context || $(document); | |||
var roleMap = { | |||
'Мир': { bg: 'rgba(76,175,125,0.12)', color: '#7dd4a6', border: 'rgba(76,175,125,0.20)' }, | |||
'Шер': { bg: 'rgba(91,143,255,0.12)', color: '#7da8ff', border: 'rgba(91,143,255,0.20)' }, | |||
'Маф': { bg: 'rgba(200,76,76,0.12)', color: '#e08888', border: 'rgba(200,76,76,0.20)' }, | |||
'Дон': { bg: 'rgba(255,215,0,0.10)', color: '#ffd700', border: 'rgba(255,215,0,0.20)' } | |||
}; | |||
$root.find('.wikitable tbody td').each(function () { | |||
var $cell = $(this); | |||
// Перевіряємо span з inline color | |||
var $span = $cell.find('span').filter(function () { | |||
var s = $(this).attr('style') || ''; | |||
return s.indexOf('color') !== -1; | |||
}); | |||
if (!$span.length) return; | |||
var text = $span.text().trim(); | |||
var style = roleMap[text]; | |||
if (!style) return; | |||
$span.css({ | |||
'display': 'inline-block', | |||
'padding': '2px 10px', | |||
'border-radius': '4px', | |||
'font-size': '12.5px', | |||
'font-weight': '600', | |||
'background': style.bg, | |||
'color': style.color, | |||
'border': '1px solid ' + style.border | |||
}); | |||
}); | |||
} | |||
$(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() { | |||
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)); | |||
}); | |||
$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 () { wrapWideTables(); }); | |||
// ============================================================ | |||
// 11. ВІНРЕЙТ — кольорові класи | |||
// ============================================================ | |||
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; | |||
var text = $cell.text().trim(); | |||
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'); | |||
}); | |||
} | |||
$(function () { applyWinrateColors(); }); | |||
// ============================================================ | |||
// 12. МОБІЛЬНИЙ ПОШУК | |||
// ============================================================ | |||
$(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(''); } | |||
}); | |||
}); | |||
// ============================================================ | |||
// 13. БЛОКУВАННЯ 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); | |||
} | |||
}); | |||
// ============================================================ | |||
// 14. BANNER SEARCH | |||
// ============================================================ | // ============================================================ | ||
$(function () { | $(function () { | ||
Версія за 19:44, 6 квітня 2026
// ============================================================
// MediaWiki:Common.js — Mafia Closed Circle
// Фінальна версія
// ============================================================
// ============================================================
// 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',
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 $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 = $('#' + 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) {
var scrollY = $(window).scrollTop() + 110;
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');
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 — ФІНАЛЬНА ВЕРСІЯ
// ============================================================
// ============================================================
// 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 {
// ФІКС: document.getElementById коректно обробляє кирилицю
var el = document.getElementById(target);
if (el) {
var $el = $(el);
// Якщо el — це span.mw-headline, беремо батьківський h2
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');
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;
var offset = $heading.offset().top - 78;
$('html, body').animate({ scrollTop: offset }, 280);
}
}
// Активуємо одразу при кліку, не чекаємо скрол
$items.removeClass('active');
$(this).addClass('active');
});
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);
});
// Перший виклик — після мікро-затримки (DOM точно готовий)
setTimeout(function () {
sectionMap = buildSectionMap();
updateActive(sectionMap);
}, 200);
});
// ============================================================
// 3. PROFILE ICONS
// ============================================================
$(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. STATS OVERLAY НА ФОТО ГРАВЦЯ В R-BOX
// Читає ігри/перемоги/поразки з параметрів шаблону і додає overlay
// ============================================================
$(function () {
var $rbox = $('.r-box');
if ($rbox.length === 0) return;
// Шукаємо рядки таблиці в r-box
var games = '', wins = '', losses = '';
$rbox.find('table tr').each(function () {
var $th = $(this).find('th');
var $td = $(this).find('td');
if (!$th.length || !$td.length) return;
var label = $th.text().trim().toLowerCase();
var val = $td.text().trim();
if (label.indexOf('офіційних') !== -1 || label.indexOf('ігор') !== -1) {
// "46.95% (208 - 235)" → витягуємо числа
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;
}
});
// Якщо знайшли дані — додаємо overlay на фото
if (games && wins && losses) {
var $figure = $rbox.find('figure');
if ($figure.length && !$figure.find('.rp-stats-overlay').length) {
var html = '<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>';
$figure.append(html);
}
}
});
// ============================================================
// 5. ДИНАМІЧНІ ПОСИЛАННЯ НА ГОЛОВНІЙ
// ============================================================
$(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>');
}
});
// ============================================================
// 6. 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');
if (tabId === 'games') {
$lbox.fadeOut(200);
} else {
$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');
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);
var $table = $content.find('table.sortable, table.wikitable');
$table.tablesorter();
// Додаємо фільтри над таблицею
injectGamesFilters($content, $table, playerName);
applyWinrateColors($content);
wrapWideTables($content);
applyRolePills($content);
$(document).trigger('mcc:content-loaded');
}
},
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 (!isMobile()) $rbox.show();
});
});
// ============================================================
// 7. ФІЛЬТРИ НАД ТАБЛИЦЕЮ ІГОР
// ============================================================
function injectGamesFilters($container, $table, playerName) {
if (!$table.length) return;
if ($container.find('.mcc-games-filters').length) return;
// Збираємо унікальні події з таблиці
var events = [];
$table.find('tbody tr').each(function () {
var event = $(this).find('td').eq(0).text().trim();
if (event && events.indexOf(event) === -1) events.push(event);
});
var eventOptions = '<option value="">Всі події</option>';
events.forEach(function (e) {
eventOptions += '<option value="' + e + '">' + e + '</option>';
});
var html = '<div class="mcc-games-filters">'
+ '<div class="mcc-filter-group">'
+ '<span class="mcc-filter-label">Подія</span>'
+ '<select class="mcc-filter-select" id="mcc-f-event">' + eventOptions + '</select>'
+ '</div>'
+ '<div class="mcc-filter-group">'
+ '<span class="mcc-filter-label">Роль</span>'
+ '<select class="mcc-filter-select" id="mcc-f-role">'
+ '<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" id="mcc-f-result">'
+ '<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" id="mcc-f-dur" min="20" max="70" value="70" step="1">'
+ '<span class="mcc-slider-val" id="mcc-dur-val">70:00</span>'
+ '</div>'
+ '</div>'
+ '<span class="mcc-filter-count" id="mcc-games-count"></span>'
+ '</div>';
$table.before(html);
var $filters = $container.find('.mcc-games-filters');
function timeToMins(str) {
str = str.trim();
// Формати: "37:57" або "37 хв 26 с"
var m1 = str.match(/^(\d+):(\d+)$/);
if (m1) return parseInt(m1[1]) + parseInt(m1[2]) / 60;
var m2 = str.match(/(\d+)\s*хв\s*(\d+)/);
if (m2) return parseInt(m2[1]) + parseInt(m2[2]) / 60;
return 999;
}
function filterTable() {
var ev = $('#mcc-f-event').val();
var role = $('#mcc-f-role').val();
var res = $('#mcc-f-result').val();
var dur = parseInt($('#mcc-f-dur').val());
var visible = 0;
$table.find('tbody tr').each(function () {
var $row = $(this);
var tds = $row.find('td');
var evVal = tds.eq(0).text().trim();
var roleVal = tds.eq(1).text().trim();
var timeVal = tds.eq(2).text().trim();
var resVal = tds.eq(3).text().trim();
var show = true;
if (ev && evVal.indexOf(ev) === -1) show = false;
if (role && roleVal.indexOf(role) === -1) show = false;
if (res && resVal.indexOf(res) === -1) show = false;
if (timeToMins(timeVal) > dur) show = false;
$row.toggle(show);
if (show) visible++;
});
$('#mcc-games-count').text(visible + ' ігор');
}
// Slider
$('#mcc-f-dur').on('input', function () {
var v = parseInt($(this).val());
$('#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 total = $table.find('tbody tr').length;
$('#mcc-games-count').text(total + ' ігор');
// Початковий slider fill
var initPct = (70 - 20) / (70 - 20) * 100;
$('#mcc-f-dur').css('background-size', initPct + '% 100%');
}
// ============================================================
// 8. ROLE PILLS — замінюємо білий текст на кольорові пілюлі
// ============================================================
function applyRolePills($context) {
var $root = $context || $(document);
var roleMap = {
'Мир': { bg: 'rgba(76,175,125,0.12)', color: '#7dd4a6', border: 'rgba(76,175,125,0.20)' },
'Шер': { bg: 'rgba(91,143,255,0.12)', color: '#7da8ff', border: 'rgba(91,143,255,0.20)' },
'Маф': { bg: 'rgba(200,76,76,0.12)', color: '#e08888', border: 'rgba(200,76,76,0.20)' },
'Дон': { bg: 'rgba(255,215,0,0.10)', color: '#ffd700', border: 'rgba(255,215,0,0.20)' }
};
$root.find('.wikitable tbody td').each(function () {
var $cell = $(this);
// Перевіряємо span з inline color
var $span = $cell.find('span').filter(function () {
var s = $(this).attr('style') || '';
return s.indexOf('color') !== -1;
});
if (!$span.length) return;
var text = $span.text().trim();
var style = roleMap[text];
if (!style) return;
$span.css({
'display': 'inline-block',
'padding': '2px 10px',
'border-radius': '4px',
'font-size': '12.5px',
'font-weight': '600',
'background': style.bg,
'color': style.color,
'border': '1px solid ' + style.border
});
});
}
$(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() {
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));
});
$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 () { wrapWideTables(); });
// ============================================================
// 11. ВІНРЕЙТ — кольорові класи
// ============================================================
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;
var text = $cell.text().trim();
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');
});
}
$(function () { applyWinrateColors(); });
// ============================================================
// 12. МОБІЛЬНИЙ ПОШУК
// ============================================================
$(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(''); }
});
});
// ============================================================
// 13. БЛОКУВАННЯ 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);
}
});
// ============================================================
// 14. 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(); } });
});