MediaWiki:Common.js: Difference between revisions

From TimeRO Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 2: Line 2:
(function ($, mw) {
(function ($, mw) {
'use strict';
'use strict';
/* =========================================================
  GLOBAL BASICS
========================================================= */


function setLogoHref() {
function setLogoHref() {
Line 11: Line 15:


function ensureFontAwesome() {
function ensureFontAwesome() {
if (!document.querySelector('link[data-timero-fa]')) {
if (document.querySelector('link[data-timero-fa]')) return;
var fa = document.createElement('link');
 
fa.rel = 'stylesheet';
var fa = document.createElement('link');
fa.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css';
fa.rel = 'stylesheet';
fa.setAttribute('data-timero-fa', '1');
fa.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css';
document.head.appendChild(fa);
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);
}
});
}
}


/* =========================
/* =========================================================
  ROUTES
  ROUTES
========================= */
========================================================= */


function resetRoutePill(pill) {
function resetRoutePill(pill) {
Line 44: Line 68:


var details = section.querySelectorAll('.route-detail');
var details = section.querySelectorAll('.route-detail');
var pills = section.querySelectorAll('.route-pill[data-route]');
for (var i = 0; i < details.length; i++) {
for (var i = 0; i < details.length; i++) {
details[i].style.display = 'none';
details[i].style.display = 'none';
}
}


var pills = section.querySelectorAll('.route-pill[data-route]');
for (var j = 0; j < pills.length; j++) {
for (var j = 0; j < pills.length; j++) {
resetRoutePill(pills[j]);
resetRoutePill(pills[j]);
Line 96: Line 121:
var pills = section.querySelectorAll('.route-pill[data-route]');
var pills = section.querySelectorAll('.route-pill[data-route]');
for (var i = 0; i < pills.length; i++) {
for (var i = 0; i < pills.length; i++) {
var pill = pills[i];
(function (pill) {
if (pill.dataset.timeroBound === '1') continue;
bindActionButton(pill, function () {
 
pill.dataset.timeroBound = '1';
pill.setAttribute('role', 'button');
pill.setAttribute('tabindex', '0');
 
pill.addEventListener('click', function () {
expandRoute(this.getAttribute('data-route'));
});
 
pill.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
expandRoute(this.getAttribute('data-route'));
expandRoute(this.getAttribute('data-route'));
}
});
});
})(pills[i]);
}
}


var closeButtons = section.querySelectorAll('.route-close[data-route]');
var closeButtons = section.querySelectorAll('.route-close[data-route]');
for (var j = 0; j < closeButtons.length; j++) {
for (var j = 0; j < closeButtons.length; j++) {
var closeBtn = closeButtons[j];
(function (closeBtn) {
if (closeBtn.dataset.timeroBound === '1') continue;
bindActionButton(closeBtn, function () {
 
closeBtn.dataset.timeroBound = '1';
closeBtn.setAttribute('role', 'button');
closeBtn.setAttribute('tabindex', '0');
 
closeBtn.addEventListener('click', function () {
expandRoute(this.getAttribute('data-route'));
});
 
closeBtn.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
expandRoute(this.getAttribute('data-route'));
expandRoute(this.getAttribute('data-route'));
}
});
});
})(closeButtons[j]);
}
}
}
}


/* =========================
/* =========================================================
  METHODS
  METHODS
========================= */
========================================================= */


function resetMethodTab(tab, type) {
function resetMethodTab(tab, type) {
Line 178: Line 179:


var panels = section.querySelectorAll('.method-panel');
var panels = section.querySelectorAll('.method-panel');
var tabs = section.querySelectorAll('.method-tab[data-method]');
for (var i = 0; i < panels.length; i++) {
for (var i = 0; i < panels.length; i++) {
panels[i].style.display = 'none';
panels[i].style.display = 'none';
}
}


var tabs = section.querySelectorAll('.method-tab[data-method]');
for (var j = 0; j < tabs.length; j++) {
for (var j = 0; j < tabs.length; j++) {
resetMethodTab(tabs[j], tabs[j].getAttribute('data-method'));
resetMethodTab(tabs[j], tabs[j].getAttribute('data-method'));
Line 206: Line 208:
var tabs = section.querySelectorAll('.method-tab[data-method]');
var tabs = section.querySelectorAll('.method-tab[data-method]');
for (var i = 0; i < tabs.length; i++) {
for (var i = 0; i < tabs.length; i++) {
var tab = tabs[i];
(function (tab) {
if (tab.dataset.timeroBound === '1') continue;
bindActionButton(tab, function () {
 
tab.dataset.timeroBound = '1';
tab.setAttribute('role', 'button');
tab.setAttribute('tabindex', '0');
 
tab.addEventListener('click', function () {
switchMethod(this.getAttribute('data-method'));
});
 
tab.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
switchMethod(this.getAttribute('data-method'));
switchMethod(this.getAttribute('data-method'));
}
});
});
})(tabs[i]);
}
}


if (!section.getAttribute('data-open-method')) {
if (!section.getAttribute('data-open-method')) {
switchMethod('grind');
switchMethod('grind');
} else {
switchMethod(section.getAttribute('data-open-method'));
}
}
}
}


