MediaWiki:Common.js
Jump to navigation
Jump to search
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
/* Any JavaScript here will be loaded for all users on every page load. */
(function ($, mw) {
'use strict';
/* =========================================================
GLOBAL BASICS
========================================================= */
function setLogoHref() {
var logoLink = document.querySelector('#p-logo a');
if (logoLink) {
logoLink.setAttribute('href', 'https://timero.com.br/');
}
}
function ensureFontAwesome() {
if (document.querySelector('link[data-timero-fa]')) return;
var fa = document.createElement('link');
fa.rel = 'stylesheet';
fa.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css';
fa.setAttribute('data-timero-fa', '1');
document.head.appendChild(fa);
}
function bindActionButton(el, handler) {
if (!el || el.dataset.timeroBound === '1') return;
el.dataset.timeroBound = '1';
el.setAttribute('role', 'button');
el.setAttribute('tabindex', '0');
el.addEventListener('click', function (e) {
e.preventDefault();
handler.call(this, e);
});
el.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handler.call(this, e);
}
});
}
function toNumber(value, fallback) {
var n = parseFloat(value);
return isNaN(n) ? fallback : n;
}
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
/* =========================================================
GUIDE PAGE SKIN SWITCH
========================================================= */
function applyTimeroGuideSkin() {
var page = mw.config.get('wgPageName') || '';
var html = document.documentElement;
var body = document.body;
var hasGuideRoot =
document.getElementById('farming-guide-root') ||
document.getElementById('mvp-guide-root') ||
document.getElementById('leveling-guide-root') ||
document.querySelector('.timero-wiki-root');
if (!html || !body) return;
Array.prototype.slice.call(html.classList).forEach(function (cls) {
if (cls === 'timero-guide-skin' || cls.indexOf('guide-page-') === 0) {
html.classList.remove(cls);
}
});
Array.prototype.slice.call(body.classList).forEach(function (cls) {
if (cls === 'timero-guide-skin' || cls.indexOf('guide-page-') === 0) {
body.classList.remove(cls);
}
});
if (/^Guia_de_/.test(page) || hasGuideRoot) {
var safePageClass = 'guide-page-' + String(page || 'custom-guide').replace(/[^A-Za-z0-9_-]/g, '-');
html.classList.add('timero-guide-skin');
body.classList.add('timero-guide-skin');
html.classList.add(safePageClass);
body.classList.add(safePageClass);
}
}
/* =========================================================
ROUTES
========================================================= */
function resetRoutePill(pill) {
if (!pill) return;
pill.style.transform = '';
pill.style.fontWeight = '800';
pill.style.boxShadow = 'none';
pill.style.filter = 'none';
}
function activateRoutePill(pill) {
if (!pill) return;
pill.style.transform = 'translateY(-1px)';
pill.style.fontWeight = '900';
pill.style.boxShadow = '0 0 0 1px rgba(255,255,255,0.05) inset, 0 0 22px rgba(255,255,255,0.05)';
pill.style.filter = 'brightness(1.08)';
}
function closeAllRoutes(section) {
if (!section) return;
var details = section.querySelectorAll('.route-detail');
var pills = section.querySelectorAll('.route-pill[data-route]');
for (var i = 0; i < details.length; i++) {
details[i].style.display = 'none';
}
for (var j = 0; j < pills.length; j++) {
resetRoutePill(pills[j]);
}
section.setAttribute('data-open-route', '');
}
window.expandRoute = function (routeId) {
var section = document.getElementById('routes-section');
if (!section) return;
var target = document.getElementById(routeId);
if (!target) return;
var currentOpen = section.getAttribute('data-open-route');
if (currentOpen === routeId) {
closeAllRoutes(section);
return;
}
closeAllRoutes(section);
target.style.display = 'block';
target.style.animation = 'route-in 0.3s ease both';
section.setAttribute('data-open-route', routeId);
var activePill = document.getElementById('pill-' + routeId);
activateRoutePill(activePill);
if (typeof target.scrollIntoView === 'function') {
setTimeout(function () {
target.scrollIntoView({
behavior: 'smooth',
block: 'nearest'
});
}, 80);
}
};
function bindRouteViewer(context) {
var scope = context && context.querySelector ? context : document;
var section = scope.querySelector('#routes-section') || document.getElementById('routes-section');
if (!section) return;
var pills = section.querySelectorAll('.route-pill[data-route]');
for (var i = 0; i < pills.length; i++) {
(function (pill) {
bindActionButton(pill, function () {
expandRoute(this.getAttribute('data-route'));
});
})(pills[i]);
}
var closeButtons = section.querySelectorAll('.route-close[data-route]');
for (var j = 0; j < closeButtons.length; j++) {
(function (closeBtn) {
bindActionButton(closeBtn, function () {
expandRoute(this.getAttribute('data-route'));
});
})(closeButtons[j]);
}
}
/* =========================================================
METHODS
========================================================= */
function resetMethodTab(tab, type) {
if (!tab) return;
tab.style.background = 'transparent';
tab.style.fontWeight = '700';
if (type === 'grind') tab.style.color = 'rgba(249,197,0,0.60)';
if (type === 'market') tab.style.color = 'rgba(0,212,255,0.60)';
if (type === 'passive') tab.style.color = 'rgba(176,108,255,0.60)';
}
function activateMethodTab(tab, type) {
if (!tab) return;
tab.style.fontWeight = '900';
if (type === 'grind') {
tab.style.background = 'linear-gradient(135deg,rgba(249,197,0,0.18),rgba(249,197,0,0.07))';
tab.style.color = '#f9c500';
}
if (type === 'market') {
tab.style.background = 'linear-gradient(135deg,rgba(0,212,255,0.14),rgba(0,212,255,0.05))';
tab.style.color = '#00d4ff';
}
if (type === 'passive') {
tab.style.background = 'linear-gradient(135deg,rgba(176,108,255,0.14),rgba(176,108,255,0.05))';
tab.style.color = '#b06cff';
}
}
window.switchMethod = function (methodId) {
var section = document.getElementById('methods-section');
if (!section) return;
var panels = section.querySelectorAll('.method-panel');
var tabs = section.querySelectorAll('.method-tab[data-method]');
for (var i = 0; i < panels.length; i++) {
panels[i].style.display = 'none';
}
for (var j = 0; j < tabs.length; j++) {
resetMethodTab(tabs[j], tabs[j].getAttribute('data-method'));
}
var activePanel = document.getElementById('method-' + methodId);
if (activePanel) {
activePanel.style.display = 'grid';
activePanel.style.animation = 'method-in 0.3s ease both';
}
var activeTab = document.getElementById('mtab-' + methodId);
activateMethodTab(activeTab, methodId);
section.setAttribute('data-open-method', methodId);
};
function bindMethodViewer(context) {
var scope = context && context.querySelector ? context : document;
var section = scope.querySelector('#methods-section') || document.getElementById('methods-section');
if (!section) return;
var tabs = section.querySelectorAll('.method-tab[data-method]');
for (var i = 0; i < tabs.length; i++) {
(function (tab) {
bindActionButton(tab, function () {
switchMethod(this.getAttribute('data-method'));
});
})(tabs[i]);
}
if (!section.getAttribute('data-open-method')) {
switchMethod('grind');
} else {
switchMethod(section.getAttribute('data-open-method'));
}
}
/* =========================================================
CALCULATOR
========================================================= */
var calcState = {
hours: 2,
booster: false,
rate: 70,
label: 'Rota Mid-Game (70M/hr)',
weeklyTarget: 12000
};
function formatMillions(value) {
var rounded = Math.round(value * 10) / 10;
if (rounded >= 1000) {
var billions = rounded / 1000;
var bText = (Math.round(billions * 10) / 10).toString().replace('.0', '').replace('.', ',');
return bText + 'B z';
}
if (rounded >= 1) {
return rounded.toString().replace('.0', '').replace('.', ',') + 'M z';
}
return Math.round(rounded * 1000) + 'k z';
}
function refreshCalcMethodUI(section) {
if (!section) return;
var labelEl = section.querySelector('#calc-method-label');
var menuEl = section.querySelector('#calc-method-menu');
var options = section.querySelectorAll('.calc-method-option');
if (labelEl) {
labelEl.textContent = calcState.label;
}
for (var i = 0; i < options.length; i++) {
var opt = options[i];
var isActive = String(opt.getAttribute('data-rate')) === String(calcState.rate);
if (isActive) {
opt.style.background = 'rgba(0,212,255,0.12)';
opt.style.borderColor = 'rgba(0,212,255,0.25)';
opt.style.color = '#00d4ff';
opt.style.fontWeight = '800';
} else {
opt.style.background = 'rgba(255,255,255,0.03)';
opt.style.borderColor = 'rgba(255,255,255,0.06)';
opt.style.color = 'rgba(232,238,248,0.82)';
opt.style.fontWeight = '700';
}
}
if (menuEl) {
menuEl.style.display = section.getAttribute('data-method-menu-open') === '1' ? 'block' : 'none';
}
}
function syncCalcStateFromNativeSelect(section) {
if (!section) return;
var select = section.querySelector('#calc-method');
if (!select) return;
calcState.rate = toNumber(select.value, calcState.rate);
if (select.options && select.selectedIndex >= 0) {
calcState.label = select.options[select.selectedIndex].text;
}
}
window.adjustCalc = function (field, delta) {
if (field !== 'hours') return;
calcState.hours = clamp(calcState.hours + delta, 0.5, 24);
updateCalc();
};
window.setBooster = function (enabled) {
calcState.booster = !!enabled;
updateCalc();
};
window.setCalcMethod = function (rate, label) {
calcState.rate = toNumber(rate, calcState.rate);
calcState.label = label || calcState.label;
var section = document.getElementById('calculator-section');
if (section) {
section.setAttribute('data-method-menu-open', '0');
var select = section.querySelector('#calc-method');
if (select) {
select.value = String(calcState.rate);
}
}
updateCalc();
};
window.toggleCalcMethodMenu = function () {
var section = document.getElementById('calculator-section');
if (!section) return;
var isOpen = section.getAttribute('data-method-menu-open') === '1';
section.setAttribute('data-method-menu-open', isOpen ? '0' : '1');
refreshCalcMethodUI(section);
};
window.closeCalcMethodMenu = function () {
var section = document.getElementById('calculator-section');
if (!section) return;
section.setAttribute('data-method-menu-open', '0');
refreshCalcMethodUI(section);
};
window.updateCalc = function () {
var section = document.getElementById('calculator-section');
if (!section) return;
syncCalcStateFromNativeSelect(section);
var hoursEl = document.getElementById('calc-hours');
var resultSession = document.getElementById('result-session');
var resultDay = document.getElementById('result-day');
var resultWeek = document.getElementById('result-week');
var resultPct = document.getElementById('result-pct');
var resultBar = document.getElementById('result-bar');
var boostNo = document.getElementById('boost-no');
var boostYes = document.getElementById('boost-yes');
var effectiveRate = calcState.booster ? calcState.rate * 1.5 : calcState.rate;
var session = calcState.hours * effectiveRate;
var day = session;
var week = day * 7;
var pct = Math.max(0, Math.min(100, Math.round((week / calcState.weeklyTarget) * 100)));
if (hoursEl) {
hoursEl.textContent = calcState.hours % 1 === 0 ? String(calcState.hours) : String(calcState.hours).replace('.', ',');
}
if (resultSession) resultSession.textContent = formatMillions(session);
if (resultDay) resultDay.textContent = formatMillions(day);
if (resultWeek) resultWeek.textContent = formatMillions(week);
if (resultPct) resultPct.textContent = pct + '%';
if (resultBar) resultBar.style.width = pct + '%';
if (boostNo && boostYes) {
if (calcState.booster) {
boostNo.style.background = 'rgba(255,255,255,0.04)';
boostNo.style.borderColor = 'rgba(255,255,255,0.09)';
boostNo.style.color = 'rgba(122,144,176,0.60)';
boostNo.style.fontWeight = '700';
boostYes.style.background = 'rgba(0,212,255,0.15)';
boostYes.style.borderColor = 'rgba(0,212,255,0.30)';
boostYes.style.color = '#00d4ff';
boostYes.style.fontWeight = '800';
} else {
boostNo.style.background = 'rgba(255,61,90,0.15)';
boostNo.style.borderColor = 'rgba(255,61,90,0.30)';
boostNo.style.color = '#ff3d5a';
boostNo.style.fontWeight = '800';
boostYes.style.background = 'rgba(255,255,255,0.04)';
boostYes.style.borderColor = 'rgba(255,255,255,0.09)';
boostYes.style.color = 'rgba(122,144,176,0.60)';
boostYes.style.fontWeight = '700';
}
}
refreshCalcMethodUI(section);
};
function bindCalculator(context) {
var scope = context && context.querySelector ? context : document;
var section = scope.querySelector('#calculator-section') || document.getElementById('calculator-section');
if (!section) return;
var adjusters = section.querySelectorAll('[data-calc-adjust]');
for (var i = 0; i < adjusters.length; i++) {
(function (adjuster) {
bindActionButton(adjuster, function (e) {
e.stopPropagation();
adjustCalc(this.getAttribute('data-calc-adjust'), toNumber(this.getAttribute('data-delta'), 0));
});
})(adjusters[i]);
}
var boosters = section.querySelectorAll('[data-booster]');
for (var j = 0; j < boosters.length; j++) {
(function (booster) {
bindActionButton(booster, function (e) {
e.stopPropagation();
setBooster(this.getAttribute('data-booster') === '1');
});
})(boosters[j]);
}
var methodDisplay = section.querySelector('#calc-method-display');
if (methodDisplay) {
bindActionButton(methodDisplay, function (e) {
e.stopPropagation();
toggleCalcMethodMenu();
});
}
var options = section.querySelectorAll('.calc-method-option');
for (var k = 0; k < options.length; k++) {
(function (option) {
bindActionButton(option, function (e) {
e.stopPropagation();
setCalcMethod(this.getAttribute('data-rate'), this.getAttribute('data-label'));
});
})(options[k]);
}
var nativeSelect = section.querySelector('#calc-method');
if (nativeSelect && nativeSelect.dataset.timeroBound !== '1') {
nativeSelect.dataset.timeroBound = '1';
nativeSelect.addEventListener('change', function () {
updateCalc();
});
}
if (!window.__timeroCalcDocBound) {
window.__timeroCalcDocBound = true;
document.addEventListener('click', function (e) {
var liveSection = document.getElementById('calculator-section');
if (!liveSection) return;
if (!liveSection.contains(e.target)) {
closeCalcMethodMenu();
}
});
}
if (!section.getAttribute('data-method-menu-open')) {
section.setAttribute('data-method-menu-open', '0');
}
updateCalc();
}
/* =========================================================
LOADOUT
========================================================= */
function setLoadoutItemState(item, active) {
if (!item) return;
var check = item.querySelector('.li-check');
if (!check) return;
if (active) {
item.classList.add('is-active');
item.style.background = 'rgba(176,108,255,0.08)';
item.style.borderColor = 'rgba(176,108,255,0.24)';
item.style.boxShadow = '0 0 0 1px rgba(176,108,255,0.05) inset';
check.style.background = 'rgba(176,108,255,0.16)';
check.style.borderColor = 'rgba(176,108,255,0.45)';
check.style.color = '#b06cff';
check.innerHTML = '✓';
} else {
item.classList.remove('is-active');
item.style.background = 'rgba(255,255,255,0.03)';
item.style.borderColor = 'rgba(255,255,255,0.07)';
item.style.boxShadow = 'none';
check.style.background = 'rgba(176,108,255,0.04)';
check.style.borderColor = 'rgba(176,108,255,0.30)';
check.style.color = 'transparent';
check.innerHTML = '';
}
}
function updateLoadoutSummary() {
var section = document.getElementById('loadout-section');
if (!section) return;
var items = section.querySelectorAll('.loadout-item');
var total = 0;
var count = 0;
for (var i = 0; i < items.length; i++) {
if (items[i].classList.contains('is-active')) {
total += toNumber(items[i].getAttribute('data-cost'), 0);
count += 1;
}
}
var totalEl = document.getElementById('loadout-total');
var countEl = document.getElementById('loadout-count');
if (totalEl) totalEl.textContent = total + 'k z';
if (countEl) countEl.textContent = String(count);
}
window.toggleLoadout = function (element) {
if (!element) return;
var active = !element.classList.contains('is-active');
setLoadoutItemState(element, active);
updateLoadoutSummary();
};
window.clearLoadout = function () {
var section = document.getElementById('loadout-section');
if (!section) return;
var items = section.querySelectorAll('.loadout-item');
for (var i = 0; i < items.length; i++) {
setLoadoutItemState(items[i], false);
}
updateLoadoutSummary();
};
function bindLoadout(context) {
var scope = context && context.querySelector ? context : document;
var section = scope.querySelector('#loadout-section') || document.getElementById('loadout-section');
if (!section) return;
var items = section.querySelectorAll('.loadout-item');
for (var i = 0; i < items.length; i++) {
(function (item) {
setLoadoutItemState(item, item.classList.contains('is-active'));
bindActionButton(item, function () {
toggleLoadout(this);
});
})(items[i]);
}
var clearBtn = section.querySelector('.loadout-clear');
if (clearBtn) {
bindActionButton(clearBtn, function () {
clearLoadout();
});
}
updateLoadoutSummary();
}
/* =========================================================
TIMERO CLICKABLE CARDS
========================================================= */
function bindTimeroNavCards(context) {
var scope = context && context.querySelector ? context : document;
var cards = scope.querySelectorAll('.timero-nav-card[data-href]');
for (var i = 0; i < cards.length; i++) {
(function (card) {
if (card.dataset.timeroNavBound === '1') return;
card.dataset.timeroNavBound = '1';
card.setAttribute('role', 'button');
card.setAttribute('tabindex', '0');
card.style.cursor = 'pointer';
card.addEventListener('click', function (e) {
if (e.target.closest('a, button, input, select, textarea')) return;
var href = this.getAttribute('data-href');
var target = this.getAttribute('data-target') || '';
if (!href) return;
if (target === '_blank') {
window.open(href, '_blank', 'noopener');
} else {
window.location.href = href;
}
});
card.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this.click();
}
});
})(cards[i]);
}
}
/* =========================================================
MVP GUIDE
========================================================= */
var MVP_TIMER_STORAGE_KEY = 'timero_mvp_timers_v1';
function readStoredMvpTimers() {
try {
var raw = window.localStorage.getItem(MVP_TIMER_STORAGE_KEY);
if (!raw) return [];
var parsed = JSON.parse(raw);
return Array.isArray(parsed) ? parsed : [];
} catch (err) {
return [];
}
}
function writeStoredMvpTimers(list) {
try {
window.localStorage.setItem(MVP_TIMER_STORAGE_KEY, JSON.stringify(list));
} catch (err) {
/* ignore */
}
}
function getRemainingTimerMs(timerObj) {
var now = Date.now();
var endAt = timerObj.killTime + timerObj.respawnMs;
return Math.max(0, endAt - now);
}
function formatTimerMs(ms) {
var totalSec = Math.max(0, Math.floor(ms / 1000));
var hours = Math.floor(totalSec / 3600);
var mins = Math.floor((totalSec % 3600) / 60);
var secs = totalSec % 60;
if (hours > 0) {
return String(hours).padStart(2, '0') + ':' + String(mins).padStart(2, '0') + ':' + String(secs).padStart(2, '0');
}
return String(mins).padStart(2, '0') + ':' + String(secs).padStart(2, '0');
}
function removeTimerById(timerId) {
var list = readStoredMvpTimers().filter(function (item) {
return item.id !== timerId;
});
writeStoredMvpTimers(list);
var row = document.getElementById(timerId);
if (row && row.parentNode) {
row.parentNode.removeChild(row);
}
var timerList = document.getElementById('timer-list');
var empty = document.getElementById('timer-empty');
if (timerList && empty && timerList.children.length === 0) {
empty.style.display = '';
}
}
function createTimerRow(timerObj) {
var list = document.getElementById('timer-list');
if (!list) return null;
var existing = document.getElementById(timerObj.id);
if (existing) return existing;
var empty = document.getElementById('timer-empty');
if (empty) empty.style.display = 'none';
var row = document.createElement('div');
row.id = timerObj.id;
row.className = 'mvp-timer-row';
row.style.cssText = 'display:grid;grid-template-columns:1fr auto auto;gap:12px;align-items:center;padding:13px 16px;border-radius:12px;background:rgba(0,212,255,0.05);border:1px solid rgba(0,212,255,0.14);';
var killTs = new Date(timerObj.killTime);
var h = killTs.getHours().toString().padStart(2, '0');
var m = killTs.getMinutes().toString().padStart(2, '0');
row.innerHTML =
'<div>' +
'<div style="font-size:0.90rem;font-weight:800;color:#fff;margin-bottom:2px;">💀 ' + timerObj.name + '</div>' +
'<div style="font-size:0.70rem;color:rgba(122,144,176,0.55);">Kill registrada às ' + h + ':' + m + ' · Respawn ' + timerObj.respawnMins + 'min</div>' +
'</div>' +
'<div class="mvp-timer-countdown" style="font-size:0.86rem;font-weight:900;color:#00d4ff;min-width:78px;text-align:right;">--:--</div>' +
'<button type="button" class="mvp-timer-remove" style="padding:8px 12px;border-radius:8px;background:rgba(255,61,90,0.10);border:1px solid rgba(255,61,90,0.22);color:#ff3d5a;font-size:0.75rem;font-weight:800;cursor:pointer;font-family:inherit;">Remover</button>';
var removeBtn = row.querySelector('.mvp-timer-remove');
if (removeBtn) {
removeBtn.addEventListener('click', function () {
removeTimerById(timerObj.id);
});
}
list.appendChild(row);
return row;
}
function refreshSingleTimerRow(timerObj) {
var row = document.getElementById(timerObj.id);
if (!row) {
row = createTimerRow(timerObj);
}
if (!row) return;
var countdown = row.querySelector('.mvp-timer-countdown');
var remaining = getRemainingTimerMs(timerObj);
if (countdown) {
if (remaining <= 0) {
countdown.textContent = 'Respawn';
countdown.style.color = '#00ff88';
row.style.borderColor = 'rgba(0,255,136,0.22)';
row.style.background = 'rgba(0,255,136,0.05)';
} else {
countdown.textContent = formatTimerMs(remaining);
countdown.style.color = '#00d4ff';
row.style.borderColor = 'rgba(0,212,255,0.14)';
row.style.background = 'rgba(0,212,255,0.05)';
}
}
}
function refreshAllMvpTimers() {
var list = readStoredMvpTimers();
for (var i = 0; i < list.length; i++) {
refreshSingleTimerRow(list[i]);
}
}
window.toggleBoss = function (card) {
if (!card) return;
var dossier = card.querySelector('.boss-dossier');
var chevron = card.querySelector('.boss-chevron');
if (!dossier) return;
var isOpen = dossier.style.display !== 'none';
dossier.style.display = isOpen ? 'none' : 'block';
if (chevron) {
chevron.textContent = isOpen ? '▼ Dossiê' : '▲ Fechar';
}
card.style.boxShadow = isOpen ? '' : '0 0 32px rgba(255,61,90,0.12)';
};
window.filterTier = function (tier) {
var btns = document.querySelectorAll('.tf-btn');
for (var i = 0; i < btns.length; i++) {
btns[i].style.opacity = '0.45';
btns[i].style.fontWeight = '700';
}
var active = document.getElementById('tf-' + tier);
if (active) {
active.style.opacity = '1';
active.style.fontWeight = '900';
}
var cards = document.querySelectorAll('.boss-card');
var shown = 0;
for (var j = 0; j < cards.length; j++) {
var t = cards[j].dataset.tier;
var show = tier === 'all' || t === tier;
cards[j].style.display = show ? '' : 'none';
if (show) shown++;
}
var label = document.getElementById('boss-count-label');
if (label) {
label.textContent = shown + (shown === 1 ? ' MVP exibido' : ' MVPs exibidos');
}
};
window.sortBosses = function (by) {
var btns = document.querySelectorAll('.sort-btn');
for (var i = 0; i < btns.length; i++) {
btns[i].style.background = 'rgba(255,255,255,0.06)';
btns[i].style.borderColor = 'rgba(255,255,255,0.10)';
btns[i].style.color = 'rgba(232,238,248,0.65)';
}
var active = document.getElementById('sort-' + by);
if (active) {
active.style.background = 'rgba(255,61,90,0.12)';
active.style.borderColor = 'rgba(255,61,90,0.30)';
active.style.color = '#ff3d5a';
}
var grid = document.getElementById('boss-grid');
if (!grid) return;
var cards = Array.prototype.slice.call(grid.querySelectorAll('.boss-card'));
cards.sort(function (a, b) {
if (by === 'danger') return parseInt(b.dataset.danger || '0', 10) - parseInt(a.dataset.danger || '0', 10);
if (by === 'exp') return parseInt(b.dataset.exp || '0', 10) - parseInt(a.dataset.exp || '0', 10);
if (by === 'respawn') return parseInt(a.dataset.respawn || '0', 10) - parseInt(b.dataset.respawn || '0', 10);
return 0;
});
for (var j = 0; j < cards.length; j++) {
grid.appendChild(cards[j]);
}
};
window.toggleClassMvp = function (row) {
if (!row) return;
var detail = row.querySelector('.class-mvp-detail');
var chevron = row.querySelector('.class-chevron');
if (!detail) return;
var open = detail.style.display !== 'none';
detail.style.display = open ? 'none' : 'block';
if (chevron) {
chevron.style.transform = open ? 'rotate(0deg)' : 'rotate(180deg)';
}
};
window.logKill = function (btn, name, respawnMins) {
var killTime = Date.now();
var respawnMs = toNumber(respawnMins, 0) * 60 * 1000;
var timerObj = {
id: 'timer-' + killTime,
name: name || 'MVP',
respawnMins: toNumber(respawnMins, 0),
respawnMs: respawnMs,
killTime: killTime
};
var list = readStoredMvpTimers();
list.unshift(timerObj);
writeStoredMvpTimers(list);
createTimerRow(timerObj);
refreshSingleTimerRow(timerObj);
if (btn) {
btn.style.filter = 'brightness(1.08)';
setTimeout(function () {
btn.style.filter = '';
}, 450);
}
};
function bindMvpGuide(context) {
var scope = context && context.querySelector ? context : document;
var root = scope.querySelector('#mvp-guide-root') || document.getElementById('mvp-guide-root');
if (!root) return;
var tierButtons = root.querySelectorAll('.tf-btn[data-tier]');
for (var i = 0; i < tierButtons.length; i++) {
(function (btn) {
bindActionButton(btn, function () {
filterTier(this.getAttribute('data-tier'));
});
})(tierButtons[i]);
}
var sortButtons = root.querySelectorAll('.sort-btn[data-sort]');
for (var j = 0; j < sortButtons.length; j++) {
(function (btn) {
bindActionButton(btn, function () {
sortBosses(this.getAttribute('data-sort'));
});
})(sortButtons[j]);
}
if (!window.__timeroMvpTimerInterval) {
window.__timeroMvpTimerInterval = window.setInterval(refreshAllMvpTimers, 1000);
}
refreshAllMvpTimers();
var label = document.getElementById('boss-count-label');
if (label && label.textContent.replace(/\s+/g, '') === '') {
filterTier('all');
}
}
/* =========================================================
OPTIONAL GUIDE HELPERS
========================================================= */
function bindPhaseNav(context) {
var scope = context && context.querySelector ? context : document;
var nav = scope.querySelector('#phase-nav');
if (!nav) return;
var links = nav.querySelectorAll('a[href^="#"], a[data-target]');
for (var i = 0; i < links.length; i++) {
(function (link) {
if (link.dataset.timeroPhaseBound === '1') return;
link.dataset.timeroPhaseBound = '1';
link.addEventListener('click', function (e) {
var targetSelector = this.getAttribute('href');
if (!targetSelector || targetSelector === '#') {
targetSelector = this.getAttribute('data-target');
}
if (!targetSelector) return;
var target = document.querySelector(targetSelector);
if (!target) return;
e.preventDefault();
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
});
})(links[i]);
}
}
/* =========================================================
INIT
========================================================= */
function init(context) {
setLogoHref();
ensureFontAwesome();
applyTimeroGuideSkin();
bindMethodViewer(context || document);
bindRouteViewer(context || document);
bindCalculator(context || document);
bindLoadout(context || document);
bindTimeroNavCards(context || document);
bindMvpGuide(context || document);
bindPhaseNav(context || document);
}
$(function () {
init(document);
});
/* =========================================================
TIMERO WIKI CARD DATABASE
Safe MediaWiki version: JS creates inputs/buttons instead of
placing raw <input>/<button> inside wiki pages.
========================================================= */
(function () {
'use strict';
const CARD_DB_SELECTOR = '#timero-card-db-app';
const state = {
api: '',
cards: [],
groups: [],
q: '',
category: 'all',
changed: false,
sort: 'card_name',
dir: 'asc',
expandedCardId: null,
loading: false,
error: ''
};
const categories = [
{ id: 'all', label: 'Todas', icon: '◆', color: '#7c6aff' },
{ id: 'weapon', label: 'Armas', icon: '⚔️', color: '#ff6b7a' },
{ id: 'armor', label: 'Armaduras', icon: '🛡', color: '#00d4ff' },
{ id: 'accessory', label: 'Acessórios', icon: '💎', color: '#f9a826' },
{ id: 'headgear', label: 'Headgear', icon: '🎩', color: '#b06cff' },
{ id: 'shield', label: 'Escudos', icon: '🛡️', color: '#70b8ff' },
{ id: 'garment', label: 'Capas', icon: '🧥', color: '#60d090' },
{ id: 'shoes', label: 'Sapatos', icon: '👢', color: '#f0c840' }
];
function escapeHtml(value) {
return String(value ?? '')
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function normalize(value) {
return String(value ?? '').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}
function debounce(fn, delay) {
let timer = null;
return function () {
const args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(null, args);
}, delay);
};
}
function getRoot() {
return document.querySelector(CARD_DB_SELECTOR);
}
function cardIconUrl(cardId) {
return 'https://timero.com.br/images/item/icons/' + encodeURIComponent(cardId) + '.png';
}
function cardLink(cardId) {
return 'https://timero.com.br/pt/item?id=' + encodeURIComponent(cardId);
}
function categoryMeta(category) {
return categories.find(c => c.id === category) || categories[0];
}
function changedBadge(card) {
if (String(card.changed) === '1') {
return '<span class="tc-status tc-changed">Alterada</span>';
}
return '<span class="tc-status tc-original">Original</span>';
}
function scriptToReadable(script) {
const raw = String(script || '').trim();
if (!raw) return '—';
return raw
.replace(/bonus\s+/gi, '')
.replace(/;/g, '; ')
.replace(/bStr/gi, 'STR')
.replace(/bAgi/gi, 'AGI')
.replace(/bVit/gi, 'VIT')
.replace(/bInt/gi, 'INT')
.replace(/bDex/gi, 'DEX')
.replace(/bLuk/gi, 'LUK')
.replace(/bAllStats/gi, 'All Stats')
.replace(/bMaxHP/gi, 'Max HP')
.replace(/bMaxSP/gi, 'Max SP')
.replace(/bBaseAtk/gi, 'ATK')
.replace(/bMatk/gi, 'MATK')
.replace(/bAspdRate/gi, 'ASPD %')
.replace(/bAtkRate/gi, 'ATK %')
.replace(/bMatkRate/gi, 'MATK %')
.replace(/\s+/g, ' ')
.trim();
}
function injectStyles() {
if (document.getElementById('timero-card-db-style')) return;
const style = document.createElement('style');
style.id = 'timero-card-db-style';
style.textContent = `
#timero-card-db-app,
#timero-card-db-app * {
box-sizing: border-box;
}
#timero-card-db-app {
--tc-bg: #050914;
--tc-card: rgba(8,14,26,0.94);
--tc-card-2: rgba(4,8,18,0.99);
--tc-border: rgba(80,170,255,0.18);
--tc-border-soft: rgba(255,255,255,0.06);
--tc-cyan: #58d7ff;
--tc-blue: #4a90d9;
--tc-muted: #6070a0;
--tc-text: #d8ecff;
--tc-gold: #f0c840;
--tc-green: #70d890;
--tc-red: #ff6b7a;
font-family: 'Segoe UI', system-ui, sans-serif;
}
.tc-shell {
background:
radial-gradient(circle at 18% 0%, rgba(74,144,217,0.10), transparent 34%),
radial-gradient(circle at 88% 24%, rgba(92,70,180,0.08), transparent 30%),
linear-gradient(135deg, rgba(5,10,22,0.98), rgba(2,5,14,0.99));
border: 1px solid var(--tc-border);
border-radius: 18px;
padding: 24px;
margin: 0 0 34px 0;
box-shadow: 0 0 30px rgba(0,0,0,0.35), inset 0 0 28px rgba(80,160,255,0.025);
color: var(--tc-text);
}
.tc-head {
background: linear-gradient(90deg, rgba(74,144,217,0.14), rgba(74,144,217,0.04), transparent);
border: 1px solid rgba(74,144,217,0.18);
border-left: 5px solid var(--tc-blue);
border-radius: 12px;
padding: 16px 18px;
margin: 0 0 18px 0;
}
.tc-kicker {
color: var(--tc-cyan);
font-weight: 900;
letter-spacing: .10em;
text-transform: uppercase;
font-size: .78rem;
margin-bottom: 8px;
}
.tc-title {
color: #fff;
font-size: clamp(1.4rem, 2.6vw, 2rem);
font-weight: 900;
margin: 0 0 6px 0;
line-height: 1.1;
}
.tc-subtitle {
color: rgba(180,205,230,0.72);
font-size: .92rem;
line-height: 1.65;
}
.tc-toolbar {
background: linear-gradient(135deg, rgba(8,14,26,0.94), rgba(4,8,18,0.99));
border: 1px solid rgba(255,255,255,0.08);
border-radius: 14px;
padding: 16px;
margin: 0 0 16px 0;
}
.tc-topline {
display: flex;
gap: 12px;
align-items: center;
flex-wrap: wrap;
}
.tc-search-wrap {
flex: 1;
min-width: 260px;
position: relative;
}
.tc-search-icon {
position: absolute;
left: 13px;
top: 50%;
transform: translateY(-50%);
opacity: .55;
pointer-events: none;
}
.tc-search {
width: 100%;
padding: 11px 12px 11px 40px;
border-radius: 11px;
background: rgba(0,0,0,0.34);
border: 1px solid rgba(255,255,255,0.09);
color: #e8eef8;
font-size: .92rem;
outline: none;
font-family: inherit;
}
.tc-search:focus {
border-color: rgba(88,215,255,0.42);
box-shadow: 0 0 0 3px rgba(88,215,255,0.08);
}
.tc-toggle {
display: inline-flex;
align-items: center;
gap: 9px;
cursor: pointer;
padding: 10px 14px;
border-radius: 11px;
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.08);
color: rgba(180,205,230,0.75);
font-size: .82rem;
font-weight: 800;
white-space: nowrap;
user-select: none;
}
.tc-toggle input {
width: 16px;
height: 16px;
accent-color: var(--tc-cyan);
}
.tc-count {
padding: 10px 15px;
border-radius: 11px;
background: rgba(74,144,217,0.10);
border: 1px solid rgba(74,144,217,0.22);
color: var(--tc-cyan);
font-weight: 900;
font-size: .82rem;
white-space: nowrap;
}
.tc-tabs {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-top: 12px;
}
.tc-tab {
border: 1px solid rgba(255,255,255,0.08);
background: rgba(255,255,255,0.035);
color: rgba(180,205,230,0.68);
border-radius: 999px;
padding: 8px 13px;
cursor: pointer;
font-family: inherit;
font-weight: 800;
font-size: .78rem;
}
.tc-tab.active {
background: linear-gradient(135deg, rgba(74,144,217,0.22), rgba(74,144,217,0.08));
border-color: rgba(88,215,255,0.30);
color: var(--tc-cyan);
box-shadow: 0 0 16px rgba(88,215,255,0.08);
}
.tc-table {
border: 1px solid rgba(255,255,255,0.08);
border-radius: 14px;
overflow: hidden;
background: rgba(3,6,14,0.96);
}
.tc-table-head {
display: grid;
grid-template-columns: 88px 1.35fr 150px 145px 120px;
background: rgba(0,0,0,0.42);
border-bottom: 1px solid rgba(255,255,255,0.06);
}
.tc-th {
padding: 12px 14px;
color: var(--tc-muted);
font-weight: 900;
letter-spacing: .08em;
text-transform: uppercase;
font-size: .68rem;
border-right: 1px solid rgba(255,255,255,0.045);
}
.tc-th.sortable {
cursor: pointer;
}
.tc-row {
display: grid;
grid-template-columns: 88px 1.35fr 150px 145px 120px;
align-items: center;
border-bottom: 1px solid rgba(255,255,255,0.045);
cursor: pointer;
transition: background .15s ease, border-color .15s ease;
}
.tc-row:hover {
background: rgba(74,144,217,0.06);
}
.tc-cell {
padding: 13px 14px;
border-right: 1px solid rgba(255,255,255,0.035);
}
.tc-id {
font-family: 'Courier New', monospace;
color: var(--tc-gold);
font-weight: 900;
}
.tc-name-line {
display: flex;
align-items: center;
gap: 12px;
min-width: 0;
}
.tc-icon {
width: 42px;
height: 42px;
border-radius: 10px;
background: rgba(0,0,0,0.28);
border: 1px solid rgba(255,255,255,0.08);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
flex: 0 0 42px;
}
.tc-icon img {
max-width: 36px;
max-height: 36px;
object-fit: contain;
image-rendering: auto;
}
.tc-card-name {
color: var(--tc-cyan);
font-weight: 900;
font-size: .96rem;
line-height: 1.2;
}
.tc-monster-name {
color: rgba(180,205,230,0.58);
font-size: .76rem;
margin-top: 3px;
}
.tc-pill {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 6px 10px;
border-radius: 999px;
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.08);
color: rgba(200,220,240,0.72);
font-weight: 800;
font-size: .76rem;
white-space: nowrap;
}
.tc-status {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 6px 10px;
border-radius: 999px;
font-weight: 900;
font-size: .72rem;
white-space: nowrap;
}
.tc-changed {
background: rgba(112,216,144,0.10);
border: 1px solid rgba(112,216,144,0.24);
color: var(--tc-green);
}
.tc-original {
background: rgba(255,255,255,0.035);
border: 1px solid rgba(255,255,255,0.08);
color: rgba(180,205,230,0.55);
}
.tc-detail {
display: none;
grid-column: 1 / -1;
padding: 0;
background: rgba(0,0,0,0.18);
border-bottom: 1px solid rgba(255,255,255,0.055);
}
.tc-row.open + .tc-detail {
display: block;
}
.tc-detail-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 12px;
padding: 16px;
}
.tc-effect-box {
border-radius: 12px;
padding: 14px;
background: linear-gradient(135deg, rgba(8,14,26,0.96), rgba(4,8,18,0.99));
border: 1px solid rgba(255,255,255,0.08);
min-height: 112px;
}
.tc-effect-title {
color: var(--tc-muted);
font-weight: 900;
letter-spacing: .08em;
text-transform: uppercase;
font-size: .68rem;
margin-bottom: 8px;
}
.tc-effect-text {
color: rgba(220,235,255,0.78);
font-size: .84rem;
line-height: 1.65;
white-space: pre-wrap;
}
.tc-effect-box.new {
border-color: rgba(112,216,144,0.18);
}
.tc-effect-box.collection {
border-color: rgba(240,200,64,0.18);
}
.tc-empty,
.tc-loading,
.tc-error {
padding: 34px;
text-align: center;
color: rgba(180,205,230,0.58);
font-weight: 700;
}
.tc-error {
color: #ff8a8a;
}
.tc-groups {
margin-top: 26px;
}
.tc-group-list {
display: grid;
gap: 10px;
}
.tc-group-card {
border-radius: 14px;
border: 1px solid rgba(255,255,255,0.08);
background: linear-gradient(135deg, rgba(8,14,26,0.94), rgba(4,8,18,0.99));
overflow: hidden;
}
.tc-group-head {
padding: 15px 18px;
display: grid;
grid-template-columns: auto 1fr auto;
gap: 13px;
align-items: center;
}
.tc-group-icon {
width: 42px;
height: 42px;
border-radius: 12px;
background: rgba(74,144,217,0.10);
border: 1px solid rgba(74,144,217,0.22);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25rem;
}
.tc-group-key {
color: var(--tc-muted);
font-size: .64rem;
font-weight: 900;
letter-spacing: .11em;
text-transform: uppercase;
margin-bottom: 2px;
}
.tc-group-name {
color: #fff;
font-weight: 900;
}
.tc-group-bonus {
padding: 11px 18px;
border-top: 1px solid rgba(255,255,255,0.055);
color: rgba(220,235,255,0.74);
line-height: 1.55;
font-size: .84rem;
}
@media (max-width: 900px) {
.tc-table-head {
display: none;
}
.tc-row {
grid-template-columns: 1fr;
padding: 12px;
}
.tc-cell {
border-right: none;
padding: 7px 4px;
}
.tc-detail-grid {
grid-template-columns: 1fr;
}
.tc-topline {
align-items: stretch;
}
.tc-toggle,
.tc-count {
width: 100%;
justify-content: center;
}
}
`;
document.head.appendChild(style);
}
function createShell(root) {
root.innerHTML = `
<div class="tc-shell">
<div class="tc-head">
<div class="tc-kicker">◇ Banco de Dados</div>
<h2 class="tc-title">Todas as Cartas Balanceadas</h2>
<div class="tc-subtitle">
Pesquise por nome da carta, ID, monstro, slot, efeito antigo, efeito novo, bônus de coleção ou script de coleção.
</div>
</div>
<div class="tc-toolbar">
<div class="tc-topline">
<div class="tc-search-wrap">
<span class="tc-search-icon">🔍</span>
<input class="tc-search" id="tc-search" type="text" placeholder="Buscar carta, ID, monstro, efeito ou coleção..." autocomplete="off">
</div>
<label class="tc-toggle" for="tc-changed">
<input id="tc-changed" type="checkbox">
⚡ Apenas alteradas
</label>
<div class="tc-count" id="tc-count">— cartas</div>
</div>
<div class="tc-tabs" id="tc-tabs"></div>
</div>
<div class="tc-table">
<div class="tc-table-head">
<div class="tc-th sortable" data-sort="id">ID ↕</div>
<div class="tc-th sortable" data-sort="name">Carta / Monstro ↕</div>
<div class="tc-th sortable" data-sort="slot">Slot ↕</div>
<div class="tc-th sortable" data-sort="category">Categoria ↕</div>
<div class="tc-th">Status</div>
</div>
<div id="tc-body">
<div class="tc-loading">Carregando cartas...</div>
</div>
</div>
<div class="tc-groups">
<div class="tc-head" style="margin-top:26px;">
<div class="tc-kicker">◇ Grupos de Coleção</div>
<h2 class="tc-title" style="font-size:1.35rem;">Bônus permanentes por grupo</h2>
<div class="tc-subtitle">Dados carregados da tabela <strong>wiki_card_groups</strong>.</div>
</div>
<div class="tc-group-list" id="tc-groups"></div>
</div>
</div>
`;
const tabs = root.querySelector('#tc-tabs');
categories.forEach(cat => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'tc-tab' + (cat.id === state.category ? ' active' : '');
btn.dataset.category = cat.id;
btn.innerHTML = `${cat.icon} ${escapeHtml(cat.label)}`;
btn.addEventListener('click', function () {
state.category = cat.id;
refresh(root);
});
tabs.appendChild(btn);
});
root.querySelector('#tc-search').addEventListener('input', debounce(function (e) {
state.q = e.target.value.trim();
refresh(root);
}, 220));
root.querySelector('#tc-changed').addEventListener('change', function (e) {
state.changed = !!e.target.checked;
refresh(root);
});
root.querySelectorAll('.tc-th.sortable').forEach(th => {
th.addEventListener('click', function () {
const sort = th.dataset.sort;
if (state.sort === sort) {
state.dir = state.dir === 'asc' ? 'desc' : 'asc';
} else {
state.sort = sort;
state.dir = 'asc';
}
refresh(root);
});
});
}
async function fetchCards(root) {
state.loading = true;
state.error = '';
renderBody(root);
const params = new URLSearchParams();
params.set('q', state.q);
params.set('category', state.category);
params.set('changed', state.changed ? '1' : '0');
params.set('sort', state.sort);
params.set('dir', state.dir);
try {
const response = await fetch(state.api + '?' + params.toString(), {
credentials: 'same-origin',
cache: 'no-store'
});
const text = await response.text();
let data;
try {
data = JSON.parse(text);
} catch (err) {
throw new Error('Invalid JSON returned by API: ' + text.slice(0, 300));
}
if (!data.ok) {
throw new Error(data.error || 'API returned an unknown error.');
}
state.cards = Array.isArray(data.cards) ? data.cards : [];
state.groups = Array.isArray(data.groups) ? data.groups : [];
state.expandedCardId = null;
} catch (err) {
state.cards = [];
state.groups = [];
state.error = err.message || String(err);
} finally {
state.loading = false;
renderAll(root);
}
}
function refresh(root) {
updateTabs(root);
fetchCards(root);
}
function updateTabs(root) {
root.querySelectorAll('.tc-tab').forEach(btn => {
btn.classList.toggle('active', btn.dataset.category === state.category);
});
}
function renderAll(root) {
renderBody(root);
renderGroups(root);
const count = root.querySelector('#tc-count');
if (count) {
count.textContent = state.cards.length + ' carta' + (state.cards.length === 1 ? '' : 's');
}
}
function renderBody(root) {
const body = root.querySelector('#tc-body');
if (!body) return;
if (state.loading) {
body.innerHTML = '<div class="tc-loading">Carregando cartas...</div>';
return;
}
if (state.error) {
body.innerHTML = '<div class="tc-error">Erro ao carregar cartas: ' + escapeHtml(state.error) + '</div>';
return;
}
if (!state.cards.length) {
body.innerHTML = '<div class="tc-empty">Nenhuma carta encontrada com os filtros atuais.</div>';
return;
}
body.innerHTML = '';
state.cards.forEach(card => {
const meta = categoryMeta(card.category);
const row = document.createElement('div');
row.className = 'tc-row';
row.dataset.cardId = card.card_id;
const monster = card.monster_name ? `<div class="tc-monster-name">${escapeHtml(card.monster_name)}</div>` : '';
const group = card.collection_group_name || '—';
row.innerHTML = `
<div class="tc-cell tc-id">#${escapeHtml(card.card_id)}</div>
<div class="tc-cell">
<div class="tc-name-line">
<div class="tc-icon">
<img src="${cardIconUrl(card.card_id)}" alt="${escapeHtml(card.card_name)}" onerror="this.style.display='none';this.parentNode.textContent='🃏';">
</div>
<div>
<div class="tc-card-name">
<a href="${cardLink(card.card_id)}" target="_blank" rel="noopener" style="color:inherit;text-decoration:none;">${escapeHtml(card.card_name)}</a>
</div>
${monster}
</div>
</div>
</div>
<div class="tc-cell"><span class="tc-pill">${escapeHtml(card.slot_type || 'Unknown')}</span></div>
<div class="tc-cell"><span class="tc-pill" style="border-color:${meta.color}44;color:${meta.color};">${meta.icon} ${escapeHtml(group)}</span></div>
<div class="tc-cell">${changedBadge(card)}</div>
`;
const detail = document.createElement('div');
detail.className = 'tc-detail';
detail.innerHTML = `
<div class="tc-detail-grid">
<div class="tc-effect-box">
<div class="tc-effect-title">Efeito Antigo</div>
<div class="tc-effect-text">${escapeHtml(card.old_effect || '—')}</div>
</div>
<div class="tc-effect-box new">
<div class="tc-effect-title">Efeito Novo</div>
<div class="tc-effect-text">${escapeHtml(card.new_effect || '—')}</div>
</div>
<div class="tc-effect-box collection">
<div class="tc-effect-title">Coleção</div>
<div class="tc-effect-text"><strong style="color:#f0c840;">Grupo:</strong> ${escapeHtml(card.collection_group_name || '—')}<br><strong style="color:#f0c840;">Pontos:</strong> ${escapeHtml(card.collection_points || 0)}<br><strong style="color:#f0c840;">Efeito:</strong> ${escapeHtml(card.collection_effect || '—')}<br><strong style="color:#f0c840;">Script:</strong> ${escapeHtml(scriptToReadable(card.collection_script))}</div>
</div>
</div>
`;
row.addEventListener('click', function () {
const isOpen = row.classList.contains('open');
body.querySelectorAll('.tc-row.open').forEach(r => r.classList.remove('open'));
if (!isOpen) row.classList.add('open');
});
body.appendChild(row);
body.appendChild(detail);
});
}
function renderGroups(root) {
const wrap = root.querySelector('#tc-groups');
if (!wrap) return;
if (state.error) {
wrap.innerHTML = '';
return;
}
if (!state.groups.length) {
wrap.innerHTML = '<div class="tc-empty">Nenhum grupo de coleção cadastrado.</div>';
return;
}
wrap.innerHTML = '';
state.groups.forEach(group => {
const color = group.color || '#7c6aff';
const el = document.createElement('div');
el.className = 'tc-group-card';
el.style.borderColor = color + '33';
el.innerHTML = `
<div class="tc-group-head">
<div class="tc-group-icon" style="border-color:${color}44;background:${color}14;">${escapeHtml(group.icon || '🃏')}</div>
<div>
<div class="tc-group-key" style="color:${color};">Grupo · ${escapeHtml(group.group_key)}</div>
<div class="tc-group-name">${escapeHtml(group.group_name)}</div>
</div>
<div class="tc-pill" style="border-color:${color}33;color:${color};">${escapeHtml(group.card_count || 0)} cartas · Min ${escapeHtml(group.min_cards || 1)}</div>
</div>
<div class="tc-group-bonus">
<strong style="color:${color};">Bônus:</strong> ${escapeHtml(group.collection_effect || '—')}
</div>
`;
wrap.appendChild(el);
});
}
function initCardDb() {
const root = getRoot();
if (!root || root.dataset.tcReady === '1') return;
root.dataset.tcReady = '1';
state.api = root.dataset.api || '/pt/api/wiki_cards.php';
injectStyles();
createShell(root);
fetchCards(root);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initCardDb);
} else {
initCardDb();
}
if (window.mw && mw.hook) {
mw.hook('wikipage.content').add(initCardDb);
}
})();
if (mw && mw.hook) {
mw.hook('wikipage.content').add(function ($content) {
init(($content && $content[0]) || document);
});
}
})(jQuery, mediaWiki);