MediaWiki:Common.js: Difference between revisions

From Winds Of Valen Wiki
Jump to navigation Jump to search
Add simple image lightbox
Switched to use CSS class for passing arguments
 
(4 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
    ];


// 2. Reader Mode Toggle
    // Data compiled from the scraped files
$(function() {
    const skillData = {
     var isReaderMode = localStorage.getItem('osrs-reader-mode') === 'true';
        "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 }
        ]
     };


     function applyReaderMode() {
     const actions = skillData[currentSkill];
         $('body').toggleClass('reader-mode-active', isReaderMode);
    if (!actions) {
         $('#reader-mode-escape').toggle(isReaderMode);
         calcContainer.innerHTML = "<p style='color:red;'>Calculator data not found for " + currentSkill + ".</p>";
         return;
     }
     }


     function toggleReaderMode() {
    // Convert level to XP
         isReaderMode = !isReaderMode;
     function levelToXP(level) {
         localStorage.setItem('osrs-reader-mode', String(isReaderMode));
         if (level < 1) return xpTable[1];
         applyReaderMode();
         if (level > 99) return xpTable[99];
         return xpTable[level];
     }
     }


     // Insert toggle button into personal nav (in the header)
     function calculateActions(currentXP, targetXP) {
    var btn = $('<li><a href="#" id="pt-reader-mode">Toggle Reader Mode</a></li>');
        const xpNeeded = targetXP - currentXP;
    btn.on('click', function(e) {
        if (xpNeeded <= 0) return [];
        e.preventDefault();
        return actions.map(action => {
        toggleReaderMode();
            const amount = Math.ceil(xpNeeded / action.xp);
    });
            return { ...action, amount, xpNeeded };
    $('#p-personal ul').prepend(btn);
        }).sort((a, b) => a.reqLevel - b.reqLevel);
    }


     // Also inject a fixed escape button that is ALWAYS visible in reader mode
     function renderCalculator() {
    var escapeBtn = $('<div id="reader-mode-escape" style="' +
         const currentLvl = parseInt(document.getElementById('calc-current-lvl').value) || 1;
         'display:none; position:fixed; top:10px; left:10px; z-index:99999;' +
         const targetLvl = parseInt(document.getElementById('calc-target-lvl').value) || (currentLvl + 1);
        '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
        const currentXP = levelToXP(currentLvl);
    applyReaderMode();
        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;
    }


// 3. Simple Image Lightbox
    // Inject CSS
$(function() {
     const style = document.createElement('style');
     $('a.image').on('click', function(e) {
    style.innerHTML = `
        e.preventDefault();
         .calc-wrapper {
       
             background: #1a1b26;
        var imgUrl = $(this).attr('href');
            color: #a9b1d6;
         if ($(this).find('img').length > 0) {
             padding: 20px;
             // Get original image url by stripping /thumb/ and the filename at the end
             border-radius: 8px;
             var thumbSrc = $(this).find('img').attr('src');
            border: 1px solid #414868;
             if (thumbSrc.indexOf('/images/thumb/') !== -1) {
            max-width: 600px;
                var parts = thumbSrc.split('/');
            margin: 20px 0;
                parts.pop(); // remove the scaled filename like 300px-Scouts.png
            font-family: 'Inter', sans-serif;
                imgUrl = parts.join('/').replace('/images/thumb/', '/images/');
             box-shadow: 0 10px 25px rgba(0,0,0,0.5);
             } else {
                imgUrl = thumbSrc;
            }
         }
         }
          
         .calc-wrapper h2 {
        var overlay = $('<div id="osrs-lightbox" style="position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.85); z-index:999999; display:flex; align-items:center; justify-content:center; cursor:pointer;"></div>');
            margin-top: 0;
         var img = $('<img src="' + imgUrl + '" style="max-width:95%; max-height:95%; box-shadow:0 0 20px rgba(0,0,0,0.8); border:2px solid #b3a07d;" />');
            color: #7aa2f7;
          
            border-bottom: 2px solid #24283b;
         var loading = $('<div style="color:white; font-family:sans-serif; position:absolute;">Loading...</div>');
            padding-bottom: 10px;
         overlay.append(loading);
        }
          
        .calc-inputs {
         img.on('load', function() {
            display: flex;
             loading.remove();
            gap: 20px;
         });
            margin-bottom: 20px;
          
        }
        overlay.append(img);
        .calc-input-group {
       
            display: flex;
         overlay.on('click', function() {
            flex-direction: column;
             $(this).fadeOut(200, function() {
            gap: 5px;
                 $(this).remove();
        }
            });
        .calc-input-group label {
        });
            font-size: 12px;
       
            text-transform: uppercase;
        $('body').append(overlay);
            font-weight: bold;
        overlay.hide().fadeIn(200);
            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();
});