/* =========================
/* =========================================================
  CALCULATOR
  CALCULATOR
========================= */
========================================================= */


var calcState = {
var calcState = {
hours: 2,
hours: 2,
booster: false,
booster: false,
rate: 70,
rate: 70,
label: 'Rota Mid-Game (70M/hr)',
label: 'Rota Mid-Game (70M/hr)',
weeklyTarget: 12000
weeklyTarget: 12000
};
};


function formatMillions(value) {
function formatMillions(value) {
var rounded = Math.round(value * 10) / 10;
var rounded = Math.round(value * 10) / 10;


if (rounded >= 1000) {
if (rounded >= 1000) {
var billions = rounded / 1000;
var billions = rounded / 1000;
var bText = (Math.round(billions * 10) / 10).toString().replace('.0', '').replace('.', ',');
var bText = (Math.round(billions * 10) / 10).toString().replace('.0', '').replace('.', ',');
return bText + 'B z';
return bText + 'B z';
}
 
var mText = rounded.toString().replace('.0', '').replace('.', ',');
return mText + 'M z';
}
}


var mText = rounded.toString().replace('.0', '').replace('.', ',');
function refreshCalcMethodUI(section) {
return mText + 'M z';
if (!section) return;
}


function refreshCalcMethodUI(section) {
var labelEl = section.querySelector('#calc-method-label');
if (!section) return;
var menuEl = section.querySelector('#calc-method-menu');
var options = section.querySelectorAll('.calc-method-option');


var labelEl = section.querySelector('#calc-method-label');
if (labelEl) {
var menuEl = section.querySelector('#calc-method-menu');
labelEl.textContent = calcState.label;
var options = section.querySelectorAll('.calc-method-option');
}


if (labelEl) {
for (var i = 0; i < options.length; i++) {
labelEl.textContent = calcState.label;
var opt = options[i];
}
var isActive = String(opt.getAttribute('data-rate')) === String(calcState.rate);


for (var i = 0; i < options.length; i++) {
if (isActive) {
var opt = options[i];
opt.style.background = 'rgba(0,212,255,0.12)';
var isActive = String(opt.getAttribute('data-rate')) === String(calcState.rate);
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 (isActive) {
if (menuEl) {
opt.style.background = 'rgba(0,212,255,0.12)';
menuEl.style.display = section.getAttribute('data-method-menu-open') === '1' ? 'block' : 'none';
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) {
window.adjustCalc = function (field, delta) {
menuEl.style.display = section.getAttribute('data-method-menu-open') === '1' ? 'block' : 'none';
if (field !== 'hours') return;
}
}


window.adjustCalc = function (field, delta) {
calcState.hours = Math.max(0.5, Math.min(24, calcState.hours + delta));
if (field !== 'hours') return;
updateCalc();
calcState.hours = Math.max(0.5, Math.min(24, calcState.hours + delta));
};
updateCalc();
};


window.setBooster = function (enabled) {
window.setBooster = function (enabled) {
calcState.booster = !!enabled;
calcState.booster = !!enabled;
updateCalc();
updateCalc();
};
};


window.setCalcMethod = function (rate, label) {
window.setCalcMethod = function (rate, label) {
calcState.rate = parseFloat(rate || '0');
calcState.rate = parseFloat(rate || '0');
calcState.label = label || '';
calcState.label = label || '';
var section = document.getElementById('calculator-section');
if (section) {
section.setAttribute('data-method-menu-open', '0');
}
updateCalc();
};


window.toggleCalcMethodMenu = function () {
var section = document.getElementById('calculator-section');
var section = document.getElementById('calculator-section');
if (section) {
if (!section) return;
section.setAttribute('data-method-menu-open', '0');
}


var isOpen = section.getAttribute('data-method-menu-open') === '1';
updateCalc();
section.setAttribute('data-method-menu-open', isOpen ? '0' : '1');
};
refreshCalcMethodUI(section);
};


window.closeCalcMethodMenu = function () {
window.toggleCalcMethodMenu = function () {
var section = document.getElementById('calculator-section');
var section = document.getElementById('calculator-section');
if (!section) return;
if (!section) return;
section.setAttribute('data-method-menu-open', '0');
refreshCalcMethodUI(section);
};


window.updateCalc = function () {
var isOpen = section.getAttribute('data-method-menu-open') === '1';
var section = document.getElementById('calculator-section');
section.setAttribute('data-method-menu-open', isOpen ? '0' : '1');
if (!section) return;
refreshCalcMethodUI(section);
};


var hoursEl = document.getElementById('calc-hours');
window.closeCalcMethodMenu = function () {
var resultSession = document.getElementById('result-session');
var section = document.getElementById('calculator-section');
var resultDay = document.getElementById('result-day');
if (!section) return;
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;
section.setAttribute('data-method-menu-open', '0');
var session = calcState.hours * effectiveRate;
refreshCalcMethodUI(section);
var day = session;
};
var week = day * 7;
var pct = Math.max(0, Math.min(100, Math.round((week / calcState.weeklyTarget) * 100)));


if (hoursEl) {
window.updateCalc = function () {
hoursEl.textContent = calcState.hours % 1 === 0 ? String(calcState.hours) : String(calcState.hours).replace('.', ',');
var section = document.getElementById('calculator-section');
}
if (!section) return;
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) {
var hoursEl = document.getElementById('calc-hours');
if (calcState.booster) {
var resultSession = document.getElementById('result-session');
boostNo.style.background = 'rgba(255,255,255,0.04)';
var resultDay = document.getElementById('result-day');
boostNo.style.borderColor = 'rgba(255,255,255,0.09)';
var resultWeek = document.getElementById('result-week');
boostNo.style.color = 'rgba(122,144,176,0.60)';
var resultPct = document.getElementById('result-pct');
boostNo.style.fontWeight = '700';
var resultBar = document.getElementById('result-bar');
var boostNo = document.getElementById('boost-no');
var boostYes = document.getElementById('boost-yes');


boostYes.style.background = 'rgba(0,212,255,0.15)';
var effectiveRate = calcState.booster ? calcState.rate * 1.5 : calcState.rate;
boostYes.style.borderColor = 'rgba(0,212,255,0.30)';
var session = calcState.hours * effectiveRate;
boostYes.style.color = '#00d4ff';
var day = session;
boostYes.style.fontWeight = '800';
var week = day * 7;
} else {
var pct = Math.max(0, Math.min(100, Math.round((week / calcState.weeklyTarget) * 100)));
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)';
if (hoursEl) {
boostYes.style.borderColor = 'rgba(255,255,255,0.09)';
hoursEl.textContent = calcState.hours % 1 === 0 ? String(calcState.hours) : String(calcState.hours).replace('.', ',');
boostYes.style.color = 'rgba(122,144,176,0.60)';
boostYes.style.fontWeight = '700';
}
}
}
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 + '%';


refreshCalcMethodUI(section);
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';


function bindCalculator(context) {
boostYes.style.background = 'rgba(0,212,255,0.15)';
var scope = context && context.querySelector ? context : document;
boostYes.style.borderColor = 'rgba(0,212,255,0.30)';
var section = scope.querySelector('#calculator-section') || document.getElementById('calculator-section');
boostYes.style.color = '#00d4ff';
if (!section) return;
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';


var adjusters = section.querySelectorAll('[data-calc-adjust]');
boostYes.style.background = 'rgba(255,255,255,0.04)';
for (var i = 0; i < adjusters.length; i++) {
boostYes.style.borderColor = 'rgba(255,255,255,0.09)';
var adjuster = adjusters[i];
boostYes.style.color = 'rgba(122,144,176,0.60)';
if (adjuster.dataset.timeroBound === '1') continue;
boostYes.style.fontWeight = '700';
 
adjuster.dataset.timeroBound = '1';
adjuster.setAttribute('role', 'button');
adjuster.setAttribute('tabindex', '0');
 
adjuster.addEventListener('click', function (e) {
e.stopPropagation();
adjustCalc(this.getAttribute('data-calc-adjust'), parseFloat(this.getAttribute('data-delta') || '0'));
});
 
adjuster.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
adjustCalc(this.getAttribute('data-calc-adjust'), parseFloat(this.getAttribute('data-delta') || '0'));
}
}
});
}
}


var boosters = section.querySelectorAll('[data-booster]');
refreshCalcMethodUI(section);
for (var j = 0; j < boosters.length; j++) {
};
var booster = boosters[j];
if (booster.dataset.timeroBound === '1') continue;


booster.dataset.timeroBound = '1';
function bindCalculator(context) {
booster.setAttribute('role', 'button');
var scope = context && context.querySelector ? context : document;
booster.setAttribute('tabindex', '0');
var section = scope.querySelector('#calculator-section') || document.getElementById('calculator-section');
if (!section) return;


booster.addEventListener('click', function (e) {
var adjusters = section.querySelectorAll('[data-calc-adjust]');
e.stopPropagation();
for (var i = 0; i < adjusters.length; i++) {
setBooster(this.getAttribute('data-booster') === '1');
(function (adjuster) {
});
bindActionButton(adjuster, function (e) {
e.stopPropagation();
adjustCalc(this.getAttribute('data-calc-adjust'), parseFloat(this.getAttribute('data-delta') || '0'));
});
})(adjusters[i]);
}


booster.addEventListener('keydown', function (e) {
var boosters = section.querySelectorAll('[data-booster]');
if (e.key === 'Enter' || e.key === ' ') {
for (var j = 0; j < boosters.length; j++) {
e.preventDefault();
(function (booster) {
setBooster(this.getAttribute('data-booster') === '1');
bindActionButton(booster, function (e) {
}
e.stopPropagation();
});
setBooster(this.getAttribute('data-booster') === '1');
}
});
 
})(boosters[j]);
var methodDisplay = section.querySelector('#calc-method-display');
}
if (methodDisplay && methodDisplay.dataset.timeroBound !== '1') {
methodDisplay.dataset.timeroBound = '1';
methodDisplay.setAttribute('role', 'button');
methodDisplay.setAttribute('tabindex', '0');
 
methodDisplay.addEventListener('click', function (e) {
e.stopPropagation();
toggleCalcMethodMenu();
});


methodDisplay.addEventListener('keydown', function (e) {
var methodDisplay = section.querySelector('#calc-method-display');
if (e.key === 'Enter' || e.key === ' ') {
if (methodDisplay) {
e.preventDefault();
bindActionButton(methodDisplay, function (e) {
e.stopPropagation();
toggleCalcMethodMenu();
toggleCalcMethodMenu();
}
});
});
}
}


var options = section.querySelectorAll('.calc-method-option');
var options = section.querySelectorAll('.calc-method-option');
for (var k = 0; k < options.length; k++) {
for (var k = 0; k < options.length; k++) {
var option = options[k];
(function (option) {
if (option.dataset.timeroBound === '1') continue;
bindActionButton(option, function (e) {
e.stopPropagation();
setCalcMethod(this.getAttribute('data-rate'), this.getAttribute('data-label'));
});
})(options[k]);
}


option.dataset.timeroBound = '1';
if (!window.__timeroCalcDocBound) {
option.setAttribute('role', 'button');
window.__timeroCalcDocBound = true;
option.setAttribute('tabindex', '0');


option.addEventListener('click', function (e) {
document.addEventListener('click', function (e) {
e.stopPropagation();
var liveSection = document.getElementById('calculator-section');
setCalcMethod(this.getAttribute('data-rate'), this.getAttribute('data-label'));
if (!liveSection) return;
});


option.addEventListener('keydown', function (e) {
if (!liveSection.contains(e.target)) {
if (e.key === 'Enter' || e.key === ' ') {
closeCalcMethodMenu();
e.preventDefault();
}
setCalcMethod(this.getAttribute('data-rate'), this.getAttribute('data-label'));
});
}
}
});
}


if (section.dataset.calcDocBound !== '1') {
if (!section.getAttribute('data-method-menu-open')) {
section.dataset.calcDocBound = '1';
section.setAttribute('data-method-menu-open', '0');
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')) {
updateCalc();
section.setAttribute('data-method-menu-open', '0');
}
}


updateCalc();
/* =========================================================
}
 
/* =========================
  LOADOUT
  LOADOUT
========================= */
========================================================= */


function setLoadoutItemState(item, active) {
function setLoadoutItemState(item, active) {
if (!item) return;
var check = item.querySelector('.li-check');
var check = item.querySelector('.li-check');
if (!check) return;
if (!check) return;
Line 501: Line 454:
item.style.borderColor = 'rgba(176,108,255,0.24)';
item.style.borderColor = 'rgba(176,108,255,0.24)';
item.style.boxShadow = '0 0 0 1px rgba(176,108,255,0.05) inset';
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.background = 'rgba(176,108,255,0.16)';
check.style.borderColor = 'rgba(176,108,255,0.45)';
check.style.borderColor = 'rgba(176,108,255,0.45)';
Line 510: Line 464:
item.style.borderColor = 'rgba(255,255,255,0.07)';
item.style.borderColor = 'rgba(255,255,255,0.07)';
item.style.boxShadow = 'none';
item.style.boxShadow = 'none';
check.style.background = 'rgba(176,108,255,0.04)';
check.style.background = 'rgba(176,108,255,0.04)';
check.style.borderColor = 'rgba(176,108,255,0.30)';
check.style.borderColor = 'rgba(176,108,255,0.30)';
Line 541: Line 496:
window.toggleLoadout = function (element) {
window.toggleLoadout = function (element) {
if (!element) return;
if (!element) return;
var active = !element.classList.contains('is-active');
var active = !element.classList.contains('is-active');
setLoadoutItemState(element, active);
setLoadoutItemState(element, active);
Line 565: Line 521:
var items = section.querySelectorAll('.loadout-item');
var items = section.querySelectorAll('.loadout-item');
for (var i = 0; i < items.length; i++) {
for (var i = 0; i < items.length; i++) {
var item = items[i];
(function (item) {
if (item.dataset.timeroBound === '1') continue;
setLoadoutItemState(item, item.classList.contains('is-active'));
 
item.dataset.timeroBound = '1';
item.setAttribute('role', 'button');
item.setAttribute('tabindex', '0');
 
setLoadoutItemState(item, false);
 
item.addEventListener('click', function () {
toggleLoadout(this);
});


item.addEventListener('keydown', function (e) {
bindActionButton(item, function () {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
toggleLoadout(this);
toggleLoadout(this);
}
});
});
})(items[i]);
}
}


var clearBtn = section.querySelector('.loadout-clear');
var clearBtn = section.querySelector('.loadout-clear');
if (clearBtn && clearBtn.dataset.timeroBound !== '1') {
if (clearBtn) {
clearBtn.dataset.timeroBound = '1';
bindActionButton(clearBtn, function () {
clearBtn.setAttribute('role', 'button');
clearBtn.setAttribute('tabindex', '0');
 
clearBtn.addEventListener('click', function () {
clearLoadout();
clearLoadout();
});
clearBtn.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
clearLoadout();
}
});
});
}
}
Line 606: Line 539:
updateLoadoutSummary();
updateLoadoutSummary();
}
}
/* =========================================================
  INIT
========================================================= */


