MediaWiki:Common.js

From EQOA Sandstorm Wiki
Revision as of 02:07, 27 May 2026 by DevRenodor (talk | contribs)
Jump to navigationJump 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 () {
    function rawPage(title) {
        return mw.config.get('wgScript') +
            '?title=' + encodeURIComponent(title) +
            '&action=raw&ctype=text/javascript';
    }

    function loadScriptUrl(url) {
        return $.getScript(url);
    }

    function loadWikiScript(title) {
        return loadScriptUrl(rawPage(title));
    }

    function applyWikiTheme(theme) {
        var isLight = theme === 'light';
        var button = document.getElementById('eqoa-theme-toggle');

        document.documentElement.classList.toggle('eqoa-theme-light', isLight);
        document.documentElement.classList.toggle('eqoa-theme-dark', !isLight);

        if (document.body) {
            document.body.classList.toggle('eqoa-theme-light', isLight);
            document.body.classList.toggle('eqoa-theme-dark', !isLight);
        }

        try {
            localStorage.setItem('eqoa-wiki-theme', isLight ? 'light' : 'dark');
            localStorage.setItem('eqoa-builder-theme', isLight ? 'light' : 'dark');
            localStorage.setItem('eqoa-cm-theme', isLight ? 'light' : 'dark');
        } catch (e) {}

        if (button) {
            button.innerHTML = isLight ? '☾' : '☼';
            button.title = isLight ? 'Switch to Dark Theme' : 'Switch to White Theme';
            button.setAttribute(
                'aria-label',
                isLight ? 'Switch to Dark Theme' : 'Switch to White Theme'
            );
        }

       if (window.dispatchEvent) {
    window.dispatchEvent(new CustomEvent('eqoa-wiki-theme-change', {
        detail: {
            theme: isLight ? 'light' : 'dark'
        }
    }));
}

setTimeout(styleRareEncounterRows, 50);
setTimeout(buildRareEncounterMobileLayout, 50);
    }

    function getSavedWikiTheme() {
        try {
            return localStorage.getItem('eqoa-wiki-theme') === 'light'
                ? 'light'
                : 'dark';
        } catch (e) {
            return 'dark';
        }
    }

    function ensureThemeButton() {
        var button = document.getElementById('eqoa-theme-toggle');

        if (!button) {
            button = document.createElement('button');
            button.id = 'eqoa-theme-toggle';
            button.type = 'button';
            button.className = 'eqoa-theme-toggle';

            button.addEventListener('click', function () {
                var isLight = document.body.classList.contains('eqoa-theme-light');
                applyWikiTheme(isLight ? 'dark' : 'light');
            });

            document.body.appendChild(button);
        }

        applyWikiTheme(getSavedWikiTheme());
    }

    function initRaidChecks() {
        var containers = document.querySelectorAll('.raid-checkboxify');

        Array.prototype.forEach.call(containers, function (container) {
            var links = container.querySelectorAll('a');

            Array.prototype.forEach.call(links, function (a) {
                var cb;
                var label;
                var key;
                var stored;

                if (a.classList.contains('raid-processed')) return;

                cb = document.createElement('input');
                cb.type = 'checkbox';
                cb.className = 'raid-checkbox-input';
                cb.style.marginRight = '8px';
                cb.style.verticalAlign = 'middle';

                label = document.createElement('label');
                label.style.display = 'block';
                label.style.cursor = 'pointer';

                a.parentNode.insertBefore(label, a);
                label.appendChild(cb);
                label.appendChild(a);

                a.classList.add('raid-processed');

                key = 'raidcheck:' +
                    (document.title || location.pathname) +
                    ':' +
                    a.getAttribute('href');

                try {
                    stored = localStorage.getItem(key);
                    if (stored === '1') cb.checked = true;
                } catch (e) {}

                cb.addEventListener('change', function () {
                    try {
                        if (cb.checked) {
                            localStorage.setItem(key, '1');
                        } else {
                            localStorage.removeItem(key);
                        }
                    } catch (e) {}
                });
            });
        });
    }


function styleRareEncounterRows() {
    var isLightTheme =
        document.documentElement.classList.contains('eqoa-theme-light') ||
        document.body.classList.contains('eqoa-theme-light');

    var headerBg = isLightTheme ? '#3a2418' : '#3a3a3a';
    var headerColor = isLightTheme ? '#ffffff' : '#ffd45a';
    var headerBorder = isLightTheme ? '#5a3a25' : '#1f1f1f';

    var rowBg1 = isLightTheme ? '#ffffff' : '#0d0c0b';
    var rowBg2 = isLightTheme ? '#f7f5f1' : '#242428';
    var rowColor = isLightTheme ? '#1f140c' : '#ffd45a';
    var rowBorder = isLightTheme ? '#e6ded4' : '#252525';

    document.querySelectorAll('.raid-encounter-table table').forEach(function (table) {
        table.style.width = '100%';
        table.style.tableLayout = 'fixed';
        table.style.borderCollapse = 'collapse';

        table.querySelectorAll('th').forEach(function (th) {
            th.style.background = headerBg;
            th.style.color = headerColor;
            th.style.border = '1px solid ' + headerBorder;
            th.style.padding = '8px';
            th.style.textAlign = 'center';
            th.style.fontWeight = 'bold';
        });

        table.querySelectorAll('td').forEach(function (td) {
            td.style.verticalAlign = 'top';
            td.style.padding = '0';
            td.style.border = '1px solid ' + rowBorder;
            td.style.background = rowBg1;
        });
    });

    document.querySelectorAll('.raid-encounter-table .raid-checkboxify').forEach(function (container) {
        var rowIndex = 0;

        container.querySelectorAll('br').forEach(function (br) {
            br.style.display = 'none';
        });

        container.querySelectorAll('b').forEach(function (zone) {
            zone.style.display = 'block';
            zone.style.background = headerBg;
            zone.style.color = headerColor;
            zone.style.borderTop = '1px solid ' + headerBorder;
            zone.style.borderBottom = '1px solid ' + headerBorder;
            zone.style.padding = '7px 8px';
            zone.style.margin = '0';
            zone.style.textAlign = 'center';
            zone.style.fontSize = '15px';
            zone.style.lineHeight = '1.15';
            zone.style.fontWeight = 'bold';
        });

        container.querySelectorAll('label').forEach(function (label) {
            rowIndex += 1;

            label.style.display = 'block';
            label.style.background = rowIndex % 2 === 0 ? rowBg2 : rowBg1;
            label.style.color = rowColor;
            label.style.borderBottom = '1px solid ' + rowBorder;
            label.style.padding = '6px 8px';
            label.style.margin = '0';
            label.style.lineHeight = '1.25';
            label.style.fontSize = '13px';
            label.style.cursor = 'pointer';

            label.querySelectorAll('a').forEach(function (link) {
                link.style.color = rowColor;
                link.style.fontWeight = 'bold';
                link.style.textDecoration = 'none';
            });
        });
    });
}


    function initItemFilters() {
        var table = document.getElementById('items-found-here-table');
        var box;
        var filters;
        var clearButton;

        if (!table) return;
        if (document.getElementById('items-filter-box')) return;

        box = document.createElement('div');
        box.id = 'items-filter-box';
        box.className = 'items-filter-box';

        box.innerHTML =
            '<b>Item Filters</b><br>' +
            'Item: <input type="text" id="filter-item"> ' +
            'Dropped By: <input type="text" id="filter-mob"> ' +
            'Class: <input type="text" id="filter-class"> ' +
            'Lvl >= <input type="number" id="filter-lvl-min"> ' +
            'DMG >= <input type="number" id="filter-dmg-min"> ' +
            'AC >= <input type="number" id="filter-ac-min"> ' +
            'HP >= <input type="number" id="filter-hp-min"> ' +
            'POW >= <input type="number" id="filter-pow-min"> ' +
            '<button id="clear-item-filters" type="button">Clear</button>';

        table.parentNode.insertBefore(box, table);

        filters = {
            item: document.getElementById('filter-item'),
            mob: document.getElementById('filter-mob'),
            className: document.getElementById('filter-class'),
            lvlMin: document.getElementById('filter-lvl-min'),
            dmgMin: document.getElementById('filter-dmg-min'),
            acMin: document.getElementById('filter-ac-min'),
            hpMin: document.getElementById('filter-hp-min'),
            powMin: document.getElementById('filter-pow-min')
        };

        clearButton = document.getElementById('clear-item-filters');

        function getCellText(row, index) {
            return (row.cells[index] ? row.cells[index].textContent : '').toLowerCase();
        }

        function getCellNumber(row, index) {
            var text = row.cells[index] ? row.cells[index].textContent.trim() : '';
            var num = parseFloat(text.replace(/[^\d.-]/g, ''));
            return isNaN(num) ? 0 : num;
        }

        function getInputNumber(input) {
            var num;

            if (!input || input.value === '') return null;

            num = parseFloat(input.value);
            return isNaN(num) ? null : num;
        }

        function filterTable() {
            var itemText = filters.item.value.toLowerCase();
            var mobText = filters.mob.value.toLowerCase();
            var classText = filters.className.value.toLowerCase();

            var lvlMin = getInputNumber(filters.lvlMin);
            var dmgMin = getInputNumber(filters.dmgMin);
            var acMin = getInputNumber(filters.acMin);
            var hpMin = getInputNumber(filters.hpMin);
            var powMin = getInputNumber(filters.powMin);

            var i;
            var row;
            var show;

            for (i = 1; i < table.rows.length; i++) {
                row = table.rows[i];
                show = true;

                if (itemText && getCellText(row, 0).indexOf(itemText) === -1) show = false;
                if (mobText && getCellText(row, 1).indexOf(mobText) === -1) show = false;
                if (classText && getCellText(row, 2).indexOf(classText) === -1) show = false;

                if (lvlMin !== null && getCellNumber(row, 3) < lvlMin) show = false;
                if (dmgMin !== null && getCellNumber(row, 4) < dmgMin) show = false;
                if (acMin !== null && getCellNumber(row, 5) < acMin) show = false;
                if (hpMin !== null && getCellNumber(row, 13) < hpMin) show = false;
                if (powMin !== null && getCellNumber(row, 15) < powMin) show = false;

                row.style.display = show ? '' : 'none';
            }
        }

        Object.keys(filters).forEach(function (key) {
            filters[key].addEventListener('input', filterTable);
        });

        clearButton.addEventListener('click', function () {
            Object.keys(filters).forEach(function (key) {
                filters[key].value = '';
            });

            filterTable();
        });
    }

    function initRelatedItemTooltips($content) {
        var root = $content && $content[0] ? $content[0] : document;
        var relatedLinks = root.querySelectorAll('.eqoa-related-items a');
        var tooltip = document.getElementById('eqoa-related-item-tooltip');
        var cache = {};

        if (!relatedLinks.length) return;

        if (!tooltip) {
            tooltip = document.createElement('div');
            tooltip.id = 'eqoa-related-item-tooltip';
            tooltip.className = 'eqoa-related-item-tooltip';
            tooltip.style.display = 'none';
            document.body.appendChild(tooltip);
        }

        function normalizeRarity(rarity) {
            return String(rarity || 'common')
                .toLowerCase()
                .replace(/\s+/g, '-');
        }

        function getPageTitleFromLink(link) {
            var href = link.getAttribute('href') || '';
            var articlePath = mw.config.get('wgArticlePath') || '';
            var title;

            if (href.indexOf('/index.php/') !== -1) {
                title = href.split('/index.php/')[1];
                return decodeURIComponent(title).replace(/_/g, ' ');
            }

            if (articlePath && articlePath.indexOf('$1') !== -1) {
                title = href.substring(href.lastIndexOf('/') + 1);
                return decodeURIComponent(title).replace(/_/g, ' ');
            }

            return link.textContent.trim();
        }

        function parseItemTemplate(wikitext, fallbackName) {
            var match = wikitext.match(/\{\{(?:ItemBox|ItemPage)([\s\S]*?)\}\}/i);
            var item;
            var lines;

            if (!match) return null;

            item = {
                name: fallbackName
            };

            lines = match[1].split('\n');

            lines.forEach(function (line) {
                var eq;
                var key;
                var value;

                line = line.trim();
                if (line.indexOf('|') !== 0) return;

                eq = line.indexOf('=');
                if (eq === -1) return;

                key = line.substring(1, eq).trim().toLowerCase();
                value = line.substring(eq + 1).trim();

                if (key) {
                    item[key] = value;
                }
            });

            if (!item.name) item.name = fallbackName;

            return item;
        }

        function statLine(item, label, key) {
            if (!item[key] || item[key] === '0') return '';
            return '<div><span>' + label + '</span> ' + item[key] + '</div>';
        }

        function buildTooltip(item) {
            var rarity = normalizeRarity(item.rarity);
            var rarityClass = 'cb-rarity-' + rarity;
            var html = '';

            html += '<div class="eqoa-related-tooltip-title ' + rarityClass + '">' +
                item.name +
                '</div>';

            if (item.slot) html += '<div><b>Equip:</b> ' + item.slot + '</div>';
            if (item.class) html += '<div><b>Class:</b> ' + item.class + '</div>';
            if (item.lvl) html += '<div><b>Level:</b> ' + item.lvl + '</div>';
            if (item.dur) html += '<div><b>DUR:</b> ' + item.dur + '</div>';

            html += '<div class="eqoa-related-tooltip-stats">';
            html += statLine(item, 'DMG:', 'dmg');
            html += statLine(item, 'AC:', 'ac');
            html += statLine(item, 'STR:', 'str');
            html += statLine(item, 'STA:', 'sta');
            html += statLine(item, 'AGI:', 'agi');
            html += statLine(item, 'DEX:', 'dex');
            html += statLine(item, 'WIS:', 'wis');
            html += statLine(item, 'INT:', 'int');
            html += statLine(item, 'CHA:', 'cha');
            html += statLine(item, 'HP:', 'hp');
            html += statLine(item, 'POW:', 'pow');
            html += statLine(item, 'PR:', 'pr');
            html += statLine(item, 'DR:', 'dr');
            html += statLine(item, 'FR:', 'fr');
            html += statLine(item, 'CR:', 'cr');
            html += statLine(item, 'LR:', 'lr');
            html += statLine(item, 'AR:', 'ar');
            html += '</div>';

            if (item.effect) html += '<div><b>Effect:</b> ' + item.effect + '</div>';
            if (item.proc) html += '<div><b>Proc:</b> ' + item.proc + '</div>';

            return html;
        }

        function moveTooltip(event) {
            var left = event.pageX + 16;
            var top = event.pageY + 16;

            if (left + 360 > window.scrollX + window.innerWidth) {
                left = event.pageX - 360;
            }

            tooltip.style.left = left + 'px';
            tooltip.style.top = top + 'px';
        }

        function showTooltip(link, event) {
            var title = getPageTitleFromLink(link);

            tooltip.className = 'eqoa-related-item-tooltip';
            tooltip.innerHTML = 'Loading...';
            tooltip.style.display = 'block';
            moveTooltip(event);

            if (cache[title]) {
                tooltip.className =
                    'eqoa-related-item-tooltip tooltip-rarity-' +
                    normalizeRarity(cache[title].rarity);

                tooltip.innerHTML = buildTooltip(cache[title]);
                return;
            }

            new mw.Api().get({
                action: 'query',
                prop: 'revisions',
                titles: title,
                rvprop: 'content',
                rvslots: 'main',
                formatversion: '2',
                format: 'json'
            }).then(function (data) {
                var page = data.query.pages[0];
                var text = '';
                var item;

                if (page && page.revisions && page.revisions[0]) {
                    if (page.revisions[0].slots && page.revisions[0].slots.main) {
                        text = page.revisions[0].slots.main.content || '';
                    } else {
                        text = page.revisions[0].content || '';
                    }
                }

                item = parseItemTemplate(text, title);

                if (!item) {
                    tooltip.innerHTML = 'No item data found.';
                    return;
                }

                cache[title] = item;

                tooltip.className =
                    'eqoa-related-item-tooltip tooltip-rarity-' +
                    normalizeRarity(item.rarity);

                tooltip.innerHTML = buildTooltip(item);
            });
        }

        Array.prototype.forEach.call(relatedLinks, function (link) {
            if (link.classList.contains('eqoa-related-tooltip-ready')) return;

            link.classList.add('eqoa-related-tooltip-ready');

            link.addEventListener('mouseenter', function (event) {
                showTooltip(link, event);
            });

            link.addEventListener('mousemove', moveTooltip);

            link.addEventListener('mouseleave', function () {
                tooltip.style.display = 'none';
            });
        });
    }

function ensureMobileSidebarButton() {
    if (document.getElementById('eqoa-mobile-sidebar-toggle')) return;

    var button = document.createElement('button');
    button.id = 'eqoa-mobile-sidebar-toggle';
    button.type = 'button';
    button.textContent = '☰';
    button.setAttribute('aria-label', 'Open sidebar');

    button.addEventListener('click', function () {
    document.documentElement.classList.toggle('eqoa-mobile-sidebar-open');

    setTimeout(removeMobilePageActions, 50);
    setTimeout(removeMobilePageActions, 250);
});

    document.body.appendChild(button);
}

function stackZoneColumnsOnMobile() {
    if (!window.matchMedia || !window.matchMedia('(max-width: 700px)').matches) return;
    if (document.querySelector('.eqoa-mobile-zone-stack')) return;

    var left = document.querySelector('.zone-left');
    var right = document.querySelector('.zone-right');

    if (!left || !right) return;

    var leftTable = left.closest('table');
    var rightTable = right.closest('table');
    var layoutTable = leftTable && leftTable === rightTable ? leftTable : null;

    var stack = document.createElement('div');
    stack.className = 'eqoa-mobile-zone-stack';

    if (layoutTable) {
        layoutTable.parentNode.insertBefore(stack, layoutTable);

        if (left.tagName.toLowerCase() === 'td') {
            var leftBox = document.createElement('div');
            leftBox.className = left.className;
            while (left.firstChild) leftBox.appendChild(left.firstChild);
            stack.appendChild(leftBox);
        } else {
            stack.appendChild(left);
        }

        if (right.tagName.toLowerCase() === 'td') {
            var rightBox = document.createElement('div');
            rightBox.className = right.className;
            while (right.firstChild) rightBox.appendChild(right.firstChild);
            stack.appendChild(rightBox);
        } else {
            stack.appendChild(right);
        }

        layoutTable.style.setProperty('display', 'none', 'important');
    } else {
        left.parentNode.insertBefore(stack, left);
        stack.appendChild(left);
        stack.appendChild(right);
    }

    stack.querySelectorAll('.zone-left, .zone-right').forEach(function (el) {
        el.style.setProperty('display', 'block', 'important');
        el.style.setProperty('float', 'none', 'important');
        el.style.setProperty('clear', 'both', 'important');
        el.style.setProperty('width', '100%', 'important');
        el.style.setProperty('max-width', '100%', 'important');
        el.style.setProperty('margin', '0 0 12px 0', 'important');
    });
}


function removeMobilePageActions() {
return;

    if (!window.matchMedia || !window.matchMedia('(max-width: 700px)').matches) return;

var selectors = [
    '#p-cactions',
    '#p-views',
    '#p-namespaces',
    '#left-navigation',
    '#right-navigation',
    '#head',
    '#mw-head',
    'li[id^="ca-"]',
    'a[id^="ca-"]',
    '[id^="ca-"]'
];

    selectors.forEach(function (selector) {
        document.querySelectorAll(selector).forEach(function (el) {
            el.remove();
        });
    });

    document.querySelectorAll('#column-one .portlet, #column-one .generated-sidebar, #column-one div').forEach(function (el) {
        var label = '';
        var heading = el.querySelector('h3, h4, h5');

        if (heading) {
            label = heading.textContent.trim().toUpperCase();
        } else {
            label = el.textContent.trim().toUpperCase();
        }

        if (label.indexOf('PAGE ACTIONS') !== -1) {
            el.remove();
        }
    });

    document.querySelectorAll('#column-one a, #column-one button, #column-one input, #column-one img').forEach(function (el) {
        if (el.closest('#p-search')) return;
        if (el.id === 'eqoa-mobile-sidebar-toggle' || el.id === 'eqoa-theme-toggle') return;

        var visibleText = (el.textContent || el.value || '').trim();
        var rect = el.getBoundingClientRect();

        if (
            !visibleText &&
            rect.width > 0 &&
            rect.height > 0 &&
            rect.width <= 70 &&
            rect.height <= 70
        ) {
            var removeTarget = el.closest('li, a, button, input, div') || el;
            removeTarget.remove();
        }
    });

    document.querySelectorAll('a, button, input, li, div, img').forEach(function (el) {
        if (el.id === 'eqoa-mobile-sidebar-toggle' || el.id === 'eqoa-theme-toggle') return;
        if (el.closest('#p-search')) return;

        var rect = el.getBoundingClientRect();
        var visibleText = (el.textContent || el.value || '').trim();

        if (
            !visibleText &&
            rect.width > 0 &&
            rect.height > 0 &&
            rect.width <= 70 &&
            rect.height <= 70 &&
            rect.top >= 0 &&
            rect.top <= 140
        ) {
            var removeTarget = el.closest('li, a, button, input, div') || el;
            removeTarget.remove();
        }
    });
}


  function loadBuilderScripts() {
    loadWikiScript('MediaWiki:EQOAConfig.js')
        .always(function () {
            loadWikiScript('MediaWiki:CharacterBuilderData.js')
                .done(function () {
                    $.when(
                        loadScriptUrl('https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js'),
                        loadScriptUrl('https://cdnjs.cloudflare.com/ajax/libs/lz-string/1.5.0/lz-string.min.js')
                    ).always(function () {
                        loadWikiScript('MediaWiki:CharacterBuilder.js')
                            .fail(function (error) {
                                console.error('Character Builder failed to load:', error);
                            });
                    });
                })
                .fail(function (error) {
                    console.error('Character Builder data failed to load:', error);
                });
        });
}





function loadClassMasteryScripts() {
    loadWikiScript('MediaWiki:ClassMasteryData.js')
        .done(function () {
            loadWikiScript('MediaWiki:ClassMasteryRawData.js')
                .always(function () {
                    loadWikiScript('MediaWiki:ClassMasteryIds.js')
                        .always(function () {
                            loadWikiScript('MediaWiki:ClassMasteryCalculator.js')
                                .fail(function (error) {
                                    console.error('Class Mastery calculator failed to load:', error);
                                });
                        });
                });
        })
        .fail(function (error) {
            console.error('Class Mastery data failed to load:', error);
        });
}
                       

    window.EQOA_APPLY_WIKI_THEME = applyWikiTheme;
    window.EQOA_GET_WIKI_THEME = getSavedWikiTheme;

        if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', ensureThemeButton);
    } else {
        ensureThemeButton();
    }

    mw.hook('wikipage.content').add(function ($content) {
        ensureThemeButton();
        ensureMobileSidebarButton();
        stackZoneColumnsOnMobile();
        removeMobilePageActions();
        ensureMobileSidebarButton();

        setTimeout(stackZoneColumnsOnMobile, 250);
        setTimeout(stackZoneColumnsOnMobile, 1000);

        setTimeout(function () {
            removeMobilePageActions();
            ensureMobileSidebarButton();
        }, 250);

        setTimeout(function () {
            removeMobilePageActions();
            ensureMobileSidebarButton();
        }, 1000);

        initRaidChecks();
styleRareEncounterRows();
setTimeout(styleRareEncounterRows, 100);
setTimeout(styleRareEncounterRows, 500);
setTimeout(buildRareEncounterMobileLayout, 100);
setTimeout(buildRareEncounterMobileLayout, 500);
        initItemFilters();

        mw.loader.using('mediawiki.api').then(function () {
            initRelatedItemTooltips($content);
        });
    });

    mw.loader.using('mediawiki.util').then(function () {
        loadBuilderScripts();
        loadClassMasteryScripts();
    });
})();





