您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Enhances Amazon's sidebar by adding direct filters for items sold by Amazon.com and other Amazon-owned sellers.
// ==UserScript== // @name Amazon Filter: Sold by Amazon.com // @namespace https://github.com/sinazadeh/userscripts // @version 2.2.0 // @description Enhances Amazon's sidebar by adding direct filters for items sold by Amazon.com and other Amazon-owned sellers. // @author TheSina // @match *://www.amazon.com/s* // @match *://www.amazon.com/*/b/* // @match *://www.amazon.com/b/* // @grant none // @run-at document-end // @license MIT // ==/UserScript== /* jshint esversion: 11 */ (function () { 'use strict'; // --- Configuration --- const ID_MAP = { Amazon: 'A2Q1LRYTXHYQ2K', 'Amazon.com': 'ATVPDKIKX0DER', 'Amazon Resale': 'A2L77EE7U53NWQ', 'Amazon.com Services LLC': 'A3ODHND3J0WMC8', }; const REVERSE_ID_MAP = Object.fromEntries( Object.entries(ID_MAP).map(([key, value]) => [value, key]), ); const OPTIONS = Object.keys(ID_MAP); // --- State Management --- const state = { selectedFilters: [], currentUrl: '', observer: null, uiInitialized: false, }; // --- Core Logic --- function applyFilters() { const baseUrl = isOnCategoryPage() ? buildSearchUrlFromCategory() : location.href; const url = new URL(baseUrl); const params = url.searchParams; const otherRhParams = (params.get('rh') || '') .split(',') .filter(p => p && !p.startsWith('p_6:')); if (state.selectedFilters.length > 0) { const sellerIds = state.selectedFilters .map(label => ID_MAP[label]) .join('%7C'); otherRhParams.push(`p_6:${sellerIds}`); params.set('rh', otherRhParams.join(',')); } else { if (otherRhParams.length > 0) { params.set('rh', otherRhParams.join(',')); } else { params.delete('rh'); } } if (url.href !== location.href) { window.location.href = url.href; } } function initializeStateFromURL() { const url = new URL(location.href); const rh = url.searchParams.get('rh') || ''; const sellerFilter = rh.split(',').find(p => p.startsWith('p_6:')); if (sellerFilter) { const sellerIds = sellerFilter.substring(4).split(/\||%7C/); state.selectedFilters = sellerIds .map(id => REVERSE_ID_MAP[id]) .filter(Boolean); } else { state.selectedFilters = []; } state.currentUrl = location.href; state.uiInitialized = false; } // --- UI Creation --- function createUI() { if (state.uiInitialized) return; const sidebar = findSidebar(); if (sidebar) { const filterContainer = sidebar.querySelector('#filter-p_6') || sidebar; buildNativeUI(filterContainer); } else if (isOnCategoryPage()) { createFloatingWidget(); } } function buildNativeUI(container) { OPTIONS.forEach(label => document.getElementById(`p_6/${ID_MAP[label]}`)?.remove(), ); const fragment = document.createDocumentFragment(); OPTIONS.slice().forEach(label => { fragment.appendChild(createCheckboxListItem(label)); }); container.prepend(fragment); const sellerBlock = container.closest('div[id*="p_6"]'); sellerBlock?.parentElement?.prepend(sellerBlock); updateCheckboxes(); state.uiInitialized = true; } function createFloatingWidget() { document.getElementById('amazon-seller-filter-widget')?.remove(); const widget = document.createElement('div'); widget.id = 'amazon-seller-filter-widget'; Object.assign(widget.style, { position: 'fixed', top: '80px', right: '20px', background: 'white', border: '1px solid #ddd', borderRadius: '4px', padding: '15px', boxShadow: '0 2px 8px rgba(0,0,0,0.15)', zIndex: '1001', fontFamily: 'Amazon Ember, Arial, sans-serif', fontSize: '14px', }); widget.innerHTML = `<h3 style="margin: 0 0 10px; font-size: 16px; font-weight: bold;">Filter by Seller</h3>`; const ul = document.createElement('ul'); ul.style.cssText = 'list-style: none; padding: 0; margin: 0;'; OPTIONS.forEach(label => ul.appendChild(createCheckboxListItem(label, true)), ); widget.appendChild(ul); document.body.appendChild(widget); updateCheckboxes(); state.uiInitialized = true; } function createCheckboxListItem(label, isWidget = false) { const li = document.createElement('li'); li.id = `p_6/${ID_MAP[label]}`; if (isWidget) { li.innerHTML = ` <label style="display: flex; align-items: center; cursor: pointer; margin: 8px 0;"> <input type="checkbox" data-label="${label}" style="margin-right: 8px;"> <span>${label}</span> </label>`; } else { li.className = 'a-list-item a-spacing-micro'; li.innerHTML = ` <a class="a-link-normal s-navigation-item" href="#"> <div class="a-checkbox a-checkbox-fancy s-navigation-checkbox aok-float-left"> <label><input type="checkbox" data-label="${label}"><i class="a-icon a-icon-checkbox"></i></label> </div> <span class="a-size-base a-color-base">${label}</span> </a>`; } const checkbox = li.querySelector('input[type=checkbox]'); const clickableArea = li.querySelector('a') || li.querySelector('label'); const handleChange = e => { e.preventDefault(); if (e.target !== checkbox) { checkbox.checked = !checkbox.checked; } const idx = state.selectedFilters.indexOf(label); if (checkbox.checked && idx === -1) { state.selectedFilters.push(label); } else if (!checkbox.checked && idx !== -1) { state.selectedFilters.splice(idx, 1); } applyFilters(); }; clickableArea.addEventListener('click', handleChange); return li; } function updateCheckboxes() { OPTIONS.forEach(label => { const checkbox = document.querySelector( `input[data-label="${label}"]`, ); if (checkbox) { checkbox.checked = state.selectedFilters.includes(label); } }); } // --- Page Data & Helpers --- /** * Correctly identifies category pages, including path /b and /b/ */ const isOnCategoryPage = () => location.pathname === '/b' || location.pathname.startsWith('/b/'); function findSidebar() { const selectors = [ '#s-refinements', '#leftNav', '#departments', 'div[data-csa-c-slot-id="left-nav"]', ]; return ( selectors.map(s => document.querySelector(s)).find(el => el) || null ); } /** * Builds a search URL from a category page, now with better data extraction. */ function buildSearchUrlFromCategory() { const {node, categoryName, searchAlias} = getCategoryPageData(); const params = new URLSearchParams(); // Prefer using the more specific search alias 'i' if found if (searchAlias) { params.set('i', searchAlias); } else { // Fallback to keyword 'k' if no alias is available params.set('k', categoryName); } // Always include the node in the 'rh' parameter if it exists if (node) { params.set('rh', `n:${node}`); } return `https://www.amazon.com/s?${params.toString()}`; } /** * Extracts page data, now including the search alias (e.g., 'i=tools'). */ function getCategoryPageData() { const node = new URL(location.href).searchParams.get('node'); let categoryName = ''; let searchAlias = ''; // Attempt to get the specific search-alias from the department dropdown const searchDropdown = document.getElementById('searchDropdownBox'); if (searchDropdown) { const selectedOption = searchDropdown.querySelector('option[selected]'); if (selectedOption) { const aliasValue = selectedOption.value; // e.g., "search-alias=tools" if (aliasValue.startsWith('search-alias=')) { searchAlias = aliasValue.split('=')[1]; } } } // Fallback logic to get a display name for the category const selectors = [ '.sg-col .a-color-state.a-text-bold', '#wayfinding-breadcrumbs_feature_div .a-link-normal:last-of-type', 'h1, .a-size-large.a-spacing-none.a-color-base', ]; categoryName = selectors .map(s => document .querySelector(s) ?.textContent.trim() .replace(/"/g, ''), ) .find(name => name) || ''; if (!categoryName) { const title = document.title; if (title.includes('Amazon.com')) { categoryName = title .split(' - ')[0] .replace('Amazon.com: ', '') .trim(); } } return {node, categoryName: categoryName || 'Category', searchAlias}; } // --- Initialization and Observation --- function handleCategoryPageRedirect() { // Only redirect on category pages that LACK a standard filter sidebar. if (isOnCategoryPage() && !findSidebar()) { const searchUrl = buildSearchUrlFromCategory(); // Use replace to avoid polluting browser history with the redirect. window.location.replace(searchUrl); return true; // Indicates a redirect has started. } return false; } function reinitialize() { // If the page is a basic category page, redirect to a search page first. if (handleCategoryPageRedirect()) { return; // Stop execution to allow the redirect to happen. } document.getElementById('amazon-seller-filter-widget')?.remove(); initializeStateFromURL(); createUI(); } function setupMutationObserver() { if (state.observer) state.observer.disconnect(); const observerTarget = document.getElementById('a-page') || document.body; state.observer = new MutationObserver(mutations => { if (location.href !== state.currentUrl) { reinitialize(); return; } if (!state.uiInitialized) { for (const mutation of mutations) { if (mutation.addedNodes.length > 0) { createUI(); if (state.uiInitialized) break; } } } }); state.observer.observe(observerTarget, { childList: true, subtree: true, }); } // --- Script Entry Point --- if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { reinitialize(); setupMutationObserver(); }); } else { reinitialize(); setupMutationObserver(); } window.addEventListener('popstate', () => setTimeout(reinitialize, 100)); })();