Reorder Certificates

Reorders certificates based on completion percentage and simplifies the interface

当前为 2025-03-06 提交的版本,查看 最新版本

// ==UserScript==
// @name         Reorder Certificates
// @namespace    FarmRPGCertificates
// @version      1.0.26
// @description  Reorders certificates based on completion percentage and simplifies the interface
// @author       ClientCoin
// @match        *farmrpg.com/index.php
// @match        *farmrpg.com/
// @match        *alpha.farmrpg.com/
// @match        *alpha.farmrpg.com/index.php
// @icon         https://www.google.com/s2/favicons?sz=64&domain=farmrpg.com
// @grant        none
// ==/UserScript==



    function reorderCertificates() {

        const locName = location.hash.slice(location.hash.search(/[^#!\/]/), location.hash.search(/.php/))
        if (locName != "temple") {
            return;
        }

        //console.log("%c[Script] Reordering certificates...", "color: cyan; font-weight: bold;");

        let allContentBlocks = document.querySelectorAll(".content-block");
        let certificatesContainer = null;

        allContentBlocks.forEach(block => {
            let titleElement = block.querySelector(".content-block-title");
            if (titleElement && titleElement.textContent.trim() === "Secret Chains") {
                certificatesContainer = block.querySelector(".list-block ul");
            }
        });

        if (!certificatesContainer) {
            console.warn("%c[Script] Certificates container not found. Exiting.", "color: red;");
            return;
        }

        let certificateElements = Array.from(certificatesContainer.querySelectorAll("li"));
        if (certificateElements.length === 0) {
            console.warn("%c[Script] No certificate elements found. Exiting.", "color: red;");
            return;
        }

        //console.log("%c[Script] Found certificate elements:", "color: cyan;", certificateElements);

        let certificateBlocks = certificateElements.map(certificate => {
            let progress = extractProgress(certificate);
            //console.log(`%c[Script] Extracted progress: ${progress}%`, "color: lightgreen;");
            return { element: certificate, progress };
        });

        let completedCount = certificateBlocks.filter(c => c.progress === 100).length;
        let maxCount = certificateBlocks.length;

        // Sorting: Completed (100%) certificates at the bottom
        certificateBlocks.sort((a, b) => {
            // Move 100% completed items to the bottom
            if (a.progress === 100 && b.progress !== 100) return 1;
            if (b.progress === 100 && a.progress !== 100) return -1;

            // Sort by highest progress first
            let progressSort = b.progress - a.progress;
            if (progressSort !== 0) return progressSort;

            // If progress is the same, sort by itemsToGo (ascending)
            return a.itemsToGo - b.itemsToGo;
        });



        //console.log("%c[Script] Certificates sorted by progress.", "color: cyan;");

certificatesContainer.innerHTML = ""; // Clear the list

    // Insert summary at the top
    let summaryElement = document.createElement("li");
    summaryElement.innerHTML = `
        <div style="font-weight: bold; color: gold; text-align: center; padding: 5px;">
            ${completedCount}/${maxCount} Certificates COMPLETE
        </div>
    `;
    certificatesContainer.appendChild(summaryElement);

    // Append reordered certificates
    certificateBlocks.forEach(({ element }) => {
        simplifyText(element);
        certificatesContainer.appendChild(element);
    });
        console.log("Certificates reordered and updated.");
    }

    function extractProgress(element) {
        let titleElement = element.querySelector(".item-title");
        if (!titleElement) {
            console.warn("%c[Script] Title element not found in certificate block.", "color: orange;");
            return 0;
        }

        // Extract sacrificed and required item counts
        let progressMatch = titleElement.innerText.match(/(\d{1,3}(?:,\d{3})*) \/ (\d{1,3}(?:,\d{3})*) Items Sacrificed/);

        if (!progressMatch) {
            console.warn("%c[Script] No progress values found in:", "color: orange;", titleElement.innerText);
            return 0;
        }

        let sacrificed = parseInt(progressMatch[1].replace(/,/g, '')); // Remove commas
        let required = parseInt(progressMatch[2].replace(/,/g, ''));

        // Calculate precise percentage
        let progress = (sacrificed / required) * 100;

        //console.log(`%c[Script] Corrected progress: ${progress.toFixed(2)}% (${sacrificed}/${required})`, "color: lightgreen;");

        return progress;
    }


    function simplifyText(element) {
        let titleElement = element.querySelector(".item-title");
        let imgElement = element.querySelector(".item-media img");

        if (!titleElement) {
            console.warn("%c[Script] Title element not found in certificate block.", "color: orange;");
            return;
        }
        if (element.classList.contains("row")) {
            element.style.marginBottom = "0px";
        }
/*
        element.style.padding = "1px 0"; // Reduce vertical padding
        element.style.margin = "2px"; // Remove margins
        element.style.lineHeight = "0"; // Reduce space between lines

        let itemInner = element.querySelector(".item-inner");
        if (itemInner) {
            itemInner.style.padding = "1px 0";
        }

        let itemTitle = element.querySelector(".item-title");
        if (itemTitle) {
            itemTitle.style.margin = "0";
        }
*/

        let nameMatch = titleElement.innerText.match(/Certificate of (.+?) Giving/);
        let progressMatch = titleElement.innerText.match(/(\d+(?:,\d+)?) \/ (\d+(?:,\d+)?) Items Sacrificed/);
        let onHandMatch = titleElement.innerText.match(/Sacrifice: (.+?) \(You have (\d+(?:,\d+)?)\)/);

        if (nameMatch && progressMatch && onHandMatch) {
            let itemName = nameMatch[1];
            let totalRequired = parseInt(progressMatch[2].replace(/,/g, ''));
            let sacrificed = parseInt(progressMatch[1].replace(/,/g, ''));
            let onHand = parseInt(onHandMatch[2].replace(/,/g, ''));
            let itemsToGo = totalRequired - sacrificed;
            let progressPercent = (sacrificed / totalRequired) * 100;

            let color = "white";
            if (itemsToGo === 0) color = "gold";
            else if (progressPercent >= 75) color = "lightgreen";
            else if (sacrificed < 10000) color = "grey";

let nn = "COMPLETE";

            let outputPad = itemsToGo === 0 ? `${nn.toLocaleString().padStart(13, '\u00A0')} [${onHand.toLocaleString().padStart(7, '\u00A0')} on hand]` : `${itemsToGo.toLocaleString().padStart(7, '\u00A0')} to go [${onHand.toLocaleString().padStart(7, '\u00A0')} on hand]`

let textContent =
     `<span style="color: ${color};">
        <div style="display:flex;flex-flow:row nowrap;align-items:center;vertical-align:middle;margin-top:auto;margin-bottom:auto;">
            <div style="width:20%;text-align:left;">${itemName}: </div>
            <div style="width:80%;text-align:left;">${outputPad}</div>
        </div>
       </span>`;



            titleElement.outerHTML = `<span style="font-family: monospace; width: 100%">${textContent}</span>`;

            if (imgElement) {
                imgElement.style.width = "1rem";
                imgElement.style.height = "1rem";
            }

            let smallImgElement = element.querySelector(".item-after img");
            if (smallImgElement) {
                smallImgElement.style.width = "1rem";
                smallImgElement.style.height = "1rem";
            }

            //console.log(`%c[Script] Updated text for ${itemName}: ${itemsToGo} to go, ${onHand} on hand.`, "color: lightblue;");
        } else {
            console.warn("%c[Script] Failed to extract data for certificate text update.", "color: orange;");
        }
    }

    function init() {
        //console.log("%c[Script] Initializing...", "color: green;");
        reorderCertificates();
        injectCSS();
    }

    window.addEventListener("load", init); // Ensures the script runs only once


function injectCSS() {
    const style = document.createElement('style');
    style.innerHTML = `
.list-block .item-content {
    min-height: 0 !important;
}

.list-block .item-inner {
    min-height: 0 !important;
}



    `;
    document.head.appendChild(style);
}

$(document).ready( () => {
    const target = document.querySelector("#fireworks")
    const observer = new MutationObserver( mutation => {if (mutation[0]?.attributeName == "data-page") reorderCertificates()} )


    const config = {
        attributes: true,
        childlist: true,
        subtree: true
    }


    observer.observe(target, config);

    const observera = new MutationObserver(() => {
        reorderCertificates();
    });

})