ESG Sentiment Analyzer
Select a Company for Analysis
Choose a simulated company from the list to see its ESG sentiment profile. The data is entirely simulated for demonstration purposes.
ESG Sentiment Analysis Results
Select a company and click "Analyze Sentiment" to see results.
Sentiment Trends & Breakdown
ESG Category Sentiment Breakdown
Overall ESG Sentiment Trend (Simulated)
Analyze sentiment to generate charts.
No analysis results available. Please select a company and click "Analyze Sentiment".
`; return; } let sentimentLevel = ''; let sentimentColorClass = ''; if (results.overallEsgSentiment >= 75) { sentimentLevel = 'Very Positive'; sentimentColorClass = 'text-green-700'; } else if (results.overallEsgSentiment >= 60) { sentimentLevel = 'Positive'; sentimentColorClass = 'text-blue-700'; } else if (results.overallEsgSentiment >= 40) { sentimentLevel = 'Neutral'; sentimentColorClass = 'text-yellow-700'; } else { sentimentLevel = 'Negative'; sentimentColorClass = 'text-red-700'; } let tableHtml = `ESG Sentiment for ${results.companyName}
Industry: ${results.industry}
Overall ESG Sentiment Score: ${results.overallEsgSentiment.toFixed(1)} / 100
Sentiment Level: ${sentimentLevel}
| Category | Overall Score | Positive (%) | Negative (%) | Neutral (%) |
|---|---|---|---|---|
| Environmental | ${results.esgSentiment.environmental.overallScore.toFixed(1)} | ${results.esgSentiment.environmental.positivePercentage}% | ${results.esgSentiment.environmental.negativePercentage}% | ${results.esgSentiment.environmental.neutralPercentage}% |
| Social | ${results.esgSentiment.social.overallScore.toFixed(1)} | ${results.esgSentiment.social.positivePercentage}% | ${results.esgSentiment.social.negativePercentage}% | ${results.esgSentiment.social.neutralPercentage}% |
| Governance | ${results.esgSentiment.governance.overallScore.toFixed(1)} | ${results.esgSentiment.governance.positivePercentage}% | ${results.esgSentiment.governance.negativePercentage}% | ${results.esgSentiment.governance.neutralPercentage}% |
Key Sentiment Drivers:
- Environmental: ${results.esgSentiment.environmental.keyTopics.map(t => `${t.topic} (Score: ${t.score.toFixed(0)})`).join(', ')}
- Social: ${results.esgSentiment.social.keyTopics.map(t => `${t.topic} (Score: ${t.score.toFixed(0)})`).join(', ')}
- Governance: ${results.esgSentiment.governance.keyTopics.map(t => `${t.topic} (Score: ${t.score.toFixed(0)})`).join(', ')}
Disclaimer: This tool provides *simulated* ESG sentiment analysis for educational and illustrative purposes only. It does not reflect real-world data from news, social media, or other sources, and should not be used for actual investment decisions. Sentiment scores are on a 0-100 scale, where <40 is negative, 40-60 is neutral, and >60 is positive.
`; resultsOutput.innerHTML = tableHtml; } /** * Renders the ESG Category Sentiment Breakdown Chart. * @param {Object} results - Results from analyzeSentiment. */ function renderEsgCategorySentimentChart(results) { if (!results || !results.esgSentiment) { chartPlaceholder.classList.remove('hidden'); if (esgCategorySentimentChartInstance) { esgCategorySentimentChartInstance.destroy(); esgCategorySentimentChartInstance = null; } return; } chartPlaceholder.classList.add('hidden'); // Hide placeholder if chart can be rendered const ctx = document.getElementById('esgCategorySentimentChart').getContext('2d'); // Destroy existing chart instance if it exists to prevent overlap if (esgCategorySentimentChartInstance) { esgCategorySentimentChartInstance.destroy(); } const chartLabels = ['Environmental', 'Social', 'Governance']; const chartData = [ results.esgSentiment.environmental.overallScore, results.esgSentiment.social.overallScore, results.esgSentiment.governance.overallScore ]; const chartColors = ['#10B981', '#3B82F6', '#A855F7']; // Green, Blue, Purple esgCategorySentimentChartInstance = new Chart(ctx, { type: 'bar', data: { labels: chartLabels, datasets: [{ label: 'Sentiment Score (0-100)', data: chartData, backgroundColor: chartColors, borderColor: chartColors.map(color => color.replace('0.2', '1')), borderWidth: 1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, title: { display: true, text: `Category Sentiment for ${results.companyName}`, font: { size: 18, family: 'Inter', weight: 'bold' }, color: '#1F2937' }, tooltip: { callbacks: { label: function(context) { return `${context.label}: ${context.parsed.y.toFixed(1)}`; } } } }, scales: { y: { beginAtZero: true, max: 100, title: { display: true, text: 'Sentiment Score (0-100)', font: { size: 14, family: 'Inter' }, color: '#333' }, ticks: { font: { family: 'Inter' } } }, x: { ticks: { font: { family: 'Inter' } } } } } }); } /** * Renders the Overall ESG Sentiment Trend Chart. * @param {Object} results - Results from analyzeSentiment. */ function renderEsgTrendChart(results) { if (!results || !results.sentimentTrend || results.sentimentTrend.length === 0) { chartPlaceholder.classList.remove('hidden'); if (esgTrendChartInstance) { esgTrendChartInstance.destroy(); esgTrendChartInstance = null; } return; } chartPlaceholder.classList.add('hidden'); // Hide placeholder if chart can be rendered const ctx = document.getElementById('esgTrendChart').getContext('2d'); // Destroy existing chart instance if it exists to prevent overlap if (esgTrendChartInstance) { esgTrendChartInstance.destroy(); } const trendLabels = results.sentimentTrend.map(t => t.quarter); const trendData = results.sentimentTrend.map(t => t.score); esgTrendChartInstance = new Chart(ctx, { type: 'line', data: { labels: trendLabels, datasets: [{ label: 'Overall ESG Sentiment Score', data: trendData, borderColor: '#3B82F6', // Blue 500 backgroundColor: 'rgba(59, 130, 246, 0.2)', // Light blue fill borderWidth: 2, tension: 0.3, // Smooth curve pointRadius: 5, pointBackgroundColor: '#3B82F6', fill: true, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: true, position: 'top', labels: { font: { size: 14, family: 'Inter', }, color: '#333' } }, title: { display: true, text: `Overall ESG Sentiment Trend for ${results.companyName}`, font: { size: 18, family: 'Inter', weight: 'bold' }, color: '#1F2937' }, tooltip: { mode: 'index', intersect: false, callbacks: { label: function(context) { let label = context.dataset.label || ''; if (label) { label += ': '; } if (context.parsed.y !== null) { label += `${context.parsed.y.toFixed(1)}`; } return label; } } } }, scales: { y: { beginAtZero: false, min: 0, max: 100, title: { display: true, text: 'Sentiment Score (0-100)', font: { size: 14, family: 'Inter' }, color: '#333' }, ticks: { font: { family: 'Inter' } } }, x: { title: { display: true, text: 'Quarter', font: { size: 14, family: 'Inter' }, color: '#333' }, ticks: { font: { family: 'Inter' } } } } } }); } /** * Handles the PDF download functionality. * Captures the results output and chart canvases to generate a PDF report. */ window.downloadPdf = async function() { if (typeof html2canvas === 'undefined' || typeof jspdf === 'undefined' || typeof jspdf.jsPDF === 'undefined') { displayMessage('PDF generation libraries not loaded. Please try again or refresh.'); return; } hideMessage(); const { jsPDF } = jspdf; const doc = new jsPDF('p', 'pt', 'a4'); // Temporarily show the charts tab to ensure they render on canvas before capturing const chartsTabWasHidden = chartsTab.classList.contains('hidden'); if (chartsTabWasHidden) { chartsTab.classList.remove('hidden'); } // Elements to capture for PDF: results table and then each chart canvas const elementsToCapture = [ document.getElementById('resultsOutput'), document.getElementById('esgCategorySentimentChart'), document.getElementById('esgTrendChart') ]; let yPos = 40; doc.setFontSize(22); doc.setTextColor(51, 51, 51); doc.text('ESG Sentiment Analysis Report', doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' }); yPos += 30; doc.setFontSize(12); doc.setTextColor(100, 100, 100); doc.text(`Generated on: ${new Date().toLocaleDateString()}`, doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' }); yPos += 40; for (const element of elementsToCapture) { try { // For chart canvases, ensure they are rendered. For other elements, ensure visibility if needed. let elementToRender = element; if (element.tagName.toLowerCase() === 'canvas') { if ((element.id === 'esgCategorySentimentChart' && esgCategorySentimentChartInstance === null) || (element.id === 'esgTrendChart' && esgTrendChartInstance === null)) { console.warn(`Chart ${element.id} not yet rendered for PDF capture. Skipping.`); continue; } } else { // For non-canvas elements, ensure they are visible for html2canvas const wasHidden = element.classList.contains('hidden'); if (wasHidden) { element.classList.remove('hidden'); } // Capture the element as a canvas elementToRender = await html2canvas(element, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }); if (wasHidden) { element.classList.add('hidden'); // Restore hidden state } } const imgData = elementToRender.toDataURL('image/png'); const imgWidth = 550; const imgHeight = (elementToRender.height * imgWidth) / elementToRender.width; if (yPos + imgHeight > doc.internal.pageSize.getHeight() - 40) { doc.addPage(); yPos = 40; } doc.addImage(imgData, 'PNG', (doc.internal.pageSize.getWidth() - imgWidth) / 2, yPos, imgWidth, imgHeight); yPos += imgHeight + 30; } catch (error) { console.error('Error capturing element for PDF:', error); displayMessage('Failed to generate part of the PDF. Please ensure all data is loaded and visible before downloading.'); } } // Restore charts tab hidden state if it was temporarily shown if (chartsTabWasHidden) { chartsTab.classList.add('hidden'); } doc.save('ESG_Sentiment_Analysis_Report.pdf'); }; // Initial setup: Populate company select and show the input tab when the DOM is ready populateCompanySelect(); showTab('input'); });