function buildRareEncounterMobileLayout() {
    var isMobile = window.matchMedia('(max-width: 700px)').matches;
    var tables = document.querySelectorAll('.raid-encounter-table table');

    function styleRareMobileHeadings(root) {
        var isLightTheme =
            document.documentElement.classList.contains('eqoa-theme-light') ||
            document.body.classList.contains('eqoa-theme-light');

        Array.prototype.forEach.call(root.querySelectorAll('.rare-mobile-heading'), function (heading) {
            heading.style.padding = '9px 10px';
            heading.style.textAlign = 'center';
            heading.style.fontSize = '18px';
            heading.style.fontWeight = 'bold';
            heading.style.borderBottom = isLightTheme ? '1px solid #5a3a25' : '1px solid #1f1f1f';
            heading.style.background = isLightTheme ? '#3a2418' : '#3a3a3a';
            heading.style.color = isLightTheme ? '#ffffff' : '#ffd45a';
        });
    }

    Array.prototype.forEach.call(tables, function (table) {
        var wrapper = table.closest('.raid-encounter-table');
        if (!wrapper) return;

        var existingMobile = wrapper.querySelector('.rare-encounter-mobile-layout');

        if (!isMobile) {
            table.style.display = '';
            if (existingMobile) existingMobile.style.display = 'none';
            return;
        }

        table.style.display = 'none';

        if (existingMobile) {
            existingMobile.style.display = 'block';
            styleRareMobileHeadings(existingMobile);
            return;
        }

        var mobile = document.createElement('div');
        mobile.className = 'rare-encounter-mobile-layout';

        var rows = table.querySelectorAll('tr');

        function addMobileBox(title, cell) {
            if (!cell || !cell.textContent.trim()) return;

            var box = document.createElement('div');
            box.className = 'rare-mobile-box';

            var heading = document.createElement('div');
            heading.className = 'rare-mobile-heading';
            heading.textContent = title;

            var body = document.createElement('div');
            body.className = 'rare-mobile-body';
            body.innerHTML = cell.innerHTML;

            box.appendChild(heading);
            box.appendChild(body);
            mobile.appendChild(box);
        }

        if (rows[1]) {
            var baseCells = rows[1].querySelectorAll('td');

            addMobileBox('Base World 1', baseCells[0]);
            addMobileBox('Base World 2', baseCells[1]);
            addMobileBox('Base World 3', baseCells[2]);
            addMobileBox('Base World 4', baseCells[3]);
        }

        if (rows[3]) {
            var frontiersCells = rows[3].querySelectorAll('td');

            addMobileBox('Frontiers 1', frontiersCells[0]);
            addMobileBox('Frontiers 2', frontiersCells[1]);
            addMobileBox('Frontiers 3', frontiersCells[2]);
        }

        wrapper.appendChild(mobile);
        styleRareMobileHeadings(mobile);
    });
}




