// Professional Newsletter App JavaScript class NewsletterApp { constructor() { this.charts = {}; this.analytics = { pageViews: 0, timeOnPage: Date.now(), interactions: 0 }; this.init(); } init() { this.trackAnalytics(); this.setupChartInteractions(); this.setupScrollTracking(); this.setupExportFunctionality(); this.setupSearchFunctionality(); } // Analytics Tracking trackAnalytics() { this.analytics.pageViews++; // Track time on page window.addEventListener('beforeunload', () => { const timeSpent = Date.now() - this.analytics.timeOnPage; this.logAnalytics('timeOnPage', timeSpent); }); // Track interactions document.addEventListener('click', (e) => { if (e.target.matches('a, button, .chart-container, .metric-card')) { this.analytics.interactions++; this.logAnalytics('interaction', e.target.tagName + ':' + (e.target.className || 'no-class')); } }); } logAnalytics(event, data) { // In production, this would send to analytics service console.log('📊 Analytics:', { event, data, timestamp: new Date().toISOString() }); } // Chart Interactions setupChartInteractions() { // Add hover effects and click interactions to charts document.addEventListener('DOMContentLoaded', () => { const chartContainers = document.querySelectorAll('.chart-container'); chartContainers.forEach((container, index) => { this.enhanceChartContainer(container, index); }); }); } enhanceChartContainer(container, index) { // Add download button for charts const downloadBtn = document.createElement('button'); downloadBtn.className = 'btn btn-secondary chart-download'; downloadBtn.innerHTML = '📥 Download Chart'; downloadBtn.style.cssText = 'position: absolute; top: 10px; right: 10px; z-index: 1000; font-size: 0.8em; padding: 5px 10px;'; downloadBtn.addEventListener('click', () => { this.downloadChart(index); }); container.style.position = 'relative'; container.appendChild(downloadBtn); // Add fullscreen option const fullscreenBtn = document.createElement('button'); fullscreenBtn.className = 'btn btn-secondary chart-fullscreen'; fullscreenBtn.innerHTML = '🔍 Expand'; fullscreenBtn.style.cssText = 'position: absolute; top: 10px; right: 120px; z-index: 1000; font-size: 0.8em; padding: 5px 10px;'; fullscreenBtn.addEventListener('click', () => { this.expandChart(container); }); container.appendChild(fullscreenBtn); } downloadChart(index) { const canvas = document.getElementById(`chart-${index}`); if (canvas) { const link = document.createElement('a'); link.download = `newsletter-chart-${index}-${Date.now()}.png`; link.href = canvas.toDataURL(); link.click(); this.logAnalytics('chartDownload', index); } } expandChart(container) { container.classList.toggle('chart-expanded'); if (container.classList.contains('chart-expanded')) { container.style.cssText += ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 90vw; height: 90vh; background: white; z-index: 10000; box-shadow: 0 10px 30px rgba(0,0,0,0.3); border-radius: 10px; padding: 20px; `; // Add close button const closeBtn = document.createElement('button'); closeBtn.innerHTML = '✕'; closeBtn.style.cssText = 'position: absolute; top: 10px; right: 10px; background: #dc3545; color: white; border: none; border-radius: 50%; width: 30px; height: 30px; cursor: pointer; z-index: 10001;'; closeBtn.addEventListener('click', () => { this.expandChart(container); // Toggle back }); container.appendChild(closeBtn); // Add overlay const overlay = document.createElement('div'); overlay.className = 'chart-overlay'; overlay.style.cssText = 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 9999;'; overlay.addEventListener('click', () => { this.expandChart(container); // Toggle back }); document.body.appendChild(overlay); } else { container.style.cssText = ''; const overlay = document.querySelector('.chart-overlay'); if (overlay) overlay.remove(); const closeBtn = container.querySelector('button[style*="position: absolute; top: 10px; right: 10px"]'); if (closeBtn) closeBtn.remove(); } this.logAnalytics('chartExpand', container.classList.contains('chart-expanded')); } // Scroll Tracking for Reading Progress setupScrollTracking() { let maxScroll = 0; window.addEventListener('scroll', () => { const scrollPercent = (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100; maxScroll = Math.max(maxScroll, scrollPercent); this.updateReadingProgress(scrollPercent); }); window.addEventListener('beforeunload', () => { this.logAnalytics('maxScrollPercent', maxScroll); }); } updateReadingProgress(percent) { // Create or update reading progress bar let progressBar = document.getElementById('reading-progress'); if (!progressBar) { progressBar = document.createElement('div'); progressBar.id = 'reading-progress'; progressBar.style.cssText = ` position: fixed; top: 0; left: 0; height: 3px; background: linear-gradient(90deg, #667eea, #764ba2); z-index: 9999; transition: width 0.1s ease; `; document.body.appendChild(progressBar); } progressBar.style.width = percent + '%'; } // Export Functionality setupExportFunctionality() { // Add export buttons to newsletter const header = document.querySelector('.header'); if (header) { const exportContainer = document.createElement('div'); exportContainer.className = 'export-buttons'; exportContainer.style.cssText = 'margin-top: 20px; display: flex; gap: 10px; justify-content: center;'; const exportPDFBtn = this.createExportButton('📄 Export PDF', 'pdf'); const exportEmailBtn = this.createExportButton('📧 Email Newsletter', 'email'); const printBtn = this.createExportButton('🖨️ Print', 'print'); exportContainer.appendChild(exportPDFBtn); exportContainer.appendChild(exportEmailBtn); exportContainer.appendChild(printBtn); header.appendChild(exportContainer); } } createExportButton(text, type) { const btn = document.createElement('button'); btn.className = 'btn btn-secondary'; btn.innerHTML = text; btn.style.fontSize = '0.9em'; btn.addEventListener('click', () => { this.handleExport(type); }); return btn; } handleExport(type) { switch (type) { case 'pdf': this.exportToPDF(); break; case 'email': this.shareViaEmail(); break; case 'print': window.print(); break; } this.logAnalytics('export', type); } exportToPDF() { // Using browser's print functionality for PDF export const originalTitle = document.title; document.title = 'Professional Newsletter - ' + new Date().toLocaleDateString(); // Temporarily hide export buttons const exportButtons = document.querySelector('.export-buttons'); if (exportButtons) exportButtons.style.display = 'none'; window.print(); // Restore document.title = originalTitle; if (exportButtons) exportButtons.style.display = 'flex'; } shareViaEmail() { const subject = encodeURIComponent(document.title); const body = encodeURIComponent(`Check out this professional newsletter: ${window.location.href}`); window.open(`mailto:?subject=${subject}&body=${body}`); } // Search Functionality setupSearchFunctionality() { // Add search box to newsletter const content = document.querySelector('.content'); if (content) { const searchContainer = document.createElement('div'); searchContainer.className = 'search-container'; searchContainer.style.cssText = 'margin-bottom: 30px; padding: 20px; background: #f8f9fa; border-radius: 8px;'; const searchInput = document.createElement('input'); searchInput.type = 'text'; searchInput.placeholder = '🔍 Search newsletter content...'; searchInput.style.cssText = 'width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 5px; font-size: 1em;'; searchInput.addEventListener('input', (e) => { this.searchContent(e.target.value); }); searchContainer.appendChild(searchInput); content.insertBefore(searchContainer, content.firstChild); } } searchContent(query) { // Remove previous highlights this.clearHighlights(); if (query.length < 3) return; const textNodes = this.getTextNodes(document.querySelector('.content')); let matchCount = 0; textNodes.forEach(node => { const text = node.textContent; const regex = new RegExp(`(${query})`, 'gi'); if (regex.test(text)) { const highlightedText = text.replace(regex, '$1'); const wrapper = document.createElement('span'); wrapper.innerHTML = highlightedText; node.parentNode.replaceChild(wrapper, node); matchCount++; } }); this.showSearchResults(matchCount, query); this.logAnalytics('search', { query, matches: matchCount }); } getTextNodes(element) { const textNodes = []; const walker = document.createTreeWalker( element, NodeFilter.SHOW_TEXT, null, false ); let node; while (node = walker.nextNode()) { if (node.textContent.trim()) { textNodes.push(node); } } return textNodes; } clearHighlights() { const highlights = document.querySelectorAll('.search-highlight'); highlights.forEach(highlight => { const parent = highlight.parentNode; parent.replaceChild(document.createTextNode(highlight.textContent), highlight); parent.normalize(); }); } showSearchResults(count, query) { let resultsDiv = document.getElementById('search-results'); if (!resultsDiv) { resultsDiv = document.createElement('div'); resultsDiv.id = 'search-results'; resultsDiv.style.cssText = 'margin-top: 10px; padding: 10px; background: #e3f2fd; border-radius: 5px; font-size: 0.9em;'; document.querySelector('.search-container').appendChild(resultsDiv); } if (count > 0) { resultsDiv.innerHTML = `✅ Found ${count} matches for "${query}"`; resultsDiv.style.background = '#e8f5e8'; } else { resultsDiv.innerHTML = `❌ No matches found for "${query}"`; resultsDiv.style.background = '#ffebee'; } } // Utility Functions debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // Initialize tooltips for data points initializeTooltips() { const metrics = document.querySelectorAll('.metric-card, .chart-container'); metrics.forEach(element => { element.addEventListener('mouseenter', (e) => { this.showTooltip(e, element); }); element.addEventListener('mouseleave', () => { this.hideTooltip(); }); }); } showTooltip(event, element) { const tooltip = document.createElement('div'); tooltip.className = 'tooltip'; tooltip.style.cssText = ` position: absolute; background: #333; color: white; padding: 8px 12px; border-radius: 4px; font-size: 0.8em; z-index: 10000; pointer-events: none; max-width: 200px; `; // Set tooltip content based on element type if (element.classList.contains('metric-card')) { tooltip.textContent = 'Click for detailed analysis'; } else if (element.classList.contains('chart-container')) { tooltip.textContent = 'Interactive chart - hover for details'; } document.body.appendChild(tooltip); // Position tooltip const rect = element.getBoundingClientRect(); tooltip.style.left = rect.left + 'px'; tooltip.style.top = (rect.top - tooltip.offsetHeight - 5) + 'px'; } hideTooltip() { const tooltip = document.querySelector('.tooltip'); if (tooltip) { tooltip.remove(); } } } // Initialize app when DOM is loaded document.addEventListener('DOMContentLoaded', () => { window.newsletterApp = new NewsletterApp(); }); // CSS for search highlights const searchStyles = document.createElement('style'); searchStyles.textContent = ` .search-highlight { background: #ffeb3b; padding: 2px 4px; border-radius: 3px; font-weight: bold; } .chart-download, .chart-fullscreen { opacity: 0; transition: opacity 0.3s ease; } .chart-container:hover .chart-download, .chart-container:hover .chart-fullscreen { opacity: 1; } @media print { .export-buttons, .search-container, #reading-progress { display: none !important; } } `; document.head.appendChild(searchStyles);