function init(context) {
function init(context) {

Revision as of 17:32, 13 April 2026

/* 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);
			}
		});
	}

	/* =========================================================
	   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';
		}

		var mText = rounded.toString().replace('.0', '').replace('.', ',');
		return mText + 'M 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';
		}
	}

	window.adjustCalc = function (field, delta) {
		if (field !== 'hours') return;

		calcState.hours = Math.max(0.5, Math.min(24, calcState.hours + delta));
		updateCalc();
	};

	window.setBooster = function (enabled) {
		calcState.booster = !!enabled;
		updateCalc();
	};

	window.setCalcMethod = function (rate, label) {
		calcState.rate = parseFloat(rate || '0');
		calcState.label = label || '';

		var section = document.getElementById('calculator-section');
		if (section) {
			section.setAttribute('data-method-menu-open', '0');
		}

		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;

		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'), parseFloat(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]);
		}

		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 += parseFloat(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();
	}

	/* =========================================================
	   INIT
	========================================================= */

	function init(context) {
		setLogoHref();
		ensureFontAwesome();
		bindMethodViewer(context || document);
		bindRouteViewer(context || document);
		bindCalculator(context || document);
		bindLoadout(context || document);
	}

	$(function () {
		init(document);
	});

	if (mw && mw.hook) {
		mw.hook('wikipage.content').add(function ($content) {
			init(($content && $content[0]) || document);
		});
	}
})(jQuery, mediaWiki);