window.addEventListener('resize', buildRareEncounterMobileLayout);
window.addEventListener('orientationchange', function () {
    setTimeout(buildRareEncounterMobileLayout, 150);
});

function limitDevRenodorSidebarSections() {
    var username = mw.config.get('wgUserName');
    var isDevRenodor = username === 'DevRenodor';

    ['p-navigation', 'p-tb'].forEach(function (id) {
        var section = document.getElementById(id);

        if (section) {
            section.style.display = isDevRenodor ? '' : 'none';
        }
    });
}

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', limitDevRenodorSidebarSections);
} else {
    limitDevRenodorSidebarSections();
}

$(function () {
    if (window.EQOA_APPLY_WIKI_THEME && window.EQOA_GET_WIKI_THEME) {
        window.EQOA_APPLY_WIKI_THEME(window.EQOA_GET_WIKI_THEME());
    }
});

function limitFlaggedRevsBadgeToDevRenodor() {
    var username = mw.config.get('wgUserName');
    var isDevRenodor = username === 'DevRenodor';

    [
        '#mw-fr-revisiontag',
        '.flaggedrevs_short',
        '.flaggedrevs_basic',
        '.flaggedrevs_preview',
        '.flaggedrevs_notice',
        '.flaggedrevs_reviewform',
        '.mw-fr-reviewlink'
    ].forEach(function (selector) {
        document.querySelectorAll(selector).forEach(function (el) {
            el.style.setProperty('display', isDevRenodor ? 'block' : 'none', 'important');
        });
    });
}

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', limitFlaggedRevsBadgeToDevRenodor);
} else {
    limitFlaggedRevsBadgeToDevRenodor();
}

