Got the progress indicator working.
This commit is contained in:
parent
a2e9e5eef1
commit
21fdd18e07
File diff suppressed because it is too large
Load diff
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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
83
shared-ui.js
Normal 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
|
||||
};
|
||||
Loading…
Reference in a new issue