// ==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); })();