function fixPersonalNotificationButtons() {
    function fixButton(id, labelText) {
        var item = document.getElementById(id);
        if (!item) return;

        var link = item.querySelector('a');
        if (!link) return;

        var rawText = link.textContent || '';
        var countMatch = rawText.match(/\d+/);
        var count = countMatch ? countMatch[0] : '';

        link.textContent = count ? labelText + ' (' + count + ')' : labelText;
        link.setAttribute('title', labelText);
        link.setAttribute('aria-label', labelText);
    }

    fixButton('pt-notifications-alert', 'alerts');
    fixButton('pt-notifications-notice', 'notices');
}

if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', fixPersonalNotificationButtons);
} else {
    fixPersonalNotificationButtons();
}






mw.hook('wikipage.content').add(fixPersonalNotificationButtons);
setTimeout(fixPersonalNotificationButtons, 500);
setTimeout(fixPersonalNotificationButtons, 1500);


mw.hook('wikipage.content').add(limitFlaggedRevsBadgeToDevRenodor);


mw.hook('wikipage.content').add(limitDevRenodorSidebarSections);

mw.loader.load(mw.config.get('wgScript') + '?title=MediaWiki:CharacterBuilderGeneratedItems.js&action=raw&ctype=text/javascript');




mw.loader.using( 'mediawiki.util', function () {
    var topbar = document.createElement( 'div' );
    topbar.id = 'eqoa-topbar';

    topbar.innerHTML =
        '<a class="eqoa-top-title" href="' + mw.util.getUrl( 'Main Page' ) + '">EQOA: Sandstorm</a>' +
        '<form class="eqoa-top-search" action="' + mw.util.getUrl( 'Special:Search' ) + '" method="get">' +
            '<input type="search" name="search" placeholder="Search EQOA: Sandstorm" />' +
            '<button type="submit">Search</button>' +
        '</form>' +
        '<a class="eqoa-discord-button" href="https://discord.gg/QK6r8Qd8Vu" target="_blank" rel="noopener">Join the Community</a>';

    document.body.insertBefore( topbar, document.body.firstChild );
} );

