UI Tweaks By web crawler 0 installs Rating 0.0 (0) approved

Neopets Petpet Lab - Show All Petpets + Click to Zap

Shows all Petpet Lab petpets at once and lets you click a petpet card to select and zap it.
lab neopets petpet lab show all
Install
https://www.scriptneo.com/script/neopets-petpet-lab-show-all-petpets-click-to-zap

Version selector


SHA256
e2099c988dcb984c0f57fa758ce525cbc4e4410ffd5e821366c708e580b5dd72
No scan flags on this version.

Source code

// ==UserScript==
// @name         Neopets Petpet Lab - Show All Petpets + Click to Zap
// @namespace    https://www.neopets.com/
// @version      1.0.0
// @description  Shows all Petpet Lab petpets at once and lets you click a petpet card to select and zap it.
// @author       You
// @match        https://www.neopets.com/labray/petpet.phtml*
// @match        https://www.neopets.com/petpetlab.phtml*
// @match        https://www.neopets.com/*petpet*lab*
// @grant        none
// @downloadURL  https://www.scriptneo.com/scripts/download.php?id=29
// @updateURL    https://www.scriptneo.com/scripts/download.php?id=29
// ==/UserScript==

(function () {
    "use strict";

    const CONFIG = {
        requireConfirmBeforeZap: true,
        hideOriginalSelect: false,
        autoScrollToGrid: false
    };

    function ready(fn) {
        if (document.readyState === "loading") {
            document.addEventListener("DOMContentLoaded", fn);
            return;
        }

        fn();
    }

    function injectCss() {
        const css = `
            #np-ppl-helper {
                max-width: 920px;
                margin: 18px auto;
                padding: 14px;
                border: 2px solid #2d2418;
                border-radius: 12px;
                background: #fff8dc;
                box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
                font-family: Arial, Helvetica, sans-serif;
                color: #2d2418;
            }

            #np-ppl-helper h2 {
                margin: 0 0 6px 0;
                font-size: 20px;
                line-height: 1.25;
                color: #2d2418;
            }

            #np-ppl-helper .np-ppl-subtitle {
                margin: 0 0 12px 0;
                font-size: 13px;
                color: #5b4a31;
            }

            #np-ppl-helper .np-ppl-status {
                margin: 0 0 12px 0;
                padding: 9px 10px;
                border-radius: 8px;
                background: #f6e8ad;
                border: 1px solid #d7bd63;
                font-size: 13px;
                font-weight: bold;
            }

            #np-ppl-grid {
                display: grid;
                grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
                gap: 12px;
            }

            .np-ppl-card {
                border: 2px solid #c9a646;
                border-radius: 12px;
                background: #fffdf2;
                padding: 10px;
                text-align: center;
                cursor: pointer;
                transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
                min-height: 155px;
            }

            .np-ppl-card:hover {
                transform: translateY(-2px);
                border-color: #9b7424;
                box-shadow: 0 4px 14px rgba(0, 0, 0, 0.22);
            }

            .np-ppl-card.np-ppl-selected {
                border-color: #4b8f29;
                background: #f0ffe9;
                box-shadow: 0 0 0 3px rgba(75, 143, 41, 0.18);
            }

            .np-ppl-card img {
                display: block;
                max-width: 80px;
                max-height: 80px;
                width: auto;
                height: auto;
                margin: 0 auto 8px auto;
                image-rendering: auto;
            }

            .np-ppl-name {
                font-size: 14px;
                font-weight: bold;
                color: #2d2418;
                margin-bottom: 4px;
                word-break: break-word;
            }

            .np-ppl-pet {
                font-size: 11px;
                color: #775f3a;
                margin-bottom: 6px;
                word-break: break-word;
            }

            .np-ppl-sound {
                font-size: 12px;
                color: #4b3d28;
                font-style: italic;
                min-height: 16px;
            }

            .np-ppl-zap-label {
                margin-top: 8px;
                display: inline-block;
                padding: 5px 8px;
                border-radius: 999px;
                background: #ffdf57;
                border: 1px solid #c19922;
                color: #2d2418;
                font-size: 11px;
                font-weight: bold;
            }

            .np-ppl-error {
                max-width: 720px;
                margin: 18px auto;
                padding: 12px;
                background: #ffe5e5;
                border: 1px solid #b94444;
                color: #631f1f;
                border-radius: 8px;
                font-weight: bold;
            }

            .np-ppl-original-visible .ppl-petpet {
                display: inline-block !important;
                vertical-align: top;
                margin: 8px;
            }

            .np-ppl-original-visible .ppl-petpet.ppl-hidden {
                display: inline-block !important;
            }
        `;

        const style = document.createElement("style");
        style.textContent = css;
        document.head.appendChild(style);
    }

    function getLabContent() {
        return document.querySelector("#PetPetLabContent") || document.body;
    }

    function getPetpetSelect() {
        const selects = Array.from(document.querySelectorAll("select"));

        return selects.find(function (select) {
            return Array.from(select.options).some(function (option) {
                return option.value && document.getElementById("PPL" + option.value);
            });
        }) || null;
    }

    function getZapButton() {
        return document.querySelector("#PPLButton") || Array.from(document.querySelectorAll("button")).find(function (button) {
            return /zap/i.test(button.textContent || "");
        }) || null;
    }

    function normalizeText(value) {
        return String(value || "").replace(/\s+/g, " ").trim();
    }

    function getPetNameFromId(petId, select) {
        if (!select) {
            return petId;
        }

        const option = Array.from(select.options).find(function (opt) {
            return opt.value === petId;
        });

        return option ? normalizeText(option.textContent) : petId;
    }

    function getPetpetData(select) {
        const petpetDivs = Array.from(document.querySelectorAll(".ppl-petpet[id^='PPL']"));

        return petpetDivs.map(function (div) {
            const petId = div.id.replace(/^PPL/, "");
            const img = div.querySelector("img");
            const sound = div.querySelector("p");

            return {
                id: petId,
                petName: getPetNameFromId(petId, select),
                petpetName: img ? normalizeText(img.getAttribute("alt")) : "Unknown Petpet",
                imgSrc: img ? img.src : "",
                sound: sound ? normalizeText(sound.textContent) : "",
                originalNode: div
            };
        }).filter(function (petpet) {
            return petpet.id && petpet.originalNode;
        });
    }

    function dispatchNativeChange(element) {
        element.dispatchEvent(new Event("change", {
            bubbles: true,
            cancelable: true
        }));
    }

    function selectPetpetById(petId, select, statusBox) {
        if (!select) {
            setStatus(statusBox, "Could not find the Petpet dropdown.", true);
            return false;
        }

        const option = Array.from(select.options).find(function (opt) {
            return opt.value === petId;
        });

        if (!option) {
            setStatus(statusBox, "Could not find this Petpet in the dropdown: " + petId, true);
            return false;
        }

        select.value = petId;
        dispatchNativeChange(select);

        if (typeof window.selectPetpet === "function") {
            try {
                window.selectPetpet(select);
            } catch (error) {
                console.warn("Petpet Lab Helper: selectPetpet failed, continuing anyway.", error);
            }
        }

        return true;
    }

    function clickZapButton(button, statusBox) {
        if (!button) {
            setStatus(statusBox, "Could not find the zap button.", true);
            return false;
        }

        if (button.disabled) {
            button.disabled = false;
        }

        button.click();
        return true;
    }

    function setStatus(statusBox, message, isError) {
        if (!statusBox) {
            return;
        }

        statusBox.textContent = message;
        statusBox.style.background = isError ? "#ffe5e5" : "#f6e8ad";
        statusBox.style.borderColor = isError ? "#b94444" : "#d7bd63";
        statusBox.style.color = isError ? "#631f1f" : "#2d2418";
    }

    function buildHelper(petpets, select, zapButton) {
        const labContent = getLabContent();

        const old = document.querySelector("#np-ppl-helper");
        if (old) {
            old.remove();
        }

        const helper = document.createElement("div");
        helper.id = "np-ppl-helper";

        const title = document.createElement("h2");
        title.textContent = "Petpet Lab Quick Zap";

        const subtitle = document.createElement("p");
        subtitle.className = "np-ppl-subtitle";
        subtitle.textContent = "All petpets are shown below. Click one to select it and zap it.";

        const statusBox = document.createElement("div");
        statusBox.className = "np-ppl-status";
        statusBox.textContent = "Found " + petpets.length + " petpet" + (petpets.length === 1 ? "" : "s") + ".";

        const grid = document.createElement("div");
        grid.id = "np-ppl-grid";

        petpets.forEach(function (petpet) {
            const card = document.createElement("button");
            card.type = "button";
            card.className = "np-ppl-card";
            card.dataset.petpetId = petpet.id;
            card.title = "Click to zap " + petpet.petpetName + " attached to " + petpet.petName;

            const img = document.createElement("img");
            img.src = petpet.imgSrc;
            img.alt = petpet.petpetName;

            const petpetName = document.createElement("div");
            petpetName.className = "np-ppl-name";
            petpetName.textContent = petpet.petpetName;

            const petName = document.createElement("div");
            petName.className = "np-ppl-pet";
            petName.textContent = "Pet: " + petpet.petName;

            const sound = document.createElement("div");
            sound.className = "np-ppl-sound";
            sound.textContent = petpet.sound;

            const zapLabel = document.createElement("div");
            zapLabel.className = "np-ppl-zap-label";
            zapLabel.textContent = "Click to Zap";

            card.appendChild(img);
            card.appendChild(petpetName);
            card.appendChild(petName);
            card.appendChild(sound);
            card.appendChild(zapLabel);

            card.addEventListener("click", function () {
                Array.from(grid.querySelectorAll(".np-ppl-card")).forEach(function (otherCard) {
                    otherCard.classList.remove("np-ppl-selected");
                });

                card.classList.add("np-ppl-selected");

                const selected = selectPetpetById(petpet.id, select, statusBox);

                if (!selected) {
                    return;
                }

                setStatus(statusBox, "Selected " + petpet.petpetName + " attached to " + petpet.petName + ".", false);

                if (CONFIG.requireConfirmBeforeZap) {
                    const ok = window.confirm(
                        "Zap this Petpet?\n\n" +
                        "Pet: " + petpet.petName + "\n" +
                        "Petpet: " + petpet.petpetName + "\n\n" +
                        "This will use your Petpet Lab zap."
                    );

                    if (!ok) {
                        setStatus(statusBox, "Cancelled. " + petpet.petpetName + " is selected but was not zapped.", false);
                        return;
                    }
                }

                setStatus(statusBox, "Zapping " + petpet.petpetName + " attached to " + petpet.petName + "...", false);
                clickZapButton(zapButton, statusBox);
            });

            grid.appendChild(card);
        });

        helper.appendChild(title);
        helper.appendChild(subtitle);
        helper.appendChild(statusBox);
        helper.appendChild(grid);

        const zapsText = document.querySelector(".ppl-zaps");
        if (zapsText && zapsText.parentNode) {
            zapsText.insertAdjacentElement("afterend", helper);
        } else {
            labContent.insertBefore(helper, labContent.firstChild);
        }

        if (CONFIG.autoScrollToGrid) {
            helper.scrollIntoView({
                behavior: "smooth",
                block: "start"
            });
        }
    }

    function showOriginalPetpets() {
        document.documentElement.classList.add("np-ppl-original-visible");

        Array.from(document.querySelectorAll(".ppl-petpet.ppl-hidden")).forEach(function (petpet) {
            petpet.classList.remove("ppl-hidden");
            petpet.style.display = "inline-block";
        });
    }

    function hideOriginalSelectIfNeeded(select) {
        if (!CONFIG.hideOriginalSelect || !select) {
            return;
        }

        select.style.display = "none";
    }

    function showError(message) {
        const error = document.createElement("div");
        error.className = "np-ppl-error";
        error.textContent = message;

        const labContent = getLabContent();
        labContent.insertBefore(error, labContent.firstChild);
    }

    function init() {
        injectCss();

        const select = getPetpetSelect();
        const zapButton = getZapButton();
        const petpets = getPetpetData(select);

        if (!petpets.length) {
            showError("Petpet Lab Helper could not find any petpets on this page.");
            return;
        }

        showOriginalPetpets();
        hideOriginalSelectIfNeeded(select);
        buildHelper(petpets, select, zapButton);
    }

    ready(init);
})();