Neopets: Shop Condenser

Condenses your shop's view. Removes useless elements.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(У мене вже є менеджер скриптів, дайте мені встановити його!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Neopets: Shop Condenser
// @namespace    https://github.com/saahphire/NeopetsUserscripts
// @version      1.0.0
// @description  Condenses your shop's view. Removes useless elements.
// @author       saahphire
// @homepageURL  https://github.com/saahphire/NeopetsUserscripts
// @homepage     https://github.com/saahphire/NeopetsUserscripts
// @match        *://*.neopets.com/market.phtml?*type=*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=neopets.com
// @run-at       document-idle
// @license      Unlicense
// ==/UserScript==

/*
•:•.•:•.•:•:•:•:•:•:•:••:•.•:•.•:•:•:•:•:•:•:•:•.•:•.•:•:•:•:•:•:•:••:•.•:•.•:•.•:•:•:•:•:•:•:•:•.•:•:•.•:•.••:•.•:•.••:
........................................................................................................................
☆ ⠂⠄⠄⠂⠁⠁⠂⠄⠄⠂✦ ⠂⠄⠄⠂⠁⠁⠂⠄⠄⠂☆ ⠂⠄⠄⠂⠁⠁⠂⠄⠄⠂✦ ⠂⠄⠄⠂⠁⠁⠂⠄⠂⠄⠄⠂☆ ⠂⠄⠄⠂⠁⠁⠂⠄⠄⠂✦ ⠂⠄⠄⠂⠁⠁⠂⠄⠂⠄⠄⠂☆ ⠂⠄⠄⠂⠁⠁⠂⠄⠄⠂✦
    This script does the following:
    - Removes:
        - The big image at the top
        - The Marketplace is where you can...
        - Land : Neopia Central (or whatever you chose)
        - Cleaner Code, Better Browsing and A Safer Neopia!
        - (please do not use rude words or IFRAMES!)
        - (it costs 150 NP to open a shop)
        - Items that cost 0 Neopoints...
        - Shopkeeper's picture and greeting in the stock page
        - Item descriptions
        - Note: shops are strictly a place for selling...
        - Please clear your Sales History regularly to save space on the Neopets servers! :)
        - Only purchases of 1000 NP or greater will be displayed here.
        - Need some extra security? Add a PIN for use in this area!
        - (max 999,999)
    - Changes:
        - Moves "Jump to" links to their own div
        - Makes item images smaller
        - Item quantity and removal are now the same column

    You may also like (use GreasyFork or GitHub for each, not both):
        - No Top Links (removes the cluster of redundant links on top of every old layour page)
            https://github.com/saahphire/NeopetsUserscripts/tree/main
            https://greatest.deepsurf.us/en/scripts/565351-neopets-no-top-links

    ✦ ⌇ saahphire
☆ ⠂⠄⠄⠂⠁⠁⠂⠄⠄⠂✦ ⠂⠄⠄⠂⠁⠁⠂⠄⠄⠂☆ ⠂⠄⠄⠂⠁⠁⠂⠄⠄⠂✦ ⠂⠄⠄⠂⠁⠁⠂⠄⠂⠄⠄⠂☆ ⠂⠄⠄⠂⠁⠁⠂⠄⠄⠂✦ ⠂⠄⠄⠂⠁⠁⠂⠄⠂⠄⠄⠂☆ ⠂⠄⠄⠂⠁⠁⠂⠄⠄⠂✦
........................................................................................................................
•:•.•:•.•:•:•:•:•:•:•:••:•.•:•.•:•:•:•:•:•:•:•:•.•:•.•:•:•:•:•:•:•:••:•.•:•.•:•.•:•:•:•:•:•:•:•:•.•:•:•.•:•.••:•.•:•.••:
*/

const jumpToLinks = [
    ['https://www.neopets.com/browseshop.phtml?owner=saahphire', 'Shop Front'],
    ['https://www.neopets.com/market.phtml?type=edit', 'Edit Shop'],
    ['https://www.neopets.com/market.phtml?type=your', 'Stock'],
    ['https://www.neopets.com/market.phtml?type=till', 'Till'],
    ['https://www.neopets.com/market.phtml?type=sales', 'Sales History'],
    ['https://www.neopets.com/quickstock.phtml', 'Quick Stock'],
    ['https://www.neopets.com/gallery/index.phtml', 'Gallery']
];

