MediaWiki:Common.js: Difference between revisions

From Winds Of Valen Wiki
Jump to navigation Jump to search
Pin sidebar, inject logo and Discord button
Switched to use CSS class for passing arguments
 
(9 intermediate revisions by the same user not shown)
Line 1: Line 1:
/**
$(document).ready(function() {
* Winds of Valen Wiki — Common JS
    $('#ca-nstab-main a').text('Article');
*/
    $('#ca-history a').text('History');
    $('#ca-view a').text('Read');
});
// Skill Calculator logic to be embedded in MediaWiki:Common.js
mw.hook('wikipage.content').add(function($content) {
    // Only run if the page contains a calculator container
    const calcContainer = $content.find('#wiki-skill-calc')[0];
    if (!calcContainer) return;
    if (calcContainer.dataset.initialized) return;
    calcContainer.dataset.initialized = 'true';


// 1. Force light color scheme immediately
    const skillClass = Array.from(calcContainer.classList).find(c => c.startsWith('skill-'));
(function () {
     if (!skillClass) {
    document.documentElement.style.colorScheme = 'light';
        calcContainer.innerHTML = "<p style='color:red;'>Error: No skill class found on container.</p>";
     var m = document.querySelector('meta[name="color-scheme"]') || document.createElement('meta');
        return;
     m.name = 'color-scheme'; m.content = 'light only';
     }
    document.head.insertBefore(m, document.head.firstChild);
    const currentSkill = skillClass.replace('skill-', ''); // e.g., "Mining"
}());


// 2. Fix html class: switch pinned-disabled → pinned-enabled
    // Raw XP Table (Level 1 to 99)
(function () {
     const xpTable = [
     var h = document.documentElement;
         0, 0, 74, 160, 258, 371, 500, 649, 820, 1016, 1241, 1500, 1797, 2139, 2531, 2982, 3500, 4095, 4778, 5563, 6464, 7500, 8690, 10056, 11626, 13429, 15500, 17879, 20612, 23751, 27358, 31500, 36258, 41724, 48003, 55215, 63500, 73017, 83949, 96506, 110930, 127500, 146533, 168397, 193512, 222361, 255500, 293567, 337294, 387523, 445222, 511500, 587634, 675088, 775547, 890944, 1023500, 1175767, 1350676, 1551594, 1782388, 2047500, 2352034, 2701852, 3103688, 3565275, 4095500, 4704568, 5404204, 6207875, 7131050, 8191500, 9409637, 10808909, 12416250, 14262600, 16383500, 18819774, 21618318, 24833000, 28525701, 32767500, 37640048, 43237135, 49666500, 57051902, 65535500, 75280595, 86474770, 99333501, 114104303, 131071500, 150561691, 172950041, 198667502, 228209107, 262143500, 301123982, 345900582, 397335504
    h.className = h.className
    ];
         .replace('vector-feature-main-menu-pinned-disabled', 'vector-feature-main-menu-pinned-enabled');
}());


// 3. Load Cinzel font for the logo banner
    // Data compiled from the scraped files
(function () {
    const skillData = {
    var l = document.createElement('link');
        "Mining": [
    l.rel = 'stylesheet';
            { "name": "Copper Ore", "reqLevel": 1, "xp": 15 },
    l.href = 'https://fonts.googleapis.com/css2?family=Cinzel:wght@700&family=PT+Serif:ital,wght@0,400;0,700;1,400&display=swap';
            { "name": "Tin Ore", "reqLevel": 1, "xp": 15 },
     document.head.appendChild(l);
            { "name": "Iron Ore", "reqLevel": 10, "xp": 30 },
}());
            { "name": "Coal Ore", "reqLevel": 20, "xp": 80 },
            { "name": "Mithril Ore", "reqLevel": 30, "xp": 150 },
            { "name": "Gold Ore", "reqLevel": 40, "xp": 350 },
            { "name": "Essence Geode", "reqLevel": 50, "xp": 550 }
        ],
        "Fishing": [
            { "name": "Minnow", "reqLevel": 1, "xp": 8 },
            { "name": "Common Trout", "reqLevel": 5, "xp": 75 },
            { "name": "Perch", "reqLevel": 10, "xp": 225 },
            { "name": "Bass", "reqLevel": 20, "xp": 200 },
            { "name": "Blue Gill", "reqLevel": 30, "xp": 75 },
            { "name": "Elder Trout", "reqLevel": 40, "xp": 500 },
            { "name": "Carp", "reqLevel": 50, "xp": 1250 }
        ],
        "PotionMaking": [
            { "name": "Weak Health Potion", "reqLevel": 1, "xp": 25 },
            { "name": "Health Potion", "reqLevel": 10, "xp": 100 },
            { "name": "Attack Potion", "reqLevel": 25, "xp": 1500 },
            { "name": "Fishing Potion", "reqLevel": 30, "xp": 2000 },
            { "name": "Mining Potion", "reqLevel": 30, "xp": 2000 },
            { "name": "Shield Potion", "reqLevel": 40, "xp": 3500 },
            { "name": "Strong Health Potion", "reqLevel": 50, "xp": 5000 },
            { "name": "Strong Shield Potion", "reqLevel": 60, "xp": 8000 }
        ]
     };


// 4. After DOM ready: move menu to pinned slot, inject logo + Discord
    const actions = skillData[currentSkill];
document.addEventListener('DOMContentLoaded', function () {
    if (!actions) {
        calcContainer.innerHTML = "<p style='color:red;'>Calculator data not found for " + currentSkill + ".</p>";
        return;
    }


     /* --- Move menu from dropdown to pinned container --- */
     // Convert level to XP
     var pinned  = document.getElementById('vector-main-menu-pinned-container');
     function levelToXP(level) {
    var unpinned = document.getElementById('vector-main-menu-unpinned-container');
        if (level < 1) return xpTable[1];
    if (pinned && unpinned && unpinned.children.length > 0 && pinned.children.length === 0) {
        if (level > 99) return xpTable[99];
         while (unpinned.firstChild) {
         return xpTable[level];
            pinned.appendChild(unpinned.firstChild);
        }
     }
     }


     /* --- Hide hamburger now that sidebar is pinned --- */
     function calculateActions(currentXP, targetXP) {
    var ham = document.getElementById('vector-main-menu-dropdown');
        const xpNeeded = targetXP - currentXP;
    if (ham) ham.style.display = 'none';
        if (xpNeeded <= 0) return [];
        return actions.map(action => {
            const amount = Math.ceil(xpNeeded / action.xp);
            return { ...action, amount, xpNeeded };
        }).sort((a, b) => a.reqLevel - b.reqLevel);
    }


     /* --- Inject Logo Banner at top of sidebar --- */
     function renderCalculator() {
    var sidebarEl = pinned || document.getElementById('vector-main-menu');
        const currentLvl = parseInt(document.getElementById('calc-current-lvl').value) || 1;
    if (sidebarEl) {
        const targetLvl = parseInt(document.getElementById('calc-target-lvl').value) || (currentLvl + 1);


         // Try to find the site logo src from #p-logo
         const currentXP = levelToXP(currentLvl);
         var logoImg = document.querySelector('#p-logo img, #p-logo .mw-logo-icon');
         const targetXP = levelToXP(targetLvl);
         var logoSrc = logoImg ? logoImg.src : '';
         const xpNeeded = targetXP - currentXP;


         var logoDiv = document.createElement('div');
         let html = `
        logoDiv.className = 'wov-sidebar-logo';
            <div class="calc-results-header">
        logoDiv.innerHTML =
                <h3>XP Required: <span>${xpNeeded.toLocaleString()}</span></h3>
            '<a href="/wiki/Main_Page">' +
                <p>From Level ${currentLvl} (${currentXP.toLocaleString()} XP) to Level ${targetLvl} (${targetXP.toLocaleString()} XP)</p>
            (logoSrc ? '<img src="' + logoSrc + '" alt="Winds of Valen Wiki" />' : '') +
             </div>
             '<div class="wov-sidebar-logo-title">Winds of Valen</div>' +
            <table class="wikitable calc-table">
            '<div class="wov-sidebar-logo-sub">Wiki</div>' +
                <tr><th>Action</th><th>Level Req.</th><th>XP/Action</th><th>Total Actions</th></tr>
            '</a>';
         `;
         sidebarEl.insertBefore(logoDiv, sidebarEl.firstChild);


         /* --- Inject Discord Button below logo --- */
         if (xpNeeded <= 0) {
         var discordDiv = document.createElement('div');
            html += `<tr><td colspan="4" style="text-align:center;">You have already reached the target level!</td></tr>`;
        discordDiv.className = 'wov-discord-btn';
         } else {
        discordDiv.innerHTML =
            const results = calculateActions(currentXP, targetXP);
            '<a href="https://discord.gg/xxzDqg9xWc" target="_blank" rel="noopener">' +
            results.forEach(res => {
            '<svg viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">' +
                const available = currentLvl >= res.reqLevel;
            '<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/>' +
                const rowClass = available ? '' : 'calc-unavailable';
            '</svg>' +
                const opacity = available ? '1' : '0.5';
             'Chat on Discord</a>';
                html += `
         sidebarEl.insertBefore(discordDiv, sidebarEl.children[1]);
                    <tr class="${rowClass}" style="opacity: ${opacity}">
                        <td><strong>${res.name}</strong></td>
                        <td>${res.reqLevel}</td>
                        <td>${res.xp}</td>
                        <td><strong>${res.amount.toLocaleString()}</strong></td>
                    </tr>
                `;
             });
        }
        html += `</table>`;
         document.getElementById('calc-results').innerHTML = html;
     }
     }
    // Inject CSS
    const style = document.createElement('style');
    style.innerHTML = `
        .calc-wrapper {
            background: #1a1b26;
            color: #a9b1d6;
            padding: 20px;
            border-radius: 8px;
            border: 1px solid #414868;
            max-width: 600px;
            margin: 20px 0;
            font-family: 'Inter', sans-serif;
            box-shadow: 0 10px 25px rgba(0,0,0,0.5);
        }
        .calc-wrapper h2 {
            margin-top: 0;
            color: #7aa2f7;
            border-bottom: 2px solid #24283b;
            padding-bottom: 10px;
        }
        .calc-inputs {
            display: flex;
            gap: 20px;
            margin-bottom: 20px;
        }
        .calc-input-group {
            display: flex;
            flex-direction: column;
            gap: 5px;
        }
        .calc-input-group label {
            font-size: 12px;
            text-transform: uppercase;
            font-weight: bold;
            color: #565f89;
        }
        .calc-input-group input {
            background: #24283b;
            border: 1px solid #414868;
            color: #c0caf5;
            padding: 10px;
            border-radius: 4px;
            width: 100px;
            font-size: 16px;
            font-weight: bold;
        }
        .calc-input-group input:focus {
            outline: none;
            border-color: #7aa2f7;
        }
        .calc-results-header h3 {
            color: #bb9af7;
            margin: 0 0 5px 0;
        }
        .calc-table {
            width: 100%;
            background: transparent !important;
            border: none !important;
            border-collapse: collapse;
        }
        .calc-table th {
            background: #24283b !important;
            color: #7dcfff !important;
            border: none !important;
            text-align: left;
            padding: 10px;
        }
        .calc-table td {
            border: none !important;
            border-bottom: 1px solid #24283b !important;
            padding: 10px;
            color: #c0caf5;
        }
        .calc-unavailable td {
            color: #565f89;
        }
        .calc-btn {
            margin-top: 19px;
            background: #7aa2f7;
            color: #1a1b26;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.2s ease;
        }
        .calc-btn:hover {
            background: #8caaee;
        }
    `;
    document.head.appendChild(style);
    // Initial Layout Setup
    calcContainer.innerHTML = `
        <div class="calc-wrapper">
            <h2>${currentSkill} Calculator</h2>
            <div class="calc-inputs">
                <div class="calc-input-group">
                    <label>Current Level</label>
                    <input type="number" id="calc-current-lvl" value="1" min="1" max="99">
                </div>
                <div class="calc-input-group">
                    <label>Target Level</label>
                    <input type="number" id="calc-target-lvl" value="10" min="2" max="99">
                </div>
                <button class="calc-btn" id="calc-refresh-btn">Calculate</button>
            </div>
            <div id="calc-results"></div>
        </div>
    `;
    document.getElementById('calc-current-lvl').addEventListener('change', renderCalculator);
    document.getElementById('calc-target-lvl').addEventListener('change', renderCalculator);
    document.getElementById('calc-refresh-btn').addEventListener('click', renderCalculator);
    // Render initially
    renderCalculator();
});
});

