Strudels

Neopets Inventory Direct Quick Stockinator

Apr 16th, 2026 (edited)
399
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Neopets Inventory Direct Quick Stockinator
  3. // @namespace    http://tampermonkey.net/
  4. // @version      1.6 [28/5/26]
  5. // @description  Quick Stock interface directly from inventory
  6. // @author       Rippy
  7. // @icon         https://www.google.com/s2/favicons?sz=64&domain=neopets.com
  8. // @include      *://*.neopets.com/inventory.phtml*
  9. // @grant        unsafeWindow
  10. // @grant        GM_setValue
  11. // @grant        GM_getValue
  12. // ==/UserScript==
  13.  
  14. /*  ========================= INSTRUCTIONS =========================
  15.     - Adds a new menu to the top of the inventory page to start quickstock selection
  16.     - Click on items to select/deselect them, then pick an action button (stock/deposit/discard/etc)
  17.     - Unlike regular quickstock, you can only do one action at a time but you get a nicer visual interface I guess
  18.     - Donate and Discard will prompt a confirmation before proceeding
  19.     - If your selection includes some invalid items for your action choice e.g. picking a mix of wearables and nonwearables and then clicking Place in Closet should only move the wearables,
  20.       leaving the nonwearables behind
  21.     - If the entire selection is invalid for the action e.g. picking only nonwearables and then clicking Place in Closet should prompt an error pop-up
  22.  
  23.  
  24.     - Tick the Bulk Select option to select a ton of items at once, similar to "Check All". When you click on an unselected item, that item and everything to its left will automatically be selected.
  25.       Clicking on a selected item will deselect that item only. This option is useful if you have a group of default items you always want in your inventory e.g. a bunch of Pant Devil Attractors,
  26.       so you can just click on the item to its left and quickstock everything past that point without touching those default items.
  27.     - If you want "Check All" functionality, just use Bulk Select and click on the bottom right-most item and that will naturally select everything.
  28.  
  29.  
  30.     - Probably works best in unstacked view, but should also work in stacked view although obviously you won't be able to access multiples of an item.
  31.     - Also works in NC view.
  32.  
  33.     - NOTE: When discarding NC items, make sure your PIN requirement for NC discarding is disabled. Additionally, only one item can be discarded at a time due to Neopets's internal limitations.
  34.  
  35.     - v1.1: Now saves your inventory preference of stacked vs unstacked, even after closing the page
  36.     - v1.2: Minor support for smaller screen sizes
  37.     - v1.3: Update to support quickstocking within inventory categories/tabs
  38.     - v1.4: Make the quickstock button smaller and less intrusive
  39.     - v1.5: Include ref_ck value as part of request payload
  40.     - v1.6: Added some support for discarding NC items
  41.     ================================================================
  42. */
  43.  
  44. (function() {
  45.     'use strict';
  46.     $(".inv-title-container").before('<div class="buttonpad"><input id="toggleqs"type="submit" value="Start Quick Stock" class="button-default__2020 button-green__2020"></div>');
  47.     $(".inv-title-container").after('<div id="quickstock-menu"></div><br>');
  48.     let quickstocking = false;
  49.     let selected = [];
  50.     let isNC = false;
  51.     let bulkSelect = GM_getValue("quickStockBulkSelect", false);
  52.     let displaySettings = GM_getValue("displaySettings", {type: "np", filter: "", action: "0", stack: "1"});
  53.     const moveLimit = 70;
  54.     const ref_ck = document.body.innerHTML.match(/"_ref_ck":'(.+)'/)[1];
  55.     function updateBulkSelect() {
  56.         if ($("#bulkSelectCheck").length) {
  57.             bulkSelect = $("#bulkSelectCheck").is(":checked");
  58.             GM_setValue("quickStockBulkSelect", bulkSelect);
  59.         }
  60.     }
  61.     function selectItem(id, objid) { // clean this up (that will probably never happen)
  62.         const inventoryTab = document.getElementById("invDisplay").dataset.action;
  63.         const targetDisplay = `#invDisplay${parseInt(inventoryTab)+1}`; // which inventory display is active e.g. Full Inventory/Food/Books/Wearable etc.
  64.         if (bulkSelect) {
  65.             const items = $(targetDisplay).find(".grid-item > .item-img");
  66.             console.log("action: " + inventoryTab);
  67.             for (let i in items) {
  68.                 const did = items[i].getAttribute("id");
  69.                 const oid = items[i].getAttribute("data-objid");
  70.                 if (oid == objid && selected.includes(objid)) { // if the item that was directly clicked on was already selected
  71.                     selected.splice(selected.indexOf(objid), 1); // deselect that item only
  72.                     $(`#selected-item-${did}`).remove();
  73.                 }
  74.                 else if (!selected.includes(oid)) {
  75.                     selected.push(oid);
  76.                     // this convoluted thing is necessary to target the right item id div within the right invDisplay to place the checkmark on, because those item id divs are duplicated across ALL invDisplay divs and are just hidden, so depending on how the page loads the selector may not pick the right one if you just do $(#id)
  77.                     $(`${targetDisplay} > #tableRowsId > .grid-item > #${did}`).after(`<div id='selected-item-${did}' style='background-position: top; background-repeat: no-repeat; width: 100%; height: 100%; position: absolute; left: 0; top: 0; z-index: 2; background-image: url(https://images.neopets.com/neggfest/y23/np/negg-found.svg); background-size: 80%;'></div>`);
  78.                     $(`#selected-item-${did}`).attr("onclick", `selectItem('${did}', '${oid}')`);
  79.                 }
  80.                 if (oid == objid) {
  81.                     break;
  82.                 }
  83.             }
  84.             console.log(selected.length);
  85.         }
  86.         else {
  87.             if (!selected.includes(objid)) {
  88.                 selected.push(objid);
  89.                 $(`${targetDisplay} > #tableRowsId > .grid-item > #${id}`).after(`<div id='selected-item-${id}' style='background-position: top; background-repeat: no-repeat; width: 100%; height: 100%; position: absolute; left: 0; top: 0; z-index: 2; background-image: url(https://images.neopets.com/neggfest/y23/np/negg-found.svg); background-size: 80%;'></div>`);
  90.                 $(`#selected-item-${id}`).attr("onclick", `selectItem('${id}', '${objid}')`);
  91.             }
  92.             else {
  93.                 selected.splice(selected.indexOf(objid), 1);
  94.                 $(`#selected-item-${id}`).remove();
  95.             }
  96.         }
  97.     }
  98.     function quickStockAction(act, confirm=false) {
  99.         console.log(act);
  100.         const actions = ['stock', 'deposit', 'donate', 'discard', 'gallery', 'closet', 'shed', 'chamber'];
  101.         if (!actions.includes(act)) {
  102.             return;
  103.         }
  104.         if (['donate', 'discard'].includes(act) && !confirm) {
  105.             return;
  106.         }
  107.         if (!selected.length) {
  108.             return;
  109.         }
  110.         const payload = {
  111.             items: [],
  112.             cashItems: [],
  113.             _ref_ck: ref_ck
  114.         };
  115.         selected = selected.slice(0, moveLimit);
  116.         for (let i = 0; i < selected.length; i++) {
  117.             if (isNC) {
  118.                 payload.cashItems.push({cashObjId: parseInt(selected[i]), action: act});
  119.             }
  120.             else {
  121.                 payload.items.push({objId: parseInt(selected[i]), action: act});
  122.             }
  123.         }
  124.         if (act === "discard" && isNC) {
  125.             $.ajax({
  126.                 type: "POST",
  127.                 url: "/np-templates/views/process_cash_object.phtml",
  128.                 data: {
  129.                     cash_obj_id: payload.cashItems[0].cashObjId,
  130.                     action: "drop",
  131.                     confirm: "1"
  132.                 },
  133.                 dataType: "html",
  134.                 success: function (response) {
  135.                     console.log("Successfully discarded NC item");
  136.                     window.location.href = "inventory.phtml";
  137.                 },
  138.                 error: function () {
  139.                     console.log("Error");
  140.                     alert("Failed to discard NC item!");
  141.                 },
  142.                 complete: function () {
  143.                     $("#invResult").find(".popup-footer__2020").show();
  144.                 }
  145.             });
  146.         }
  147.         else {
  148.             fetch("/np-templates/ajax/process_quickstock.php", {
  149.                 method:  'POST',
  150.                 headers: { 'Content-Type': 'application/json' },
  151.                 body:    JSON.stringify(payload),
  152.             })
  153.                 .then(function(resp) { return resp.json(); })
  154.                 .then(function(data) {
  155.                     if (data.success) {
  156.                         console.log(data.message);
  157.                         window.location.href = "inventory.phtml";
  158.                     } else {
  159.                         console.log(data.message);
  160.                         alert("This action is not applicable to any of the selected items!");
  161.                     }
  162.                 })
  163.                 .catch(function() {
  164.                 console.log("oops");
  165.             });
  166.         }
  167.     }
  168.     function discardConfirm(act) {
  169.         if (!selected.length) {
  170.             return;
  171.         }
  172.         const msg = `Warning! You are about to ${act.toUpperCase()} ${selected.length} item(s)! Are you sure?`;
  173.         if (!confirm(msg)) {
  174.             return;
  175.         }
  176.         quickStockAction(act, true);
  177.     }
  178.     const inventorySetup = () => {
  179.         const items = $("#invDisplay").find(".grid-item > .item-img");
  180.         if (!items.length) {
  181.             return false;
  182.         }
  183.         items.each((i, e) => {
  184.             const id = e.getAttribute("id");
  185.             const objid = e.getAttribute("data-objid");
  186.             e.setAttribute("onclick", quickstocking ? `invView2('${id}');` : `selectItem('${id}', '${objid}')`);
  187.         });
  188.         isNC = document.getElementById("invDisplay").dataset.type === "nc";
  189.         return true;
  190.     };
  191.     const resetQuickStock = () => {
  192.         $("#toggleqs").attr("class", "button-default__2020 button-green__2020");
  193.         $("#toggleqs").attr("value", "Start Quick Stock");
  194.         $("[id^='selected-item']").remove();
  195.         $("#quickstock-buttons").remove();
  196.         $("#bulkSelect").remove();
  197.         selected = [];
  198.         quickstocking = false;
  199.     };
  200.     const startQuickStock = () => {
  201.         $("#toggleqs").attr("class", "button-default__2020 button-red__2020");
  202.         $("#toggleqs").attr("value", "Cancel Quick Stock");
  203.         $("#quickstock-menu").append(`<div id='bulkSelect'><p style=\"font-family:'CafeteriaBlack'; font-size: 17px; text-align: center;\">Bulk Select?<input type='checkbox' id='bulkSelectCheck' onClick='updateBulkSelect()'/></p></div>
  204.        <div id="quickstock-buttons" class="inv-menubar">
  205.        <ul class="inv-menulinks" style="margin-bottom: 0px !important; background: rgba(0,0,0,0);">
  206.            <li style='width:12.5%'><input class="action-deposit button-default__2020 button-blue__2020" type="submit" value="Deposit"></li>
  207.            <li style='width:12.5%'><input class="action-closet button-default__2020 button-blue__2020" type="submit" value="Place in Closet"></li>
  208.            <li style='width:12.5%'><input class="action-stock button-default__2020 button-blue__2020" type="submit" value="Stock in Shop"></li>
  209.            <li style='width:12.5%'><input class="action-gallery button-default__2020 button-blue__2020" type="submit" value="Place in Gallery"></li>
  210.            <li style='width:12.5%'><input class="action-shed button-default__2020 button-blue__2020" type="submit" value="Place in Shed"></li>
  211.            <li style='width:12.5%'><input class="action-chamber button-default__2020 button-blue__2020" type="submit" value="Place in Chamber"></li>
  212.            <li style='width:12.5%'><input class="action-donate button-default__2020 button-red__2020" type="submit" value="Donate"></li>
  213.            <li style='width:12.5%'><input class="action-discard button-default__2020 button-red__2020" type="submit" value="Discard"></li>
  214.        </ul>
  215.        <ul class="inv-menulinks mobile" style="margin-bottom: 0px !important; background: rgba(0,0,0,0);">
  216.            <li style='width:10%'><input class="action-deposit button-default__2020 button-blue__2020" type="submit" value="SDB"></li>
  217.            <li style='width:10%'><input class="action-closet button-default__2020 button-blue__2020" type="submit" value="Closet"></li>
  218.            <li style='width:10%'><input class="action-stock button-default__2020 button-blue__2020" type="submit" value="Shop"></li>
  219.            <li style='width:10%'><input class="action-gallery button-default__2020 button-blue__2020" type="submit" value="Gallery"></li>
  220.            <li style='width:10%'><input class="action-shed button-default__2020 button-blue__2020" type="submit" value="Shed"></li>
  221.            <li style='width:10%'><input class="action-chamber button-default__2020 button-blue__2020" type="submit" value="Style"></li>
  222.            <li style='width:10%'><input class="action-donate button-default__2020 button-red__2020" type="submit" value="Donate"></li>
  223.            <li style='width:10%'><input class="action-discard button-default__2020 button-red__2020" type="submit" value="Discard"></li>
  224.        </ul>
  225.        </div>`);
  226.        $(".action-stock").attr("onclick", `quickStockAction("stock")`);
  227.        $(".action-deposit").attr("onclick", `quickStockAction("deposit")`);
  228.        $(".action-gallery").attr("onclick", `quickStockAction("gallery")`);
  229.        $(".action-closet").attr("onclick", `quickStockAction("closet")`);
  230.        $(".action-shed").attr("onclick", `quickStockAction("shed")`);
  231.        $(".action-chamber").attr("onclick", `quickStockAction("chamber")`);
  232.        $(".action-donate").attr("onclick", `discardConfirm("donate")`);
  233.        $(".action-discard").attr("onclick", `discardConfirm("discard")`);
  234.        $("#bulkSelectCheck").prop("checked", bulkSelect);
  235.    };
  236.    const toggleQuickStock = () => {
  237.        if (!inventorySetup()) {
  238.            return;
  239.        }
  240.        quickstocking = !quickstocking;
  241.        if (quickstocking) {
  242.            startQuickStock();
  243.        }
  244.        else {
  245.            resetQuickStock();
  246.        }
  247.    };
  248.    const _toggleInvFilter2 = toggleInvFilter2;
  249.    toggleInvFilter2 = (filterid, filtervalue) => {
  250.        resetQuickStock();
  251.        _toggleInvFilter2(filterid, filtervalue);
  252.        const data = document.getElementById("invDisplay").dataset;
  253.        displaySettings = {type: data.type, filter: data.filter, action: data.action, stack: data.stack};
  254.        GM_setValue("displaySettings", displaySettings);
  255.        console.log(JSON.stringify(displaySettings));
  256.    };
  257.    const observer = new MutationObserver(() => {
  258.        observer.disconnect();
  259.        if (displaySettings.stack != document.getElementById("invDisplay").dataset.stack) {
  260.            console.log("Swapping to stored stack preference");
  261.            _toggleInvFilter2(4);
  262.        }
  263.    });
  264.    observer.observe(document.querySelector("#invDisplay"), {childList: true, subtree: true});
  265.    $("#toggleqs").click(toggleQuickStock);
  266.    unsafeWindow.selectItem = selectItem; // page needs access to these
  267.    unsafeWindow.quickStockAction = quickStockAction;
  268.    unsafeWindow.discardConfirm = discardConfirm;
  269.    unsafeWindow.updateBulkSelect = updateBulkSelect;
  270.    const style = document.createElement("style");
  271.    style.innerHTML = `
  272.    #toggleqs {
  273.        width: 150px;
  274.        position: absolute;
  275.        left: 3%;
  276.        top: 5px;
  277.    }
  278.    @media screen and (max-width:950px) {
  279.        #toggleqs {
  280.            position: relative;
  281.            margin: auto;
  282.            left: unset;
  283.        }
  284.        .buttonpad {
  285.            padding: 10px;
  286.        }
  287.    }`;
  288.    document.head.appendChild(style);
  289. })();
Advertisement
Add Comment
Please, Sign In to add comment