const remove = [
    'br + b + p:has(br)',
    'table table:has(.contentModule)',
    'center + p',
    'center:has([name="keeperimage"])',
    '.saahphire-stocked + b',
    'form + table:has(a[href="/pin_prefs.phtml"])',
    'font[size="1"]'
];

const shopLetters = [
    ['a', 'n', '0'],
    ['b', 'o', '1'],
    ['c', 'p', '2'],
    ['d', 'q', '3'],
    ['e', 'r', '4'],
    ['f', 's', '5'],
    ['g', 't', '6'],
    ['h', 'u', '7'],
    ['i', 'v', '8'],
    ['j', 'w', '9'],
    ['k', 'x', '_'],
    ['l', 'y'],
    ['m', 'z']
];

const usernameLetter = document.querySelector('.user a').textContent.charAt(0);
const swLetters = shopLetters.find(letterCluster => letterCluster.find(letter => letter === usernameLetter));

const buildJumpTo = () => {
    const ul = document.createElement('ul');
    ul.classList.add('saahphire-jump-to');
    ul.ariaLabel = 'Shop Navigation';
    document.querySelector('table table:has(.contentModule)').insertAdjacentElement('beforeBegin', ul);
    jumpToLinks.forEach(link => {
        const li = document.createElement('li');
        ul.appendChild(li);
        const a = document.createElement('a');
        li.appendChild(a);
        a.href = link[0];
        a.textContent = link[1];
    });
}

const buildStock = () => {
    const siblings = document.getElementsByName('subbyprev');
    if(!siblings.length) return;
    const bs = document.querySelectorAll('center:has([name="keeperimage"]) b');
    siblings.forEach(sibling => sibling.insertAdjacentHTML('afterend', `<p class="saahphire-stocked">Items Stocked: <strong>${bs[1].textContent}</strong> | Free Space: <strong>${bs[2].textContent}</strong>`));
}

const unanchorPage = () => {
    const currentPageStart = document.querySelector('input + b')?.textContent.match(/Viewing items (\d+)/)?.[1];
    if(!currentPageStart) return;
    ['.saahphire-jump-to ~ p a', 'form[action="process_market.phtml"] + center a'].forEach(anchorQuery => {
        const currentPageAnchor = [...document.querySelectorAll(anchorQuery)].find(a => a.textContent.startsWith(`[${currentPageStart}-`));
        currentPageAnchor.insertAdjacentHTML('afterend', `<span class="saahphire-page">${currentPageAnchor.textContent}</span>`);
        currentPageAnchor.remove();
    });
}

const moveStockInputs = form => {
    [...form.childNodes].filter(node => node.nodeType === 3).forEach(node => node.remove());
    const centerDiv = document.createElement('div');
    const rightDiv = document.createElement('div');
    const invisibleDiv = document.createElement('div');
    if(form.querySelector('input[value="Find"]')) {
        rightDiv.appendChild(document.getElementsByName('obj_name')[0]);
        rightDiv.appendChild(form.querySelector('input[value="Find"]'));
    }
    [...form.children].forEach(child => centerDiv.appendChild(child));
    [invisibleDiv, centerDiv, rightDiv].forEach(div => form.appendChild(div));
}

const removeDescriptions = () => {
    const column = [...document.querySelectorAll('[name="view"] + table > tbody > tr:first-child td')].findIndex(child => child.textContent === 'Description');
    document.querySelectorAll(`[name="view"] + table > tbody > tr td:nth-of-type(${column + 1})`).forEach(description => description.style.display = 'none');
}

