// Extract content from page (injected function) - waits for JS-rendered content function extractPageContent() { return new Promise((resolve) => { function doExtract() { let content = ''; let title = document.title; let image = ''; // First: check og:description — on JS-rendered sites this often contains // the full article text server-side rendered (e.g. spa.gov.sa, wam.ae) // Only use if it's substantial (>300 chars) to avoid short page descriptions const ogDesc = document.querySelector('meta[property="og:description"]'); if (ogDesc && ogDesc.content && ogDesc.content.trim().length > 300) { content = ogDesc.content.trim(); } // Try multiple DOM selectors in order of specificity if (!content || content.trim().length < 300) { const contentSelectors = [ 'article', '[itemprop="articleBody"]', '.article-body', '.post-content', '.entry-content', '.content-body', '.article-content', '#article-body', 'main article', 'main', '[role="main"]', '.content', '#content', '.main-content', '#main-content' ]; for (const selector of contentSelectors) { const element = document.querySelector(selector); if (element && element.innerText && element.innerText.trim().length > 100) { content = element.innerText; break; } } } // Collect all
tags (works well for standard sites)
if (!content || content.trim().length < 100) {
const paragraphs = document.querySelectorAll('p');
const paragraphTexts = [];
for (const p of paragraphs) {
const text = p.innerText.trim();
if (text.length > 30) {
paragraphTexts.push(text);
}
}
if (paragraphTexts.length > 0) {
content = paragraphTexts.join('\n\n');
}
}
// Largest div fallback
if (!content || content.trim().length < 100) {
const allDivs = document.querySelectorAll('div, section');
let maxLength = 0;
let bestDiv = null;
for (const div of allDivs) {
const text = div.innerText;
if (text && text.length > maxLength && text.length > 200) {
const classList = div.className.toLowerCase();
if (!classList.includes('nav') && !classList.includes('menu') &&
!classList.includes('sidebar') && !classList.includes('footer') &&
!classList.includes('header') && !classList.includes('ad')) {
maxLength = text.length;
bestDiv = div;
}
}
}
if (bestDiv) content = bestDiv.innerText;
}
// Last resort: full body
if (!content || content.trim().length < 100) {
content = document.body.innerText;
}
// Image: og:image → twitter:image → article img → any large img
const ogImage = document.querySelector('meta[property="og:image"]');
if (ogImage && ogImage.content) {
image = ogImage.content;
}
if (!image) {
const twitterImage = document.querySelector('meta[name="twitter:image"]');
if (twitterImage && twitterImage.content) image = twitterImage.content;
}
if (!image) {
const articleImgSelectors = ['article img', '.article-body img', '.post-content img',
'.entry-content img', '.article-content img', 'main img', '.content img'];
for (const selector of articleImgSelectors) {
const img = document.querySelector(selector);
if (img && img.src && img.src.startsWith('http') && img.width > 200 && img.height > 100) {
image = img.src; break;
}
}
}
if (!image) {
const allImgs = document.querySelectorAll('img');
for (const img of allImgs) {
if (img.src && img.src.startsWith('http') && img.width > 300 && img.height > 150 &&
!img.src.includes('logo') && !img.src.includes('icon') &&
!img.src.includes('avatar') && !img.src.includes('sprite')) {
image = img.src; break;
}
}
}
// Title: h1 → og:title → document.title
const h1 = document.querySelector('h1');
if (h1 && h1.innerText.trim().length > 0) {
title = h1.innerText.trim();
} else {
const ogTitle = document.querySelector('meta[property="og:title"]');
if (ogTitle && ogTitle.content.trim().length > 0) title = ogTitle.content.trim();
}
return { title, content, image };
}
// If content is already there, return immediately
const quick = doExtract();
if (quick.content && quick.content.trim().length > 200) {
resolve(quick);
return;
}
// Otherwise wait up to 5 seconds for JS to render the page
let attempts = 0;
const maxAttempts = 10;
const interval = setInterval(() => {
attempts++;
const result = doExtract();
if (result.content && result.content.trim().length > 200 || attempts >= maxAttempts) {
clearInterval(interval);
resolve(result);
}
}, 500);
});
}
// Load current page info
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const currentTab = tabs[0];
document.getElementById('pageTitle').textContent = currentTab.title;
// Auto-detect target site based on content
chrome.scripting.executeScript({
target: { tabId: currentTab.id },
func: extractPageContent
}).then(async results => {
if (results && results[0]) {
const pageData = await results[0].result;
const detectedSite = detectTargetSite(pageData.title, pageData.content);
if (detectedSite) {
document.getElementById('targetSite').value = detectedSite;
setSectionModeForSite(detectedSite);
}
}
}).catch(err => {
console.log('Could not auto-detect site:', err);
});
});
// Set section mode based on target site
function setSectionModeForSite(site) {
let sectionMode = 'custom'; // default
if (site === 'dharab' || site === 'mhealthspot') {
sectionMode = 'custom';
} else if (site === 'vpnreports' || site === 'bestaitools') {
sectionMode = 'auto';
} else if (site === 'intomobile') {
sectionMode = 'none';
}
// Set the radio button
const radioBtn = document.querySelector(`input[name="sectionsMode"][value="${sectionMode}"]`);
if (radioBtn) {
radioBtn.checked = true;
toggleSectionsField(); // Update visibility
}
}
// Auto-detect target site based on content
function detectTargetSite(title, content) {
const text = (title + ' ' + content).toLowerCase();
// Check for keywords (order matters - most specific first)
// GCC countries = DHArab
const gccCountries = ['united arab emirates', 'uae', 'dubai', 'abu dhabi', 'qatar', 'doha',
'saudi arabia', 'riyadh', 'jeddah', 'kuwait', 'bahrain', 'oman',
'gcc', 'middle east', 'mena'];
if (gccCountries.some(keyword => text.includes(keyword))) {
return 'dharab';
}
// VPN/Security = VPN Reports
const vpnKeywords = ['vpn', 'virtual private network', 'privacy', 'security', 'encryption',
'cybersecurity', 'data protection', 'online security', 'internet privacy',
'proxy', 'firewall', 'malware', 'antivirus'];
if (vpnKeywords.some(keyword => text.includes(keyword))) {
return 'vpnreports';
}
// AI = BestAITools
const aiKeywords = ['artificial intelligence', ' ai ', 'machine learning', 'deep learning',
'neural network', 'chatgpt', 'gpt', 'claude', 'gemini', 'llm',
'generative ai', 'ai tool', 'ai model', 'openai', 'anthropic',
'palantir', 'mistral', 'cohere', 'ai startup'];
if (aiKeywords.some(keyword => text.includes(keyword))) {
return 'bestaitools';
}
// Mobile = IntoMobile
const mobileKeywords = ['smartphone', 'mobile', 'iphone', 'android', 'galaxy', 'pixel',
'ios', 'mobile app', 'tablet', 'ipad', '5g', 'cellular',
'mobile phone', 'samsung', 'apple', 'google phone'];
if (mobileKeywords.some(keyword => text.includes(keyword))) {
return 'intomobile';
}
// Healthcare = mHealthSpot
const healthKeywords = ['health', 'healthcare', 'medical', 'hospital', 'doctor', 'patient',
'medicine', 'clinical', 'diagnosis', 'treatment', 'disease',
'wellness', 'telehealth', 'telemedicine', 'mhealth', 'digital health',
'healthtech', 'pharma', 'pharmaceutical'];
if (healthKeywords.some(keyword => text.includes(keyword))) {
return 'mhealthspot';
}
// Default: no detection
return null;
}
// Toggle sections field visibility
function toggleSectionsField() {
const selected = document.querySelector('input[name="sectionsMode"]:checked').value;
const customField = document.getElementById('customSectionsField');
customField.style.display = selected === 'custom' ? 'block' : 'none';
}
document.querySelectorAll('input[name="sectionsMode"]').forEach(radio => {
radio.addEventListener('change', toggleSectionsField);
});
// Initialize
toggleSectionsField();
// Listen for target site changes to auto-adjust section mode
document.getElementById('targetSite').addEventListener('change', (e) => {
const selectedSite = e.target.value;
if (selectedSite) {
setSectionModeForSite(selectedSite);
}
});
// Settings link
document.getElementById('settingsLink').addEventListener('click', (e) => {
e.preventDefault();
chrome.runtime.openOptionsPage();
});
// Publish button
document.getElementById('publishBtn').addEventListener('click', async () => {
const targetSite = document.getElementById('targetSite').value;
const sectionsMode = document.querySelector('input[name="sectionsMode"]:checked').value;
const customSections = document.getElementById('articleSections').value;
const statusDiv = document.getElementById('status');
const publishBtn = document.getElementById('publishBtn');
// Validate target site selection
if (!targetSite) {
showStatus('Please select a target website', 'error');
return;
}
// Get API keys and settings from extension storage
const settings = await chrome.storage.sync.get(['anthropicKey', 'openaiKey', 'defaultStatus']);
const anthropicKey = settings.anthropicKey;
const openaiKey = settings.openaiKey;
const defaultStatus = settings.defaultStatus || 'publish';
if (!anthropicKey) {
showStatus('Please configure Anthropic API key in Settings', 'error');
return;
}
publishBtn.disabled = true;
publishBtn.textContent = '⏳ Publishing...';
showStatus('Extracting article content...', 'info');
// Get current tab
chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => {
const currentTab = tabs[0];
// Get WordPress site URL for the selected target
const wpSiteUrl = getWordPressSiteUrl(targetSite);
if (!wpSiteUrl) {
showStatus('Could not determine WordPress URL for selected site', 'error');
publishBtn.disabled = false;
publishBtn.textContent = 'Publish Article';
return;
}
// Extract content from page
try {
const results = await chrome.scripting.executeScript({
target: { tabId: currentTab.id },
func: extractPageContent
});
if (!results || !results[0]) {
showStatus('Failed to extract page content', 'error');
publishBtn.disabled = false;
publishBtn.textContent = 'Publish Article';
return;
}
// Await the promise returned by extractPageContent (waits for JS rendering)
const pageData = await results[0].result;
console.log('Article title:', pageData.title);
console.log('Content length:', pageData.content ? pageData.content.length : 0);
// Validate we actually have content before sending
if (!pageData.content || pageData.content.trim().length < 200) {
showStatus(
`Could not extract article content from this page (got ${pageData.content ? pageData.content.trim().length : 0} chars).
` +
`Please paste the article text manually below.`,
'error'
);
document.getElementById('pasteSection').style.display = 'block';
publishBtn.disabled = false;
publishBtn.textContent = 'Publish Article';
return;
}
// Prepare data for WordPress
const formData = new FormData();
formData.append('action', 'multisite_extension_publish');
formData.append('target_site', targetSite);
formData.append('anthropic_api_key', anthropicKey);
if (openaiKey) {
formData.append('openai_api_key', openaiKey);
}
formData.append('default_status', defaultStatus);
formData.append('source_url', currentTab.url);
formData.append('article_title', pageData.title);
formData.append('article_text', pageData.content);
formData.append('image_url', pageData.image || '');
formData.append('sections_mode', sectionsMode);
formData.append('custom_sections', customSections);
console.log('Publishing to:', wpSiteUrl);
console.log('Target site:', targetSite);
console.log('Section mode:', sectionsMode);
// Send to WordPress with retry logic
const maxRetries = 3;
let lastError = null;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
if (attempt === 1) {
showStatus('Sending to WordPress...', 'info');
} else {
showStatus(`Retrying... (attempt ${attempt} of ${maxRetries})`, 'info');
}
const response = await fetch(wpSiteUrl + '/wp-admin/admin-ajax.php', {
method: 'POST',
body: formData
});
console.log(`Attempt ${attempt} - Response status:`, response.status);
// Get raw text first so we can log it if JSON parse fails
const rawText = await response.text();
console.log(`Attempt ${attempt} - Raw response:`, rawText);
let result;
try {
// Strip anything before the first { in case WordPress leaked output
const jsonStart = rawText.indexOf('{');
if (jsonStart === -1) {
// No JSON at all - WordPress returned pure HTML (PHP error page)
lastError = 'WordPress returned an error: ' + rawText.replace(/<[^>]+>/g, '').substring(0, 150).trim();
console.error(`Attempt ${attempt} - No JSON in response:`, rawText);
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, 2000));
continue;
}
break;
}
const cleanText = jsonStart > 0 ? rawText.slice(jsonStart) : rawText;
result = JSON.parse(cleanText);
} catch (parseError) {
lastError = 'Failed to parse response: ' + rawText.substring(0, 150);
console.error(`Attempt ${attempt} - Parse error:`, rawText);
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, 2000));
continue;
}
break;
}
if (result.success) {
const viewLink = result.data.post_url ?
`
→ View Post` :
'';
showStatus(`✓ Published successfully to ${result.data.site}!${viewLink}`, 'success');
publishBtn.textContent = '✓ Published!';
if (result.data.image_debug) {
console.log('=== IMAGE DEBUG ===');
result.data.image_debug.forEach(line => console.log(line));
console.log('===================');
}
setTimeout(() => {
publishBtn.textContent = 'Publish Article';
publishBtn.disabled = false;
}, 3000);
return; // success - exit loop
} else {
// WordPress returned a proper error - don't retry, show it
const errorMsg = result.data || 'Unknown error';
console.error('WordPress error:', errorMsg);
showStatus('Error: ' + errorMsg, 'error');
publishBtn.disabled = false;
publishBtn.textContent = 'Publish Article';
return; // exit loop
}
} catch (fetchError) {
lastError = fetchError.message;
console.error(`Attempt ${attempt} - Fetch error:`, fetchError);
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, 2000)); // wait 2s before retry
}
}
}
// All retries exhausted
showStatus(`Error: ${lastError} — please try again.`, 'error');
publishBtn.disabled = false;
publishBtn.textContent = 'Publish Article';
} catch (error) {
console.error('Extension error:', error);
console.error('Error stack:', error.stack);
showStatus('Error: ' + error.message + '
Check browser console (F12) for details', 'error');
publishBtn.disabled = false;
publishBtn.textContent = 'Publish Article';
}
});
});
// Get WordPress site URL for target site
function getWordPressSiteUrl(targetSite) {
const sites = {
'mhealthspot': 'https://mhealthspot.com',
'dharab': 'https://dharab.com',
'intomobile': 'https://intomobile.com',
'vpnreports': 'https://www.vpnreports.com',
'bestaitools': 'https://www.bestaitools.com'
};
return sites[targetSite] || null;
}
function showStatus(message, type) {
const statusDiv = document.getElementById('status');
statusDiv.innerHTML = message;
statusDiv.className = type;
statusDiv.style.display = 'block';
}
// Handle publish with manually pasted content
document.getElementById('publishWithPasteBtn').addEventListener('click', async () => {
const manualContent = document.getElementById('manualContent').value.trim();
if (!manualContent || manualContent.length < 100) {
showStatus('Please paste some article content first.', 'error');
return;
}
const targetSite = document.getElementById('targetSite').value;
if (!targetSite) {
showStatus('Please select a target website.', 'error');
return;
}
const sectionsMode = document.querySelector('input[name="sectionsMode"]:checked').value;
const customSections = document.getElementById('articleSections').value;
const publishBtn = document.getElementById('publishWithPasteBtn');
const settings = await chrome.storage.sync.get(['anthropicKey', 'openaiKey', 'defaultStatus']);
const anthropicKey = settings.anthropicKey;
const openaiKey = settings.openaiKey;
const defaultStatus = settings.defaultStatus || 'publish';
if (!anthropicKey) {
showStatus('Please configure Anthropic API key in Settings.', 'error');
return;
}
const wpSiteUrl = getWordPressSiteUrl(targetSite);
if (!wpSiteUrl) {
showStatus('Could not determine WordPress URL for selected site.', 'error');
return;
}
publishBtn.disabled = true;
publishBtn.textContent = '⏳ Publishing...';
chrome.tabs.query({ active: true, currentWindow: true }, async (tabs) => {
const currentTab = tabs[0];
const formData = new FormData();
formData.append('action', 'multisite_extension_publish');
formData.append('target_site', targetSite);
formData.append('anthropic_api_key', anthropicKey);
if (openaiKey) formData.append('openai_api_key', openaiKey);
formData.append('default_status', defaultStatus);
formData.append('source_url', currentTab.url);
formData.append('article_title', document.getElementById('pageTitle').textContent);
formData.append('article_text', manualContent);
formData.append('image_url', '');
formData.append('sections_mode', sectionsMode);
formData.append('custom_sections', customSections);
try {
showStatus('Sending to WordPress...', 'info');
const response = await fetch(wpSiteUrl + '/wp-admin/admin-ajax.php', {
method: 'POST',
body: formData
});
const rawText = await response.text();
const jsonStart = rawText.indexOf('{');
const cleanText = jsonStart > 0 ? rawText.slice(jsonStart) : rawText;
const result = JSON.parse(cleanText);
if (result.success) {
const viewLink = result.data.post_url ?
`
→ View Post` : '';
showStatus(`✓ Published to ${result.data.site}!${viewLink}`, 'success');
publishBtn.textContent = '✓ Published!';
} else {
showStatus('Error: ' + (result.data || 'Unknown error'), 'error');
publishBtn.disabled = false;
publishBtn.textContent = 'Publish with Pasted Content';
}
} catch (err) {
showStatus('Error: ' + err.message, 'error');
publishBtn.disabled = false;
publishBtn.textContent = 'Publish with Pasted Content';
}
});
});