您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds buttons to augment the tcgplayer shopping cart experience.
// ==UserScript== // @name TCGPlayer Cart Buttons - Remove Duplicates, Save Expensive or Non-Direct For Later, and More! // @namespace http://tampermonkey.net/ // @version 2024-05-09 // @description Adds buttons to augment the tcgplayer shopping cart experience. // @author ganondorc // @match https://www.tcgplayer.com/cart* // @grant none // ==/UserScript== (function () { 'use strict'; // Default values const defaultMinCards = 5; const defaultMaxPrice = 3; function createButtonRow(buttonText, onClickHandler, testid) { const row = document.createElement('div'); row.className = 'button-row'; row.style.paddingBottom = '5px'; row.style.paddingTop = '5px'; const button = document.createElement('button'); button.className = 'tcg-button tcg-button--md tcg-standard-button tcg-standard-button--priority is-full-width checkout-btn'; button.type = 'button'; button.dataset.testid = testid; const buttonContent = document.createElement('span'); buttonContent.className = 'tcg-standard-button__content'; const spanText = document.createElement('span'); spanText.textContent = buttonText; buttonContent.appendChild(spanText); button.appendChild(buttonContent); button.addEventListener('click', onClickHandler); row.appendChild(button); return row; } function injectButtons() { const cartSummary = document.querySelector('section.cart-summary'); const savedForLaterActions = document.querySelector('section.saved-for-later-actions'); if (!cartSummary && !savedForLaterActions) { console.warn('Relevant sections not found.'); return; } // Inject into cart summary section if (cartSummary) { const checkOutButton = cartSummary.querySelector('.checkout-btn'); if (!checkOutButton) { console.warn('Checkout button not found.'); return; } // Create the button container let buttonContainer = cartSummary.querySelector('.button-container'); if (!buttonContainer) { buttonContainer = document.createElement('div'); buttonContainer.className = 'button-container'; cartSummary.insertBefore(buttonContainer, checkOutButton); } // Input for "save non-direct packages" if (!buttonContainer.querySelector('input[data-testid="inputMinCards"]')) { const inputRow = document.createElement('div'); inputRow.className = 'button-row'; inputRow.style.display = 'flex'; inputRow.style.justifyContent = 'center'; inputRow.style.flexDirection = 'column'; const label = document.createElement('label'); label.textContent = 'Min Cards'; label.style.marginBottom = '5px'; label.style.textAlign = 'center'; const input = document.createElement('input'); input.type = 'number'; input.placeholder = 'Min Cards'; input.min = '0'; input.value = defaultMinCards; input.className = 'min-cards-input'; input.style.height = '30px'; input.dataset.testid = 'inputMinCards'; inputRow.appendChild(label); inputRow.appendChild(input); buttonContainer.appendChild(inputRow); } // Save non-direct packages button if (!buttonContainer.querySelector('button[data-testid="btnSaveNonDirectPackages"]')) { const savePackagesButtonRow = createButtonRow( 'Save Non-Direct Packages', () => { const input = buttonContainer.querySelector('input[data-testid="inputMinCards"]'); const minCards = parseInt(input.value); saveNonDirectPackages(minCards); }, 'btnSaveNonDirectPackages' ); buttonContainer.appendChild(savePackagesButtonRow); } if (!buttonContainer.querySelector('button[data-testid="btnSaveExpensiveItems"]')) { // Create the input field row const inputRow = document.createElement('div'); inputRow.className = 'button-row'; inputRow.style.display = 'flex'; inputRow.style.justifyContent = 'center'; inputRow.style.flexDirection = 'column'; const label = document.createElement('label'); label.textContent = 'Max Price'; label.style.marginBottom = '5px'; label.style.textAlign = 'center'; const input = document.createElement('input'); input.type = 'number'; input.placeholder = 'Max Price'; input.step = '0.01'; input.min = '0'; input.value = defaultMaxPrice; input.className = 'max-price-input'; input.style.height = '30px'; inputRow.appendChild(label); inputRow.appendChild(input); buttonContainer.appendChild(inputRow); const saveExpensiveButtonRow = createButtonRow( 'Save Items Over Price', () => { const maxPrice = parseFloat(input.value); if (isNaN(maxPrice)) { window.alert('Please enter a valid maximum price.'); } else { saveExpensiveItems(maxPrice); } }, 'btnSaveExpensiveItems' ); buttonContainer.appendChild(saveExpensiveButtonRow); } if (!buttonContainer.querySelector('button[data-testid="btnRemoveDuplicateCards"]')) { const removeDuplicatesButtonRow = createButtonRow( 'Remove Duplicate Cards', removeDuplicateCards, 'btnRemoveDuplicateCards' ); buttonContainer.appendChild(removeDuplicatesButtonRow); } } // Inject into saved for later actions section if (savedForLaterActions) { if (!savedForLaterActions.querySelector('button[data-testid="btnActuallyAddAllToCart"]')) { const actuallyAddAllButtonRow = createButtonRow( 'Actually Add All To Cart', actuallyAddAllToCart, 'btnActuallyAddAllToCart' ); savedForLaterActions.appendChild(actuallyAddAllButtonRow); } } } function isDirectPackage(packageElement) { return !packageElement.classList.contains('non-direct-package'); } function saveItemsForPackage(packageElement) { const allItemsActions = packageElement.querySelector('.all-items-actions'); if (allItemsActions) { const buttons = allItemsActions.querySelectorAll('button'); const saveButton = Array.from(buttons).find(button => button.textContent.includes("Save items") ); if (saveButton) { console.log('Clicking Save items button for package:', packageElement); saveButton.click(); } else { console.warn('Save items button not found for package:', packageElement); } } else { console.warn('all-items-actions div not found for package:', packageElement); } } function saveNonDirectPackages(minCards = defaultMinCards) { const packages = document.querySelectorAll('section.tab-content.non-direct-package'); let savedPackagesCount = 0; packages.forEach(packageElement => { const items = packageElement.querySelectorAll('.package-item'); // Only save packages with at least the specified minimum number of items if (!isDirectPackage(packageElement) && items.length < minCards) { saveItemsForPackage(packageElement); savedPackagesCount++; } }); window.alert(`Saved items for ${savedPackagesCount} non-direct package(s) with under ${minCards} cards.`); } function saveExpensiveItems(maxPrice = defaultMaxPrice) { const stackedSections = document.querySelectorAll('section.stacked-content'); let savedItemsCount = 0; stackedSections.forEach(sectionElement => { const items = sectionElement.querySelectorAll('.package-item'); items.forEach(item => { const priceElement = item.querySelector('.price'); if (priceElement) { const priceText = priceElement.textContent.trim().replace('$', ''); const price = parseFloat(priceText); if (price > maxPrice) { const saveButton = item.querySelector('button.save-for-later'); if (saveButton) { saveButton.click(); savedItemsCount++; } else { console.warn('Save for later button not found for item:', item); } } } else { console.warn('Price element not found for item:', item); } }); }); window.alert(`Saved ${savedItemsCount} item(s) over $${maxPrice}.`); } function removeDuplicateCards() { const stackedSections = document.querySelectorAll('section.stacked-content'); const itemMap = new Map(); let removedItemsCount = 0; stackedSections.forEach(sectionElement => { const items = sectionElement.querySelectorAll('.package-item'); items.forEach(item => { const nameElement = item.querySelector('[data-testid="productName"]'); const priceElement = item.querySelector('.price'); if (!nameElement || !priceElement) { console.warn('Name or price element not found for item:', item); return; } const name = nameElement.textContent.trim(); const price = parseFloat(priceElement.textContent.trim().replace('$', '')); if (!itemMap.has(name)) { itemMap.set(name, [{ item, price }]); } else { itemMap.get(name).push({ item, price }); } }); }); itemMap.forEach(itemArray => { itemArray.sort((a, b) => a.price - b.price); const selectedItem = itemArray[0]; itemArray.forEach(({ item, price }, index) => { if (index > 0) { const removeButton = item.querySelector('button.remove'); if (removeButton) { removeButton.click(); removedItemsCount++; } } else { const quantitySelect = item.querySelector('select[aria-label*="cart quantity"]'); if (quantitySelect.value != '1') { console.warn("found a greater than 1 quantity: ", quantitySelect.value); // quantitySelect.value = "1"; // simulateMouseClick(quantitySelect); // const event = new Event('change', { bubbles: true }); // quantitySelect.dispatchEvent(event); } } }); }); window.alert(`Removed ${removedItemsCount} duplicate card(s).`); } function simulateMouseClick(targetNode) { function triggerMouseEvent(targetNode, eventType) { var clickEvent = document.createEvent('MouseEvents'); clickEvent.initEvent(eventType, true, true); targetNode.dispatchEvent(clickEvent); } ["mouseover", "mousedown", "mouseup", "click"].forEach(function(eventType) { triggerMouseEvent(targetNode, eventType); }); } function actuallyAddAllToCart() { const savedItems = document.querySelectorAll('section.saved-item'); let addedItemsCount = 0; savedItems.forEach(item => { const addToCartButton = item.querySelector('button[data-testid="saveForLaterAddToCart"]'); if (addToCartButton) { addToCartButton.click(); addedItemsCount++; } else { console.warn('Add to Cart button not found for saved item:', item); } }); window.alert(`Added ${addedItemsCount} item(s) to the cart.`); } function setupObserver() { const observer = new MutationObserver(() => { injectButtons(); }); observer.observe(document.body, { childList: true, subtree: true }); } window.addEventListener('load', () => { setupObserver(); injectButtons(); // Initial injection }); })();