const addMaxToRemove = () => {
    document.querySelectorAll('[name="view"] + table > tbody > tr').forEach(row => {
        const quantity = row.querySelector('td[width="50"]');
        if(!quantity) return;
        [...row.getElementsByTagName('option')].forEach(option => option.textContent += `/${quantity.textContent}`);
        row.querySelector('td[width="50"]').style.display = 'none';
    });
    [...document.querySelectorAll('[name="view"] + table > tbody > tr:first-child td')].find(td => td.textContent === 'Stock')?.remove();
    document.querySelector('[value="Update"]')?.addEventListener('click', () => document.querySelectorAll('[name="view"] + table > tbody > tr').forEach(row => [...row.getElementsByTagName('option')].forEach(option => option.textContent = option.value)));
}

const addPinToLastRow = () => {
    const parent = document.querySelector('[name="view"] + table > tbody > tr:last-child td');
    if(!parent) return;
    const div = document.createElement('div');
    parent.appendChild(div);
    const pinRow = document.querySelector('[name="view"] + table tr:has(table)');
    div.insertAdjacentHTML('beforeend', '<span class="saahphire-pin">Enter your <a href="https://www.neopets.com/pin_prefs.phtml">PIN</a>:</span>');
    div.appendChild(pinRow.querySelector('input'));
    pinRow.remove();
}

const css = `<style>
p, ul {
    margin: 0.25em;
}

#search-form, #ssw-tabs-1 {
    height: auto;
}

#search-form p {
    margin: 0;
}

#results_table tr:not(:first-child, :nth-child(2), :has(a[href*="owner=${swLetters.join('"], a[href*="owner=')}"])) {
    display: none;
}

.saahphire-jump-to {
    padding: 0;
    text-align: center;
}

.saahphire-jump-to li {
    display: inline;

    &:not(:last-child)::after {
        content: "|";
        margin: 0.25em;
        cursor: auto;
        pointer-events: none;
    }
}

.saahphire-stocked {
    text-align: center;
    display: inline;
    margin: 0 1em;
}

.saahphire-page {
    font-weight: 600;
    margin: 0.3em;
}

form[action="market.phtml"] {
    display: grid;
    grid-template-columns: 1fr 3fr 1fr;

    & div {
        text-align: center;

        &:last-child {
            text-align: right;
        }
    }
}

[name="view"] + table .search-helper, [name="view"] + table, [name="shopform"] table, textarea {
    width: 100%;
}

[name="view"] + table > tbody {
    display: grid;
    grid-template-columns: subgrid;
}

[name="view"] + table td {
    align-items: center;
    justify-content: center;
    display: flex;
    padding: 0.25em;
    flex-wrap: wrap;
    text-align: center;
    height: 100%;
    width: auto!important;
    background-color: transparent!important;
    box-sizing: border-box;
}

[name="view"] + table :not(.search-helper) img {
    width: 40px;
    height: 40px;
}

[name="view"] + table tr {
    display: grid;
    grid-template-columns: 3fr repeat(var(--columns), 1fr);
    padding: 0;
    background-color: #ffffcc;
}

[name="view"] + table tr:nth-child(2n) {
    background-color: white;
}

[name="view"] + table tr:is(:first-child, :last-child) {
    background-color: #dddd77;
    font-weight: 600;
}

[name="view"] + table td:first-child {
    justify-content: left;
    text-align: left;
}

[name="view"] + table tr:first-child td:first-child {
    text-align: center;
}

[name="view"] + table tr:last-child td {
    grid-column: 1 / -1;
}

[name="view"] + table tr:last-child td div {
    margin-left: auto;
}

br, tr:has(> td[bgcolor="#cccccc"]), .sf {
    display: none;
}
</style>`;

(function() {
    'use strict';
    document.head.insertAdjacentHTML('beforeend', css);
    buildJumpTo();
    unanchorPage();
    buildStock();
    document.querySelectorAll('form[action="market.phtml"]').forEach(form => moveStockInputs(form));
    removeDescriptions();
    addMaxToRemove();
    document.querySelector('[name="view"] + table')?.style.setProperty('--columns', document.querySelectorAll('[name="view"] + table > tbody > tr:first-child td').length - 2);
    addPinToLastRow();
    document.querySelectorAll(remove.join(', ')).forEach(rm => rm.remove());
    [...document.querySelectorAll('b + p[align="center"]')].find(p => p.textContent.startsWith('Only purchases'))?.remove();
})();