Got the progress indicator working.

This commit is contained in:
Lord_Devi 2024-11-21 14:03:45 -05:00
parent a2e9e5eef1
commit 21fdd18e07
4 changed files with 745 additions and 502 deletions

File diff suppressed because it is too large Load diff

View file

@ -16,11 +16,11 @@
"content_scripts": [
{
"matches": ["*://ads.google.com/*"],
"js": ["content-script.js"]
"js": ["shared-ui.js", "content-script.js"]
},
{
"matches": ["*://*.semrush.com/*"],
"js": ["semrush-content-script.js"]
"js": ["shared-ui.js", "semrush-content-script.js"]
}
],
"browser_action": {

View file

@ -26,53 +26,96 @@
// Helper function to get current page info
function getCurrentPageInfo() {
try {
const pageInput = document.querySelector('.sm-pagination__input input');
const totalPagesElement = document.querySelector('.___STotalPages_175b1-kmt_ span');
// Try multiple selectors for pagination elements
const pageInputSelectors = [
'.sm-pagination__input input',
'[aria-label="Current page"]',
'input[type="number"][aria-label*="page"]',
'.pagination input[type="number"]'
];
const totalPagesSelectors = [
'.___STotalPages_175b1-kmt_ span',
'.pagination__total',
'.pagination__pages-total',
'.sm-pagination__total'
];
// Find page input
let pageInput = null;
for (const selector of pageInputSelectors) {
pageInput = document.querySelector(selector);
if (pageInput) {
console.log(`Found page input using selector: ${selector}`);
break;
}
}
// Find total pages element
let totalPagesElement = null;
for (const selector of totalPagesSelectors) {
totalPagesElement = document.querySelector(selector);
if (totalPagesElement) {
console.log(`Found total pages using selector: ${selector}`);
break;
}
}
// Log the elements we found for debugging
console.log('Page Input Element found:', !!pageInput);
console.log('Total Pages Element found:', !!totalPagesElement);
if (!pageInput) {
// Try alternative selector for the page input
const altPageInput = document.querySelector('[aria-label="Current page"]');
if (altPageInput) {
console.log('Found page input using alternative selector');
pageInput = altPageInput;
} else {
console.log('Could not find page input element with either selector');
return null;
}
console.log('Could not find page input element with any selector');
return null;
}
// Get the current page value
const currentPage = parseInt(pageInput.value);
if (isNaN(currentPage)) {
console.log('Invalid current page value:', pageInput.value);
return null;
}
// If we can't find the total pages element, but we know we're on page 5
// and the next button is disabled, we can assume we're on the last page
if (!totalPagesElement && currentPage === 5) {
const nextButton = document.querySelector('.___SNextPage_175b1-kmt_');
if (nextButton && (nextButton.getAttribute('aria-disabled') === 'true' ||
nextButton.hasAttribute('disabled') ||
nextButton.classList.contains('__disabled_se82q-kmt_'))) {
console.log('On last page (page 5) with disabled next button');
return { currentPage: 5, totalPages: 5 };
let totalPages = null;
// Try to get total pages from element
if (totalPagesElement) {
const totalText = totalPagesElement.textContent.trim();
totalPages = parseInt(totalText.replace(/[^\d]/g, ''));
}
// If we can't find total pages but we're on page 5 with disabled next button
if (totalPages === null && currentPage === 5) {
const nextButtonSelectors = [
'.___SNextPage_175b1-kmt_',
'.sm-pagination__next',
'.pagination__next',
'button[aria-label*="next"]'
];
for (const selector of nextButtonSelectors) {
const nextButton = document.querySelector(selector);
if (nextButton && (
nextButton.getAttribute('aria-disabled') === 'true' ||
nextButton.hasAttribute('disabled') ||
nextButton.classList.contains('__disabled_se82q-kmt_')
)) {
console.log('On last page (page 5) with disabled next button');
totalPages = 5;
break;
}
}
}
const totalPages = totalPagesElement ?
parseInt(totalPagesElement.textContent.replace(/,/g, '')) :
(currentPage === 5 ? 5 : null);
if (totalPages === null) {
console.log('Could not determine total pages');
return null;
// If we still can't determine total pages but have current page
if (totalPages === null && currentPage > 0) {
console.log('Using current page as minimum total pages');
totalPages = currentPage;
}
// Debug the actual input field value
console.log(`Page Input Value: "${pageInput.value}", Parsed Current Page: ${currentPage}`);
console.log(`Current page: ${currentPage}, Total pages: ${totalPages}`);
return { currentPage, totalPages };
} catch (error) {
console.error('Error getting page info:', error);
return null;
@ -242,77 +285,78 @@
document.body.removeChild(textArea);
}
// Helper function to format keywords
function formatKeywords(keywords, includeHeaders) {
let csv = '';
if (includeHeaders) {
csv = 'Keyword,Search Volume\n';
}
csv += keywords.map(k => `${k.keyword},${k.volume}`).join('\n');
return csv;
}
// Main function to extract keywords
async function extractKeywords() {
const progressDialog = new KeywordGrabberUI.ProgressDialog().create();
try {
const options = window.keywordGrabberOptions || { includeHeaders: true };
const allKeywords = [];
const initialPageInfo = getCurrentPageInfo();
if (!initialPageInfo) {
throw new Error('Could not determine page information');
const pageInfo = getCurrentPageInfo();
if (!pageInfo) {
throw new Error('Could not get page information');
}
console.log(`Starting extraction from page ${initialPageInfo.currentPage} of ${initialPageInfo.totalPages}`);
let currentPage = initialPageInfo.currentPage;
const totalPages = initialPageInfo.totalPages;
do {
// Extract current page
progressDialog.updateProgress(`Starting extraction from page ${pageInfo.currentPage} of ${pageInfo.totalPages}`);
console.log(`Starting extraction from page ${pageInfo.currentPage} of ${pageInfo.totalPages}`);
let currentPage = pageInfo.currentPage;
const totalPages = pageInfo.totalPages;
while (currentPage <= totalPages) {
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} (Page ${currentPage} of ${totalPages})`);
if (currentPage >= totalPages) {
console.log('Reached last page');
break;
}
const success = await goToNextPage(currentPage, totalPages);
if (!success) {
console.log('Failed to navigate to next page');
allKeywords.push(...keywords);
progressDialog.updateProgress(`Total keywords collected: ${allKeywords.length} (Page ${currentPage} of ${totalPages})`);
console.log(`Total keywords collected: ${allKeywords.length} (Page ${currentPage} of ${totalPages})`);
if (currentPage < totalPages) {
progressDialog.updateProgress(`Navigating to page ${currentPage + 1}...`);
const success = await goToNextPage(currentPage, totalPages);
if (!success) {
throw new Error('Failed to navigate to next page');
}
currentPage++;
} else {
break;
}
currentPage++;
} while (true);
// Convert to CSV without quotes
let csv = '';
if (options.includeHeaders) {
csv = 'Keyword,Search Volume\n';
}
csv += allKeywords.map(k => `${k.keyword},${k.volume}`).join('\n');
// Format the keywords
const formattedKeywords = formatKeywords(allKeywords, options.includeHeaders);
// Copy to clipboard
copyToClipboard(csv);
copyToClipboard(formattedKeywords);
progressDialog.updateProgress(`Extraction complete. Total keywords: ${allKeywords.length}`);
console.log(`Extraction complete. Total keywords: ${allKeywords.length}`);
// Create notification
const div = document.createElement('div');
div.style.position = 'fixed';
div.style.top = '20px';
div.style.left = '50%';
div.style.transform = 'translateX(-50%)';
div.style.backgroundColor = '#4CAF50';
div.style.color = 'white';
div.style.padding = '15px';
div.style.borderRadius = '5px';
div.style.zIndex = '10000';
div.textContent = `${allKeywords.length} keywords copied to clipboard!`;
document.body.appendChild(div);
setTimeout(() => div.remove(), 3000);
// Give users time to see the completion message
await new Promise(resolve => setTimeout(resolve, 2000));
return allKeywords;
} catch (error) {
console.error('Error during keyword extraction:', error);
progressDialog.updateProgress(`Error: ${error.message}`);
// Keep error message visible for a moment
await new Promise(resolve => setTimeout(resolve, 3000));
throw error;
} finally {
progressDialog.remove();
}
}

83
shared-ui.js Normal file
View file

@ -0,0 +1,83 @@
// Shared UI components for keyword grabber extension
// Create and manage a progress dialog
class ProgressDialog {
constructor() {
this.dialog = null;
this.messageElement = null;
}
create() {
// Create dialog if it doesn't exist
if (!this.dialog) {
this.dialog = document.createElement('div');
this.dialog.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: white;
border: 1px solid #ccc;
border-radius: 4px;
padding: 15px 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
z-index: 10000;
font-family: Arial, sans-serif;
min-width: 200px;
`;
// Add progress message
this.messageElement = document.createElement('div');
this.messageElement.style.cssText = `
margin-bottom: 10px;
color: #333;
font-size: 14px;
`;
this.dialog.appendChild(this.messageElement);
// Add spinner
const spinner = document.createElement('div');
spinner.style.cssText = `
width: 20px;
height: 20px;
border: 2px solid #f3f3f3;
border-top: 2px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 10px auto;
`;
this.dialog.appendChild(spinner);
// Add spinner animation
const style = document.createElement('style');
style.textContent = `
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`;
document.head.appendChild(style);
document.body.appendChild(this.dialog);
}
return this;
}
updateProgress(message) {
if (this.messageElement) {
this.messageElement.textContent = message;
}
}
remove() {
if (this.dialog && this.dialog.parentNode) {
this.dialog.parentNode.removeChild(this.dialog);
this.dialog = null;
this.messageElement = null;
}
}
}
// Export for use in other scripts
window.KeywordGrabberUI = {
ProgressDialog
};