mw.loader.using( 'mediawiki.util', function () {
    if ( document.getElementById( 'eqoa-right-rail' ) ) {
        return;
    }

    var rail = document.createElement( 'aside' );
    rail.id = 'eqoa-right-rail';

    rail.innerHTML =
        '<iframe ' +
            'src="https://discord.com/widget?id=1251536624467050628&theme=dark" ' +
            'width="300" ' +
            'height="500" ' +
            'allowtransparency="true" ' +
            'frameborder="0" ' +
            'sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts">' +
        '</iframe>';

    document.body.appendChild( rail );
} );

function eqoaKeepPersonalToolsVisible() {
    var personal = document.getElementById( 'p-personal' );

    if ( !personal ) {
        return;
    }

    personal.style.display = 'block';
    personal.style.visibility = 'visible';
    personal.style.opacity = '1';
    personal.style.position = 'fixed';
    personal.style.top = '72px';
    personal.style.right = '18px';
    personal.style.left = 'auto';
    personal.style.zIndex = '2147483647';
    personal.style.background = 'transparent';
    personal.style.overflow = 'visible';

    var body = personal.querySelector( '.pBody' );
    var list = personal.querySelector( 'ul' );

    [ body, list ].forEach( function ( el ) {
        if ( !el ) {
            return;
        }

        el.style.display = 'flex';
        el.style.visibility = 'visible';
        el.style.opacity = '1';
        el.style.gap = '4px';
        el.style.alignItems = 'center';
        el.style.justifyContent = 'flex-end';
        el.style.background = 'transparent';
        el.style.border = '0';
        el.style.margin = '0';
        el.style.padding = '0';
        el.style.listStyle = 'none';
    } );

    personal.querySelectorAll( 'li, a' ).forEach( function ( el ) {
        el.style.display = 'inline-flex';
        el.style.visibility = 'visible';
        el.style.opacity = '1';
    } );
}

