S SOLARSPECS
Home / Compare / Spec
Loading products...
'; try { // Use API for faster initial load, fallback to data.json try { var resp = await fetch('/api/brands'); await resp.json(); // warm up cache } catch(e) {} const response = await fetch('data.json?v=' + Date.now()); allProducts = await response.json(); renderTable(); } catch(e) { document.getElementById('compareContainer').innerHTML = '
Failed to load data. Go back
'; } window.addEventListener('scroll', function() { var btn = document.getElementById('scrollTop'); btn.style.display = window.scrollY > 400 ? 'flex' : 'none'; }); } function getBestWorst(products, key, higherIsBetter) { var vals = products.map(function(p) { return p[key]; }).filter(function(v) { return v !== undefined && v !== null && !isNaN(v); }); if (vals.length === 0) return {}; var best = higherIsBetter ? Math.max.apply(null, vals) : Math.min.apply(null, vals); var worst = higherIsBetter ? Math.min.apply(null, vals) : Math.max.apply(null, vals); return { best: best, worst: worst, range: best - worst || 1 }; } function barStyle(val, best, worst, range, higherIsBetter) { var pct = range > 0 ? ((val - (higherIsBetter ? worst : best)) / range * 100) : 100; return Math.max(5, Math.min(100, pct)); } function computeOverallScore(p) { var score = 0; if (p.pmax > 500) score += 25; else if (p.pmax > 400) score += 15; else if (p.pmax > 300) score += 10; if (p.efficiency > 22) score += 30; else if (p.efficiency > 21) score += 20; else if (p.efficiency > 20) score += 15; if (p.warranty >= 25) score += 20; else if (p.warranty >= 20) score += 15; else if (p.warranty >= 15) score += 10; if (p.tier === 1) score += 15; else if (p.tier === 2) score += 8; if (p.cell_type && p.cell_type.toLowerCase().includes('n-type')) score += 10; return Math.min(100, score); } function renderTable() { var compareSlugs = getCompareSlugs(); // Validate: remove any slugs that don't match actual products var validSlugs = compareSlugs.filter(function(slug) { return allProducts.some(function(p) { return p.slug === slug; }); }); if (validSlugs.length !== compareSlugs.length) { localStorage.setItem('compareSolar', JSON.stringify(validSlugs)); compareSlugs = validSlugs; } var products = allProducts.filter(function(p) { return compareSlugs.includes(p.slug); }); var container = document.getElementById('compareContainer'); var countLabel = document.getElementById('compareCountLabel'); if (countLabel) countLabel.textContent = compareSlugs.length + ' models selected'; // Show/hide Clear All button var clearBtnContainer = document.getElementById('clearCompareContainer'); if (clearBtnContainer) { clearBtnContainer.style.display = compareSlugs.length > 0 ? 'flex' : 'none'; } if (products.length === 0) { container.innerHTML = '
⚖️
No products selected for comparison
Browse Product Database
'; return; } var bwPmax = getBestWorst(products, 'pmax', true); var bwEff = getBestWorst(products, 'efficiency', true); var bwWarr = getBestWorst(products, 'warranty', true); var bwVoc = getBestWorst(products, 'temp_coeff', false); // lower is better var fields = [ { label: 'Brand', key: 'brand', type: 'text' }, { label: 'Model', key: 'model', type: 'text' }, { label: 'Max Power', key: 'pmax', unit: 'W', type: 'bar', bw: bwPmax, higher: true }, { label: 'Efficiency', key: 'efficiency', unit: '%', type: 'bar', bw: bwEff, higher: true }, { label: 'Cell Type', key: 'cell_type', type: 'text' }, { label: 'Voltage (Vmp)', key: 'vmp', unit: 'V', type: 'num' }, { label: 'Open Circuit (Voc)', key: 'voc', unit: 'V', type: 'num' }, { label: 'Current (Imp)', key: 'imp', unit: 'A', type: 'num' }, { label: 'Short Circuit (Isc)', key: 'isc', unit: 'A', type: 'num' }, { label: 'Temp Coefficient', key: 'temp_coeff', unit: '%/°C', type: 'num' }, { label: 'Warranty', key: 'warranty', unit: ' years', type: 'bar', bw: bwWarr, higher: true }, { label: 'Weight', key: 'weight', unit: ' kg', type: 'num' }, { label: 'Dimensions', key: 'dimensions', type: 'text' } ]; // Compute overall scores var scores = products.map(function(p) { return { slug: p.slug, score: computeOverallScore(p) }; }); var maxScore = Math.max.apply(null, scores.map(function(s) { return s.score; })); var html = '
'; // Logo row + Score html += ''; products.forEach(function(p) { var domain = p.domain || 'solarreviews.com'; var logoUrl = 'https://img.logo.dev/' + domain + '?token=' + logoToken + '&format=png&size=200'; var score = scores.find(function(s) { return s.slug === p.slug; }); var scorePct = score ? Math.round(score.score / maxScore * 100) : 0; var scoreColor = scorePct >= 80 ? '#34d399' : scorePct >= 60 ? '#fbbf24' : '#f87171'; html += ''; }); html += ''; fields.forEach(function(f) { html += '' + ''; products.forEach(function(p) { var val = p[f.key]; var dispVal = val !== undefined && val !== null ? val : '—'; if (f.type === 'bar' && f.bw && val !== undefined && val !== null && !isNaN(val)) { var pct = barStyle(val, f.bw.best, f.bw.worst, f.bw.range, f.higher); var isBest = val === f.bw.best; var isWorst = val === f.bw.worst; var barColor = isBest ? '#34d399' : isWorst ? '#f87171' : '#f59e0b'; html += ''; } else { if (f.unit && dispVal !== '—') dispVal += f.unit; html += ''; } }); html += ''; }); html += '
Panel' + '
' + '
' + '' + p.brand + '' + '
' + '
' + '
' + p.model + '
' + '
' + p.brand + '
' + '
' + // Score bar '
' + '
' + '
' + '
' + '' + scorePct + '%' + '
' + 'View Specs →' + '' + '
' + '
' + f.label + '' + '
' + '' + val + (f.unit || '') + '' + '
' + '
' + '
' + '
' + '
' + dispVal + '
'; // Share button var slugStr = compareSlugs.join(','); var shareUrl = window.location.origin + window.location.pathname + '?s=' + slugStr; html += '
' + '' + '' + '
'; container.innerHTML = html; } function removeCompare(slug) { var compareSlugs = getCompareSlugs().filter(function(s) { return s !== slug; }); localStorage.setItem('compareSolar', JSON.stringify(compareSlugs)); renderTable(); } function clearCompare() { localStorage.setItem('compareSolar', JSON.stringify([])); renderTable(); } init();