Latest revision as of 19:17, 18 July 2026

$(document).ready(function() {
    $('#ca-nstab-main a').text('Article');
    $('#ca-history a').text('History');
    $('#ca-view a').text('Read');
});
// Skill Calculator logic to be embedded in MediaWiki:Common.js
mw.hook('wikipage.content').add(function($content) {
    // Only run if the page contains a calculator container
    const calcContainer = $content.find('#wiki-skill-calc')[0];
    if (!calcContainer) return;
    if (calcContainer.dataset.initialized) return;
    calcContainer.dataset.initialized = 'true';

    const skillClass = Array.from(calcContainer.classList).find(c => c.startsWith('skill-'));
    if (!skillClass) {
        calcContainer.innerHTML = "<p style='color:red;'>Error: No skill class found on container.</p>";
        return;
    }
    const currentSkill = skillClass.replace('skill-', ''); // e.g., "Mining"

    // Raw XP Table (Level 1 to 99)
    const xpTable = [
        0, 0, 74, 160, 258, 371, 500, 649, 820, 1016, 1241, 1500, 1797, 2139, 2531, 2982, 3500, 4095, 4778, 5563, 6464, 7500, 8690, 10056, 11626, 13429, 15500, 17879, 20612, 23751, 27358, 31500, 36258, 41724, 48003, 55215, 63500, 73017, 83949, 96506, 110930, 127500, 146533, 168397, 193512, 222361, 255500, 293567, 337294, 387523, 445222, 511500, 587634, 675088, 775547, 890944, 1023500, 1175767, 1350676, 1551594, 1782388, 2047500, 2352034, 2701852, 3103688, 3565275, 4095500, 4704568, 5404204, 6207875, 7131050, 8191500, 9409637, 10808909, 12416250, 14262600, 16383500, 18819774, 21618318, 24833000, 28525701, 32767500, 37640048, 43237135, 49666500, 57051902, 65535500, 75280595, 86474770, 99333501, 114104303, 131071500, 150561691, 172950041, 198667502, 228209107, 262143500, 301123982, 345900582, 397335504
    ];

    // Data compiled from the scraped files
    const skillData = {
        "Mining": [
            { "name": "Copper Ore", "reqLevel": 1, "xp": 15 },
            { "name": "Tin Ore", "reqLevel": 1, "xp": 15 },
            { "name": "Iron Ore", "reqLevel": 10, "xp": 30 },
            { "name": "Coal Ore", "reqLevel": 20, "xp": 80 },
            { "name": "Mithril Ore", "reqLevel": 30, "xp": 150 },
            { "name": "Gold Ore", "reqLevel": 40, "xp": 350 },
            { "name": "Essence Geode", "reqLevel": 50, "xp": 550 }
        ],
        "Fishing": [
            { "name": "Minnow", "reqLevel": 1, "xp": 8 },
            { "name": "Common Trout", "reqLevel": 5, "xp": 75 },
            { "name": "Perch", "reqLevel": 10, "xp": 225 },
            { "name": "Bass", "reqLevel": 20, "xp": 200 },
            { "name": "Blue Gill", "reqLevel": 30, "xp": 75 },
            { "name": "Elder Trout", "reqLevel": 40, "xp": 500 },
            { "name": "Carp", "reqLevel": 50, "xp": 1250 }
        ],
        "PotionMaking": [
            { "name": "Weak Health Potion", "reqLevel": 1, "xp": 25 },
            { "name": "Health Potion", "reqLevel": 10, "xp": 100 },
            { "name": "Attack Potion", "reqLevel": 25, "xp": 1500 },
            { "name": "Fishing Potion", "reqLevel": 30, "xp": 2000 },
            { "name": "Mining Potion", "reqLevel": 30, "xp": 2000 },
            { "name": "Shield Potion", "reqLevel": 40, "xp": 3500 },
            { "name": "Strong Health Potion", "reqLevel": 50, "xp": 5000 },
            { "name": "Strong Shield Potion", "reqLevel": 60, "xp": 8000 }
        ]
    };

    const actions = skillData[currentSkill];
    if (!actions) {
        calcContainer.innerHTML = "<p style='color:red;'>Calculator data not found for " + currentSkill + ".</p>";
        return;
    }

    // Convert level to XP
    function levelToXP(level) {
        if (level < 1) return xpTable[1];
        if (level > 99) return xpTable[99];
        return xpTable[level];
    }

    function calculateActions(currentXP, targetXP) {
        const xpNeeded = targetXP - currentXP;
        if (xpNeeded <= 0) return [];
        return actions.map(action => {
            const amount = Math.ceil(xpNeeded / action.xp);
            return { ...action, amount, xpNeeded };
        }).sort((a, b) => a.reqLevel - b.reqLevel);
    }

    function renderCalculator() {
        const currentLvl = parseInt(document.getElementById('calc-current-lvl').value) || 1;
        const targetLvl = parseInt(document.getElementById('calc-target-lvl').value) || (currentLvl + 1);

        const currentXP = levelToXP(currentLvl);
        const targetXP = levelToXP(targetLvl);
        const xpNeeded = targetXP - currentXP;

        let html = `
            <div class="calc-results-header">
                <h3>XP Required: <span>${xpNeeded.toLocaleString()}</span></h3>
                <p>From Level ${currentLvl} (${currentXP.toLocaleString()} XP) to Level ${targetLvl} (${targetXP.toLocaleString()} XP)</p>
            </div>
            <table class="wikitable calc-table">
                <tr><th>Action</th><th>Level Req.</th><th>XP/Action</th><th>Total Actions</th></tr>
        `;

        if (xpNeeded <= 0) {
            html += `<tr><td colspan="4" style="text-align:center;">You have already reached the target level!</td></tr>`;
        } else {
            const results = calculateActions(currentXP, targetXP);
            results.forEach(res => {
                const available = currentLvl >= res.reqLevel;
                const rowClass = available ? '' : 'calc-unavailable';
                const opacity = available ? '1' : '0.5';
                html += `
                    <tr class="${rowClass}" style="opacity: ${opacity}">
                        <td><strong>${res.name}</strong></td>
                        <td>${res.reqLevel}</td>
                        <td>${res.xp}</td>
                        <td><strong>${res.amount.toLocaleString()}</strong></td>
                    </tr>
                `;
            });
        }
        html += `</table>`;
        document.getElementById('calc-results').innerHTML = html;
    }

    // Inject CSS
    const style = document.createElement('style');
    style.innerHTML = `
        .calc-wrapper {
            background: #1a1b26;
            color: #a9b1d6;
            padding: 20px;
            border-radius: 8px;
            border: 1px solid #414868;
            max-width: 600px;
            margin: 20px 0;
            font-family: 'Inter', sans-serif;
            box-shadow: 0 10px 25px rgba(0,0,0,0.5);
        }
        .calc-wrapper h2 {
            margin-top: 0;
            color: #7aa2f7;
            border-bottom: 2px solid #24283b;
            padding-bottom: 10px;
        }
        .calc-inputs {
            display: flex;
            gap: 20px;
            margin-bottom: 20px;
        }
        .calc-input-group {
            display: flex;
            flex-direction: column;
            gap: 5px;
        }
        .calc-input-group label {
            font-size: 12px;
            text-transform: uppercase;
            font-weight: bold;
            color: #565f89;
        }
        .calc-input-group input {
            background: #24283b;
            border: 1px solid #414868;
            color: #c0caf5;
            padding: 10px;
            border-radius: 4px;
            width: 100px;
            font-size: 16px;
            font-weight: bold;
        }
        .calc-input-group input:focus {
            outline: none;
            border-color: #7aa2f7;
        }
        .calc-results-header h3 {
            color: #bb9af7;
            margin: 0 0 5px 0;
        }
        .calc-table {
            width: 100%;
            background: transparent !important;
            border: none !important;
            border-collapse: collapse;
        }
        .calc-table th {
            background: #24283b !important;
            color: #7dcfff !important;
            border: none !important;
            text-align: left;
            padding: 10px;
        }
        .calc-table td {
            border: none !important;
            border-bottom: 1px solid #24283b !important;
            padding: 10px;
            color: #c0caf5;
        }
        .calc-unavailable td {
            color: #565f89;
        }
        .calc-btn {
            margin-top: 19px;
            background: #7aa2f7;
            color: #1a1b26;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.2s ease;
        }
        .calc-btn:hover {
            background: #8caaee;
        }
    `;
    document.head.appendChild(style);

    // Initial Layout Setup
    calcContainer.innerHTML = `
        <div class="calc-wrapper">
            <h2>${currentSkill} Calculator</h2>
            <div class="calc-inputs">
                <div class="calc-input-group">
                    <label>Current Level</label>
                    <input type="number" id="calc-current-lvl" value="1" min="1" max="99">
                </div>
                <div class="calc-input-group">
                    <label>Target Level</label>
                    <input type="number" id="calc-target-lvl" value="10" min="2" max="99">
                </div>
                <button class="calc-btn" id="calc-refresh-btn">Calculate</button>
            </div>
            <div id="calc-results"></div>
        </div>
    `;

    document.getElementById('calc-current-lvl').addEventListener('change', renderCalculator);
    document.getElementById('calc-target-lvl').addEventListener('change', renderCalculator);
    document.getElementById('calc-refresh-btn').addEventListener('click', renderCalculator);

    // Render initially
    renderCalculator();
});