document.addEventListener( 'DOMContentLoaded', eqoaKeepPersonalToolsVisible );
mw.hook( 'wikipage.content' ).add( eqoaKeepPersonalToolsVisible );
setTimeout( eqoaKeepPersonalToolsVisible, 250 );
setTimeout( eqoaKeepPersonalToolsVisible, 1000 );
setTimeout( eqoaKeepPersonalToolsVisible, 2000 );




function eqoaBuildSidebarAccountButtons() {
    var sidebar = document.getElementById('column-one');
    var personal = document.getElementById('p-personal');

    if (!sidebar || !personal) return;

    var oldBox = document.getElementById('eqoa-sidebar-account');
    if (oldBox) oldBox.remove();

    var links = personal.querySelectorAll('a');
    if (!links.length) return;

    var box = document.createElement('div');
    box.id = 'eqoa-sidebar-account';

    var title = document.createElement('div');
    title.className = 'eqoa-sidebar-account-title';
    title.textContent = 'Account';
box.appendChild(title);

var grid = document.createElement('div');
grid.className = 'eqoa-sidebar-account-grid';
box.appendChild(grid);

links.forEach(function (link) {
        var text = (link.textContent || '').trim();

        if (!text) return;

        var clone = document.createElement('a');
        clone.href = link.href;
        clone.textContent = text;
        clone.title = link.title || text;
        clone.className = 'eqoa-sidebar-account-button';

        grid.appendChild(clone);
    });

    var logo = document.getElementById('p-logo');

    if (logo && logo.parentNode === sidebar) {
        logo.insertAdjacentElement('afterend', box);
    } else {
        sidebar.insertBefore(box, sidebar.firstChild);
    }
}


