diff --git a/content-script.js b/content-script.js index 7fad141..876b490 100644 --- a/content-script.js +++ b/content-script.js @@ -80,8 +80,9 @@ let lastRowCount = 0; let currentRowCount = getRowCount(); let attempts = 0; + let maxAttempts = 3; - while (lastRowCount !== currentRowCount && attempts < 5) { + while (lastRowCount !== currentRowCount && attempts < maxAttempts) { lastRowCount = currentRowCount; container.scrollTop = container.scrollHeight; await new Promise(resolve => setTimeout(resolve, 1000)); @@ -90,7 +91,8 @@ console.log(`Row loading attempt ${attempts}: ${currentRowCount} rows`); } - return true; + // If we have rows and they're stable (not changing), consider it successful + return currentRowCount > 0; } // Helper function to wait for content to load after navigation @@ -358,7 +360,15 @@ // Helper function to extract current page async function extractCurrentPage() { // Wait for rows to be loaded - await waitForRowsToLoad(); + if (!await waitForRowsToLoad()) { + console.log('Failed to load rows, but checking if we have any content'); + // Even if waitForRowsToLoad fails, check if we have any rows + const rows = document.querySelectorAll('.particle-table-row'); + if (rows.length === 0) { + createNotification('No keywords found on this page', 3000, true); + return null; + } + } // Find all keyword sections const rows = document.querySelectorAll('.particle-table-row'); @@ -410,8 +420,28 @@ } // Helper function to create notifications - function createNotification(message, duration = 3000, isError = false) { + function createNotification(message, duration = 3000, isError = false, id = null, remove = false) { + if (remove && id) { + const existingNotification = document.getElementById(id); + if (existingNotification) { + existingNotification.remove(); + } + return id; + } + + // If updating existing notification + if (id) { + const existingNotification = document.getElementById(id); + if (existingNotification) { + existingNotification.textContent = message; + return id; + } + } + + // Create new notification const notification = document.createElement('div'); + const notificationId = id || 'notification-' + Date.now(); + notification.id = notificationId; notification.style.cssText = ` position: fixed; top: 20px; @@ -421,7 +451,6 @@ color: white; border-radius: 4px; z-index: 10000; - font-family: Arial, sans-serif; box-shadow: 0 2px 5px rgba(0,0,0,0.2); max-width: 300px; word-wrap: break-word; @@ -430,10 +459,15 @@ document.body.appendChild(notification); if (duration > 0) { - setTimeout(() => notification.remove(), duration); + setTimeout(() => { + const notificationToRemove = document.getElementById(notificationId); + if (notificationToRemove) { + notificationToRemove.remove(); + } + }, duration); } - return notification; + return notificationId; } // Helper function to set show rows to 500 @@ -595,7 +629,7 @@ let lastRowCount = 0; let sameCountIterations = 0; - let maxAttempts = 10; + let maxAttempts = 5; let attempts = 0; while (attempts < maxAttempts) { @@ -604,7 +638,7 @@ if (currentRowCount === lastRowCount) { sameCountIterations++; - if (sameCountIterations >= 2) { + if (sameCountIterations >= 2 && currentRowCount > 0) { console.log('Row loading complete. Total rows:', currentRowCount); return true; } @@ -623,7 +657,14 @@ attempts++; } - console.log('Could not load all rows after maximum attempts'); + // If we have rows but didn't meet the stability criteria, still consider it a success + const finalRowCount = getRowCount(); + if (finalRowCount > 0) { + console.log('Row loading completed with', finalRowCount, 'rows'); + return true; + } + + console.log('Could not load any rows after maximum attempts'); return false; } @@ -649,133 +690,120 @@ return paginationText; } + // Helper function to format keywords into CSV format + function formatKeywords(keywords, includeHeaders) { + let csvContent = ''; + if (includeHeaders) { + csvContent = 'Keyword,Search Volume,Source\n'; + } + csvContent += keywords.map(k => `${k.keyword},${k.volume},${k.source}`).join('\n'); + return csvContent; + } + // Main function to extract keywords async function extractKeywords() { - if (isExtracting) { - console.log('Extraction already in progress, skipping...'); - return; - } - - isExtracting = true; - const progressDialog = new KeywordGrabberUI.ProgressDialog().create(); - - try { - progressDialog.updateProgress('Starting extraction...'); - const processedKeywords = new Map(); - - // Initial setup - await waitForContent(); - - // Set rows to 500 - progressDialog.updateProgress('Setting rows to 500...'); - await setShowRows500(); - await waitForContentLoad(); - - // Get total pages - const initialPageInfo = getCurrentPageInfo(); - const totalPages = initialPageInfo && initialPageInfo.total ? - Math.ceil(initialPageInfo.total / initialPageInfo.perPage) : 0; - - let pageCount = 1; - - do { - progressDialog.updateProgress(`Processing page ${pageCount}${totalPages ? ` of ${totalPages}` : ''}...`); - - // Load rows for current page - await loadAllRows(); - - // Extract keywords from current page - const currentPageKeywords = await extractCurrentPage(); - if (!currentPageKeywords || currentPageKeywords.length === 0) { - console.log('No keywords found on current page'); - break; - } - - console.log(`Found ${currentPageKeywords.length} keywords on page ${pageCount}`); - - // Add to processed set - currentPageKeywords.forEach(({ keyword, volume, source }) => { - if (!processedKeywords.has(keyword)) { - processedKeywords.set(keyword, { volume, source }); - } - }); - - progressDialog.updateProgress(`Found ${processedKeywords.size} keywords...`); - - // Check if we're on the last page - if (isLastPage()) { - console.log('On last page, finished pagination'); - break; - } - - // Get current page info before moving - const currentStart = getCurrentPageInfo()?.start; - if (!currentStart) { - console.log('Could not get current page info'); - break; - } - - // Move to next page - progressDialog.updateProgress('Moving to next page...'); - const nextButton = findNextButton(); - if (!nextButton) { - console.log('Could not find next button'); - break; - } - - nextButton.click(); - - // Wait for page update - const updated = await waitForPageUpdate(currentStart); - if (!updated) { - console.log('Page did not update after clicking next'); - break; - } - - // Wait for content to load on new page - await waitForContentLoad(); - pageCount++; - - } while (pageCount <= totalPages || totalPages === 0); - - // Convert keywords to array and sort by volume - const keywordArray = Array.from(processedKeywords.entries()).map(([keyword, { volume, source }]) => ({ - keyword, - volume, - source - })); - - // Sort by volume (highest to lowest) - keywordArray.sort((a, b) => { - const volA = parseInt(a.volume.replace(/,/g, '')) || 0; - const volB = parseInt(b.volume.replace(/,/g, '')) || 0; - return volB - volA; - }); - - // Create CSV content - let csvContent = ''; - if (window.keywordGrabberOptions.includeHeaders) { - csvContent = 'Keyword,Search Volume,Source\n'; + if (isExtracting) { + console.log('Already extracting keywords'); + return; } - csvContent += keywordArray.map(k => `${k.keyword},${k.volume},${k.source}`).join('\n'); - // Copy to clipboard - await navigator.clipboard.writeText(csvContent); - - console.log(`Total keywords processed: ${processedKeywords.size}`); - progressDialog.updateProgress(`Successfully copied ${processedKeywords.size} keywords!`); - await new Promise(resolve => setTimeout(resolve, 2000)); - - } catch (error) { - console.error('Error:', error); - progressDialog.updateProgress(`Error: ${error.message}`); - await new Promise(resolve => setTimeout(resolve, 3000)); - throw error; - } finally { - progressDialog.remove(); - isExtracting = false; - hasStarted = false; - } + isExtracting = true; + let progressNotificationId = null; + try { + // Initial setup + await waitForContent(); + + // Get initial page info to check total keywords + const initialPageInfo = getCurrentPageInfo(); + if (!initialPageInfo) { + throw new Error('Could not get page information'); + } + + console.log(`Total keywords found: ${initialPageInfo.total}`); + + // Only modify show rows if we have more than 100 keywords + if (initialPageInfo.total > 100) { + console.log('More than 100 keywords found, setting show rows to 500'); + if (!await setShowRows500()) { + throw new Error('Failed to set show rows to 500'); + } + await waitForContent(); + } else { + console.log('Less than 100 keywords found, skipping show rows modification'); + } + + // Get updated page info after potential show rows change + const pageInfo = getCurrentPageInfo(); + if (!pageInfo) { + throw new Error('Could not get page information'); + } + + console.log(`Starting extraction from page ${pageInfo.start} to ${pageInfo.end} of ${pageInfo.total}`); + + const allKeywords = []; + let currentPage = 1; + + // Create initial progress notification + progressNotificationId = createNotification(`Extracting keywords... (0/${pageInfo.total})`, 0, false, 'progress-notification'); + + do { + console.log(`Processing page ${currentPage}`); + + const keywords = await extractCurrentPage(); + if (!keywords) { + throw new Error('Failed to extract keywords from current page'); + } + + allKeywords.push(...keywords); + console.log(`Total keywords collected: ${allKeywords.length}`); + + // Update progress notification + createNotification(`Extracting keywords... (${allKeywords.length}/${pageInfo.total})`, 0, false, progressNotificationId); + + if (await isLastPage()) { + break; + } + + if (!await goToNextPage()) { + throw new Error('Failed to navigate to next page'); + } + + currentPage++; + + } while (true); + + if (allKeywords.length === 0) { + throw new Error('No keywords found'); + } + + // Format the keywords and copy to clipboard + const formattedKeywords = formatKeywords(allKeywords, window.keywordGrabberOptions?.includeHeaders ?? true); + await navigator.clipboard.writeText(formattedKeywords); + + console.log(`Successfully extracted ${allKeywords.length} keywords`); + + // Remove progress notification before showing success + createNotification('', 0, false, progressNotificationId, true); + createNotification(`Extracted ${allKeywords.length} keywords`); + + return allKeywords; + + } catch (error) { + console.error('Error during keyword extraction:', error); + // Remove progress notification before showing error + if (progressNotificationId) { + createNotification('', 0, false, progressNotificationId, true); + } + createNotification(error.message, 5000, true); + throw error; + } finally { + isExtracting = false; + hasStarted = false; + // Ensure progress notification is removed + if (progressNotificationId) { + createNotification('', 0, false, progressNotificationId, true); + } + } } // Since this is an Angular app, content might load dynamically @@ -802,14 +830,14 @@ const success = await loadAllRows(); if (success && hasStarted) { await extractKeywords(); - } else { + } else if (hasStarted) { // Only show error if we haven't already completed createNotification('Could not load all rows. Please try again.', 5000, true); hasStarted = false; } } else if (attempts < maxAttempts && hasStarted) { attempts++; setTimeout(tryFindContent, 1000); // Wait 1 second before trying again - } else { + } else if (hasStarted) { // Only show error if we haven't already completed createNotification('Could not find keyword content after multiple attempts. Please make sure you are on the correct page.', 5000, true); hasStarted = false; }