document.addEventListener('DOMContentLoaded', () => { // DOM Elements const keywordsContainer = document.getElementById('keywords-container'); const refreshBtn = document.getElementById('refresh-btn'); const loading = document.getElementById('loading'); const selectedKeywordText = document.getElementById('selected-keyword-text'); const generatePromptBtn = document.getElementById('generate-prompt-btn'); const promptResult = document.getElementById('prompt-result'); const promptTitle = document.getElementById('prompt-title'); const promptBody = document.getElementById('prompt-body'); const copyPromptBtn = document.getElementById('copy-prompt-btn'); const copySuccess = document.getElementById('copy-success'); // State let selectedKeyword = null; let currentPrompt = null; // Fetch trending keywords with opportunity scoring const fetchTrendingKeywords = async () => { try { loading.classList.remove('hidden'); keywordsContainer.innerHTML = ''; // Step 1: Get trending topics from multiple sources const trendingTopics = await fetchTrendingTopics(); // Step 2: Enrich with search volume and result count data const enrichedKeywords = await enrichKeywordsWithOpportunityData(trendingTopics); // Step 3: Calculate opportunity score and sort const keywordsWithOpportunity = calculateOpportunityScore(enrichedKeywords); if (keywordsWithOpportunity.length > 0) { renderKeywords(keywordsWithOpportunity); } else { useBackupKeywords(); } } catch (error) { console.error('Error fetching trending keywords:', error); useBackupKeywords(); } finally { loading.classList.add('hidden'); } }; // Import Google Trends API functions import { fetchGoogleTrends, getTrendingDate } from './trends-api.js'; // Fetch trending topics from multiple sources for diversity const fetchTrendingTopics = async () => { try { let combinedTopics = []; // Source 1: Google Trends API console.log('Fetching trending topics from Google Trends...'); const trendingData = await fetchGoogleTrends(); if (trendingData && trendingData.length > 0) { combinedTopics = [...combinedTopics, ...trendingData]; } // Update trending date display updateTrendingDateDisplay(); // Source 2: Wikipedia trending articles (as backup) const wikiResponse = await fetch('https://wikimedia.org/api/rest_v1/metrics/pageviews/top/en.wikipedia/all-access/2023/01/all-days'); const wikiData = await wikiResponse.json(); // Process Wikipedia data if (wikiData && wikiData.items && wikiData.items[0] && wikiData.items[0].articles) { const wikiTopics = wikiData.items[0].articles .filter(article => !article.article.startsWith('Special:') && !article.article.startsWith('Main_Page') && !article.article.startsWith('Wikipedia:')) .slice(0, 15) .map(article => ({ keyword: article.article.replace(/_/g, ' '), searchVolume: article.views, source: 'wikipedia' })); combinedTopics = [...combinedTopics, ...wikiTopics]; } // Source 3: Emerging research topics (simulated) const emergingTopics = [ { keyword: 'biomimetic materials science', searchVolume: 32000 }, { keyword: 'neuromorphic computing chips', searchVolume: 28000 }, { keyword: 'digital twin technology applications', searchVolume: 47000 }, { keyword: 'post-quantum cryptography standards', searchVolume: 39000 }, { keyword: 'spatial computing interfaces', searchVolume: 43000 } ].map(item => ({ ...item, source: 'emerging_research' })); combinedTopics = [...combinedTopics, ...emergingTopics]; // Deduplicate and return return Array.from(new Map(combinedTopics.map(item => [item.keyword, item])).values()); } catch (error) { console.error('Error fetching trending topics:', error); return []; } }; // Update trending date display in the UI const updateTrendingDateDisplay = () => { const dateInfo = getTrendingDate(); const dateDisplay = document.getElementById('trending-date'); if (dateDisplay) { const formattedDate = new Date(dateInfo.timestamp).toLocaleString(); dateDisplay.textContent = `Trending as of: ${formattedDate}`; dateDisplay.style.display = 'block'; } }; // Enrich keywords with search result counts const enrichKeywordsWithOpportunityData = async (keywords) => { // In a production environment, you would use a real search API // Here we'll simulate the data with a deterministic algorithm return Promise.all(keywords.map(async (keyword) => { // Simulate API call to get search result count // In reality, you would use a search engine API // Algorithm to generate realistic but varied result counts // Technical/specific keywords tend to have fewer results const wordCount = keyword.keyword.split(' ').length; const containsTechnicalTerms = /\b(api|algorithm|framework|protocol|quantum|neural|encryption|serverless|computing)\b/i.test(keyword.keyword); const isNiche = wordCount >= 3 || containsTechnicalTerms; // Base result count - more specific terms have fewer results let baseResultCount; if (isNiche) { baseResultCount = Math.floor(50000 + Math.random() * 500000); } else { baseResultCount = Math.floor(1000000 + Math.random() * 50000000); } // Add some randomness but keep it deterministic for the same keyword const seed = keyword.keyword.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0); const pseudoRandom = Math.sin(seed) * 10000; const resultCount = Math.max(10000, Math.floor(baseResultCount + pseudoRandom)); return { ...keyword, resultCount }; })); }; // Calculate opportunity score based on search volume vs. result count const calculateOpportunityScore = (keywords) => { // Calculate the opportunity score // Higher score = high search volume + low result count (better opportunity) const keywordsWithScore = keywords.map(keyword => { // Normalize search volume (0-100) const maxSearchVolume = Math.max(...keywords.map(k => k.searchVolume)); const normalizedSearchVolume = (keyword.searchVolume / maxSearchVolume) * 100; // Normalize result count inversely (fewer results = higher score) const maxResultCount = Math.max(...keywords.map(k => k.resultCount)); const normalizedResultCount = 100 - ((keyword.resultCount / maxResultCount) * 100); // Calculate SR (Search Relevance) - how relevant the keyword is based on search volume const searchRelevance = Math.round(normalizedSearchVolume); // Calculate DR (Difficulty Rating) - how difficult it is to rank for this keyword // Lower result count means easier to rank (lower difficulty) const difficultyRating = Math.round(100 - normalizedResultCount); // Calculate opportunity score (weighted average) // 60% weight to search volume, 40% weight to inverse result count const opportunityScore = Math.round((normalizedSearchVolume * 0.6) + (normalizedResultCount * 0.4)); // Calculate competition level (lower is better) const competitionLevel = Math.round(keyword.resultCount / keyword.searchVolume); // Calculate keyword quality score (0-100) for color coding // Higher is better - combines all metrics const qualityScore = Math.round( (opportunityScore * 0.5) + (searchRelevance * 0.3) + ((100 - difficultyRating) * 0.2) ); return { ...keyword, opportunityScore, searchRelevance, difficultyRating, competitionLevel, qualityScore }; }); // Sort by opportunity score (highest first) return keywordsWithScore .sort((a, b) => b.opportunityScore - a.opportunityScore) .slice(0, 10); }; // Use backup keywords when API fails const useBackupKeywords = () => { const backupKeywords = [ { keyword: 'quantum machine learning algorithms', opportunityScore: 95, searchVolume: 42000, resultCount: 156000, competitionLevel: 4 }, { keyword: 'zero-trust network architecture', opportunityScore: 92, searchVolume: 68000, resultCount: 310000, competitionLevel: 5 }, { keyword: 'synthetic data generation techniques', opportunityScore: 89, searchVolume: 51000, resultCount: 245000, competitionLevel: 5 }, { keyword: 'edge computing security frameworks', opportunityScore: 87, searchVolume: 73000, resultCount: 420000, competitionLevel: 6 }, { keyword: 'federated learning privacy', opportunityScore: 85, searchVolume: 47000, resultCount: 280000, competitionLevel: 6 }, { keyword: 'explainable ai for healthcare', opportunityScore: 82, searchVolume: 59000, resultCount: 390000, competitionLevel: 7 }, { keyword: 'post-quantum cryptography implementation', opportunityScore: 80, searchVolume: 38000, resultCount: 265000, competitionLevel: 7 }, { keyword: 'neuromorphic computing applications', opportunityScore: 78, searchVolume: 31000, resultCount: 225000, competitionLevel: 7 }, { keyword: 'graph neural networks for recommendation', opportunityScore: 76, searchVolume: 44000, resultCount: 340000, competitionLevel: 8 }, { keyword: 'digital twin technology standards', opportunityScore: 74, searchVolume: 53000, resultCount: 420000, competitionLevel: 8 } ]; renderKeywords(backupKeywords); }; // Render keywords const renderKeywords = (keywords) => { keywordsContainer.innerHTML = ''; keywords.forEach(keyword => { const keywordCard = document.createElement('div'); keywordCard.className = 'keyword-card'; keywordCard.dataset.keyword = keyword.keyword; // Display opportunity score and metrics const score = keyword.opportunityScore || keyword.score || 0; const searchVolume = keyword.searchVolume ? `${(keyword.searchVolume/1000).toFixed(1)}K` : 'N/A'; const resultCount = keyword.resultCount ? `${(keyword.resultCount/1000).toFixed(1)}K` : 'N/A'; const competitionLevel = keyword.competitionLevel || 'N/A'; // Determine color based on quality score const qualityScore = keyword.qualityScore || score; const qualityColor = getQualityColor(qualityScore); keywordCard.innerHTML = `