// Amazon Product Bar Extension - Content Script // This script runs on Amazon search result pages (function() { 'use strict'; console.log('Amazon Product Bar Extension loaded'); /** * Checks if the current URL is an Amazon search results page * @param {string} url - The URL to check * @returns {boolean} - True if it's a search results page */ function isSearchResultsPage(url) { // Check for Amazon search patterns: /s? or /s/ or search in URL return url.includes('/s?') || url.includes('/s/') || url.includes('field-keywords') || url.includes('k='); } /** * Finds all product cards within a container element * @param {Element} container - The container to search within * @returns {Element[]} - Array of product card elements */ function findAllProductCards(container) { // Try multiple selectors for Amazon product cards let productCards = container.querySelectorAll('[data-component-type="s-search-result"]'); // Fallback selectors if the main one doesn't work if (productCards.length === 0) { productCards = container.querySelectorAll('[data-asin]:not([data-asin=""])'); } if (productCards.length === 0) { productCards = container.querySelectorAll('.s-result-item'); } return Array.from(productCards); } /** * Finds the image container within a product card * @param {Element} productCard - The product card element * @returns {Element|null} - The image container element or null if not found */ function findImageContainer(productCard) { // Try multiple selectors for image containers let imageContainer = productCard.querySelector('.s-image'); if (!imageContainer) { imageContainer = productCard.querySelector('.a-link-normal img'); if (imageContainer) { imageContainer = imageContainer.parentElement; } } if (!imageContainer) { imageContainer = productCard.querySelector('img[data-image-latency]'); if (imageContainer) { imageContainer = imageContainer.parentElement; } } if (!imageContainer) { const imgElement = productCard.querySelector('img'); if (imgElement) { imageContainer = imgElement.parentElement; } } return imageContainer; } /** * Checks if a product card already has a bar injected * @param {Element} productCard - The product card element to check * @returns {boolean} - True if the product card already has a bar */ function hasBar(productCard) { // Check if the product card has been processed by looking for our marker attribute return productCard.hasAttribute('data-ext-processed') || productCard.querySelector('.amazon-ext-product-bar') !== null; } /** * Injects a product bar below the image container * @param {Element} imageContainer - The image container element */ function injectBar(imageContainer) { // Get the parent product card to check for duplicates const productCard = imageContainer.closest('[data-component-type="s-search-result"]') || imageContainer.closest('[data-asin]') || imageContainer.closest('.s-result-item'); // Skip if no product card found or bar already exists if (!productCard || hasBar(productCard)) { return; } // Create the product bar element const productBar = document.createElement('div'); productBar.className = 'amazon-ext-product-bar'; productBar.setAttribute('data-ext-processed', 'true'); productBar.textContent = '🔥 Product Bar Active'; // Temporary content to make it visible // Insert the bar after the image container if (imageContainer.nextSibling) { imageContainer.parentNode.insertBefore(productBar, imageContainer.nextSibling); } else { imageContainer.parentNode.appendChild(productBar); } // Mark the product card as processed to prevent duplicates productCard.setAttribute('data-ext-processed', 'true'); console.log('Product bar injected for product card'); } /** * Processes product cards in a given container * @param {Element} container - The container to process */ function processProductCards(container) { const productCards = findAllProductCards(container); console.log(`Found ${productCards.length} product cards to process`); productCards.forEach(productCard => { // Skip if already processed if (hasBar(productCard)) { return; } const imageContainer = findImageContainer(productCard); if (imageContainer) { injectBar(imageContainer); } else { console.warn('No image container found for product card'); } }); } /** * Creates and configures a MutationObserver for dynamic content * @returns {MutationObserver} - The configured observer */ function createDOMObserver() { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { // Only process added nodes if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { mutation.addedNodes.forEach((node) => { // Only process element nodes if (node.nodeType === Node.ELEMENT_NODE) { // Check if the added node is a product card itself if (node.matches && (node.matches('[data-component-type="s-search-result"]') || node.matches('[data-asin]') || node.matches('.s-result-item'))) { const imageContainer = findImageContainer(node); if (imageContainer) { injectBar(imageContainer); } } else { // Check if the added node contains product cards processProductCards(node); } } }); } }); }); return observer; } /** * Starts observing the DOM for changes */ function startObserving() { const observer = createDOMObserver(); // Observe the entire document body for changes // This will catch infinite scroll additions and other dynamic content observer.observe(document.body, { childList: true, subtree: true }); console.log('DOM observer started for dynamic content detection'); // Return observer for potential cleanup return observer; } // Initialize the extension function initialize() { // Check if we're on a search results page if (!isSearchResultsPage(window.location.href)) { console.log('Not a search results page, extension inactive'); return; } console.log('Initializing Amazon Product Bar Extension'); // Process existing product cards on page load processProductCards(document.body); // Start observing for dynamic content startObserving(); } // Wait for DOM to be ready, then initialize if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initialize); } else { // DOM is already ready initialize(); } })();