/* =========================================================
   EQOA Interactive Maps - Load Leaflet
   ========================================================= */

mw.loader.load('https://unpkg.com/leaflet@1.9.4/dist/leaflet.js');
mw.loader.load('https://unpkg.com/leaflet@1.9.4/dist/leaflet.css', 'text/css');

/* =========================================================
   EQOA Interactive Maps
   ========================================================= */

function initEqoaInteractiveMaps() {
    if (typeof L === 'undefined') {
        setTimeout(initEqoaInteractiveMaps, 100);
        return;
    }

    document.querySelectorAll('.eqoa-interactive-map').forEach(function (mapBox) {
        if (mapBox.classList.contains('eqoa-map-ready')) return;

        var imageUrl = mapBox.getAttribute('data-image');
        var width = parseInt(mapBox.getAttribute('data-width'), 10) || 2048;
        var height = parseInt(mapBox.getAttribute('data-height'), 10) || 2048;
        var points = mapBox.querySelectorAll('.eqoa-map-point');
        var bounds = [[0, 0], [height, width]];
        var layersByType = {};
        var map;

        if (!imageUrl) return;

        mapBox.classList.add('eqoa-map-ready');

        map = L.map(mapBox, {
            crs: L.CRS.Simple,
            minZoom: -2,
            maxZoom: 3,
            zoomSnap: 0.25,
            attributionControl: false
        });

mapBox._leaflet_map_instance = map;

map.on('click', function (event) {
    console.log(
        'EQOA map coordinate:',
        'data-x="' + Math.round(event.latlng.lng) + '"',
        'data-y="' + Math.round(event.latlng.lat) + '"'
    );
});

L.imageOverlay(imageUrl, bounds).addTo(map);
        map.fitBounds(bounds);

        points.forEach(function (point) {
            var type = point.getAttribute('data-type') || 'Marker';
            var name = point.getAttribute('data-name') || 'Unknown';
            var level = point.getAttribute('data-level') || '';
            var x = parseFloat(point.getAttribute('data-x'));
            var y = parseFloat(point.getAttribute('data-y'));
            var marker;
            var label;

            if (isNaN(x) || isNaN(y)) return;

            if (!layersByType[type]) {
                layersByType[type] = L.layerGroup().addTo(map);
            }

            label = '<b>' + name + '</b>';
            if (level) label += '<br>Level: ' + level;
            label += '<br>Type: ' + type;

marker = L.circleMarker([y, x], {
    radius: 4,
    color: getEqoaMapMarkerOutlineColor(type),
    fillColor: getEqoaMapMarkerColor(type),
    fillOpacity: 0.95,
    weight: 2,
    className: 'eqoa-map-dot'
});

            marker.bindTooltip(label, {
                direction: 'top',
                sticky: true
            });

            marker.addTo(layersByType[type]);
        });

        L.control.layers(null, layersByType, {
            collapsed: false
        }).addTo(map);
    });
}

