MediaWiki:Common.js: Difference between revisions

From Winds Of Valen Wiki
Jump to navigation Jump to search
Add always-visible reader mode escape button
Switched to use CSS class for passing arguments
 
(5 intermediate revisions by the same user not shown)
Line 1: Line 1:
/* OSRS Custom JS Framework */
$(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';


// 1. Asynchronous Calculator Loader
    const skillClass = Array.from(calcContainer.classList).find(c => c.startsWith('skill-'));
$(function() {
    if (!skillClass) {
    if ($('.jcConfig').length > 0) {
        calcContainer.innerHTML = "<p style='color:red;'>Error: No skill class found on container.</p>";
        // Asynchronously pull in calculator scripts
         return;
        mw.loader.getScript('/resources/assets/calc.js').then(function() {
            mw.loader.load('/resources/assets/calc.css', 'text/css');
            console.log("OSRS calculators loaded.");
         });
     }
     }
});
    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 }
        ]
    };


// 2. Reader Mode Toggle
    const actions = skillData[currentSkill];
$(function() {
    if (!actions) {
    var isReaderMode = localStorage.getItem('osrs-reader-mode') === 'true';
        calcContainer.innerHTML = "<p style='color:red;'>Calculator data not found for " + currentSkill + ".</p>";
        return;
    }


     function applyReaderMode() {
    // Convert level to XP
         $('body').toggleClass('reader-mode-active', isReaderMode);
     function levelToXP(level) {
         $('#reader-mode-escape').toggle(isReaderMode);
         if (level < 1) return xpTable[1];
        if (level > 99) return xpTable[99];
         return xpTable[level];
     }
     }


     function toggleReaderMode() {
     function calculateActions(currentXP, targetXP) {
         isReaderMode = !isReaderMode;
         const xpNeeded = targetXP - currentXP;
         localStorage.setItem('osrs-reader-mode', String(isReaderMode));
         if (xpNeeded <= 0) return [];
         applyReaderMode();
        return actions.map(action => {
            const amount = Math.ceil(xpNeeded / action.xp);
            return { ...action, amount, xpNeeded };
         }).sort((a, b) => a.reqLevel - b.reqLevel);
     }
     }


     // Insert toggle button into personal nav (in the header)
     function renderCalculator() {
    var btn = $('<li><a href="#" id="pt-reader-mode">Toggle Reader Mode</a></li>');
        const currentLvl = parseInt(document.getElementById('calc-current-lvl').value) || 1;
    btn.on('click', function(e) {
        const targetLvl = parseInt(document.getElementById('calc-target-lvl').value) || (currentLvl + 1);
        e.preventDefault();
 
         toggleReaderMode();
        const currentXP = levelToXP(currentLvl);
     });
        const targetXP = levelToXP(targetLvl);
     $('#p-personal ul').prepend(btn);
        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>
    `;


     // Also inject a fixed escape button that is ALWAYS visible in reader mode
     document.getElementById('calc-current-lvl').addEventListener('change', renderCalculator);
    var escapeBtn = $('<div id="reader-mode-escape" style="' +
    document.getElementById('calc-target-lvl').addEventListener('change', renderCalculator);
        'display:none; position:fixed; top:10px; left:10px; z-index:99999;' +
     document.getElementById('calc-refresh-btn').addEventListener('click', renderCalculator);
        'background:rgba(0,0,0,0.7); color:#fff; padding:8px 14px;' +
        'border-radius:6px; cursor:pointer; font-size:13px; font-weight:bold;' +
        'box-shadow:0 2px 8px rgba(0,0,0,0.4);">&#x2715; Exit Reader Mode</div>');
    escapeBtn.on('click', function() {
        toggleReaderMode();
     });
    $('body').append(escapeBtn);


     // Apply state on page load
     // Render initially
     applyReaderMode();
     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();
});