function getEqoaMapMarkerColor(type) {
    type = String(type || '').toLowerCase();

    if (type === 'quest') return '#d86f78';
    if (type === 'npc') return '#ff3b3b';
    if (type === 'named') return '#7f1010';
    if (type === 'enemy') return '#ff3b3b';

    return '#f2ab31';
}

function getEqoaMapMarkerOutlineColor(type) {
    type = String(type || '').toLowerCase();

    if (type === 'quest') return '#5c2025';
    if (type === 'npc') return '#5c0909';
    if (type === 'named') return '#260000';
    if (type === 'enemy') return '#5c0909';

    return '#3a2508';
}

mw.hook('wikipage.content').add(initEqoaInteractiveMaps);
document.addEventListener('DOMContentLoaded', initEqoaInteractiveMaps);
setTimeout(initEqoaInteractiveMaps, 500);
setTimeout(initEqoaInteractiveMaps, 1500);










document.addEventListener('DOMContentLoaded', eqoaBuildSidebarAccountButtons);
mw.hook('wikipage.content').add(eqoaBuildSidebarAccountButtons);
setTimeout(eqoaBuildSidebarAccountButtons, 250);
setTimeout(eqoaBuildSidebarAccountButtons, 1000);
setTimeout(eqoaBuildSidebarAccountButtons, 2000);