| 1 |
(function () { |
| 2 |
'use strict'; |
| 3 |
|
| 4 |
function badgeColors(badge) { |
| 5 |
if (badge === 'skip') return { bg: '#fee2e2', color: '#991b1b' }; |
| 6 |
if (badge === 'best-value') return { bg: '#dcfce7', color: '#14532d' }; |
| 7 |
if (badge === 'top-pick') return { bg: '#eff6ff', color: '#1e40af' }; |
| 8 |
if (badge === 'award-winner') return { bg: '#fef9c3', color: '#713f12' }; |
| 9 |
return { bg: '#fbbf24', color: '#451a03' }; // recommended / default |
| 10 |
} |
| 11 |
|
| 12 |
function badgeLabel(badge) { |
| 13 |
var map = { |
| 14 |
'recommended': '⭐ Recommended', |
| 15 |
'best-value': '💰 Best Value', |
| 16 |
'top-pick': '� |
| 17 |
Top Pick', |
| 18 |
'award-winner': '🏆 Award Winner', |
| 19 |
'skip': '⚠️ Skip It', |
| 20 |
'none': '' |
| 21 |
}; |
| 22 |
return map[badge] || ''; |
| 23 |
} |
| 24 |
|
| 25 |
function scoreFill(score, max) { |
| 26 |
return Math.min(100, Math.max(0, (score / max) * 100)); |
| 27 |
} |
| 28 |
|
| 29 |
function el(tag, attrs) { |
| 30 |
var node = document.createElement(tag); |
| 31 |
if (attrs) { |
| 32 |
Object.keys(attrs).forEach(function (k) { |
| 33 |
if (k === 'textContent') { node.textContent = attrs[k]; return; } |
| 34 |
if (k === 'innerHTML') { node.innerHTML = attrs[k]; return; } |
| 35 |
if (k === 'style') { |
| 36 |
Object.keys(attrs.style).forEach(function (s) { |
| 37 |
node.style[s] = attrs.style[s]; |
| 38 |
}); |
| 39 |
return; |
| 40 |
} |
| 41 |
node.setAttribute(k, attrs[k]); |
| 42 |
}); |
| 43 |
} |
| 44 |
return node; |
| 45 |
} |
| 46 |
|
| 47 |
function append(parent) { |
| 48 |
var children = Array.prototype.slice.call(arguments, 1); |
| 49 |
children.forEach(function (c) { if (c) parent.appendChild(c); }); |
| 50 |
return parent; |
| 51 |
} |
| 52 |
|
| 53 |
function buildScore(o) { |
| 54 |
var display = o.scoreDisplay || 'circle'; |
| 55 |
|
| 56 |
if (display === 'circle') { |
| 57 |
var circle = el('div', { |
| 58 |
className: 'bkbg-vb-score-circle', |
| 59 |
style: { |
| 60 |
background: o.scoreCircleBg || '#6366f1', |
| 61 |
color: o.scoreCircleColor || '#fff' |
| 62 |
} |
| 63 |
}); |
| 64 |
append(circle, |
| 65 |
el('span', { className: 'bkbg-vb-score-value', textContent: parseFloat(o.overallScore).toFixed(1) }), |
| 66 |
el('span', { className: 'bkbg-vb-score-max', textContent: '/' + (o.scoreMax || 10) }) |
| 67 |
); |
| 68 |
return circle; |
| 69 |
} |
| 70 |
|
| 71 |
if (display === 'bar') { |
| 72 |
var wrap = el('div', { |
| 73 |
className: 'bkbg-vb-score-bar-wrap', |
| 74 |
style: { color: o.headerColor || '#fff' } |
| 75 |
}); |
| 76 |
var numRow = el('div', { style: { display: 'flex', alignItems: 'baseline', gap: '2px' } }); |
| 77 |
append(numRow, |
| 78 |
el('span', { className: 'bkbg-vb-score-bar-num', textContent: parseFloat(o.overallScore).toFixed(1) }), |
| 79 |
el('span', { className: 'bkbg-vb-score-bar-max', textContent: '/' + (o.scoreMax || 10) }) |
| 80 |
); |
| 81 |
var track = el('div', { className: 'bkbg-vb-score-bar-track' }); |
| 82 |
var fill = el('div', { |
| 83 |
className: 'bkbg-vb-score-bar-fill', |
| 84 |
style: { |
| 85 |
width: scoreFill(o.overallScore, o.scoreMax) + '%', |
| 86 |
background: o.scoreCircleBg || '#6366f1' |
| 87 |
} |
| 88 |
}); |
| 89 |
append(track, fill); |
| 90 |
append(wrap, numRow, track); |
| 91 |
return wrap; |
| 92 |
} |
| 93 |
|
| 94 |
// numeric |
| 95 |
var numeric = el('div', { style: { color: o.headerColor || '#fff', flexShrink: '0' } }); |
| 96 |
append(numeric, |
| 97 |
el('div', { |
| 98 |
style: { fontSize: '36px', fontWeight: '900', lineHeight: '1' }, |
| 99 |
textContent: parseFloat(o.overallScore).toFixed(1) |
| 100 |
}), |
| 101 |
el('div', { |
| 102 |
style: { fontSize: '12px', opacity: '.7' }, |
| 103 |
textContent: o.scoreLabel || 'Overall Score' |
| 104 |
}) |
| 105 |
); |
| 106 |
return numeric; |
| 107 |
} |
| 108 |
|
| 109 |
function buildHeader(o) { |
| 110 |
var header = el('div', { |
| 111 |
className: 'bkbg-vb-header', |
| 112 |
style: { |
| 113 |
background: o.headerBg || '#0f172a', |
| 114 |
color: o.headerColor || '#fff' |
| 115 |
} |
| 116 |
}); |
| 117 |
|
| 118 |
// Score |
| 119 |
append(header, buildScore(o)); |
| 120 |
|
| 121 |
// Product info |
| 122 |
var info = el('div', { style: { flex: '1', minWidth: '180px' } }); |
| 123 |
|
| 124 |
var nameEl = el('h3', { |
| 125 |
className: 'bkbg-vb-product-name', |
| 126 |
style: { |
| 127 |
color: o.headerColor || '#fff' |
| 128 |
}, |
| 129 |
textContent: o.productName || '' |
| 130 |
}); |
| 131 |
append(info, nameEl); |
| 132 |
|
| 133 |
if (o.showTagline && o.productTagline) { |
| 134 |
append(info, el('div', { |
| 135 |
className: 'bkbg-vb-tagline', |
| 136 |
textContent: o.productTagline |
| 137 |
})); |
| 138 |
} |
| 139 |
|
| 140 |
if (o.badge && o.badge !== 'none') { |
| 141 |
var bColors = badgeColors(o.badge); |
| 142 |
var badge = el('span', { |
| 143 |
className: 'bkbg-vb-badge', |
| 144 |
style: { background: bColors.bg, color: bColors.color }, |
| 145 |
textContent: badgeLabel(o.badge) |
| 146 |
}); |
| 147 |
append(info, badge); |
| 148 |
} |
| 149 |
|
| 150 |
append(header, info); |
| 151 |
return header; |
| 152 |
} |
| 153 |
|
| 154 |
function buildProsCons(o) { |
| 155 |
var wrapper = el('div', { className: 'bkbg-vb-proscons' + (o.layout === 'stacked' ? ' is-stacked' : '') }); |
| 156 |
|
| 157 |
// Pros |
| 158 |
var prosDiv = el('div', { |
| 159 |
className: 'bkbg-vb-pros', |
| 160 |
style: { |
| 161 |
background: o.prosBg || '#f0fdf4', |
| 162 |
color: o.prosColor || '#14532d', |
| 163 |
borderRight: '1px solid ' + (o.prosBorderColor || '#bbf7d0') |
| 164 |
} |
| 165 |
}); |
| 166 |
append(prosDiv, el('span', { |
| 167 |
className: 'bkbg-vb-pros-label', |
| 168 |
textContent: o.prosLabel || '� |
| 169 |
Pros' |
| 170 |
})); |
| 171 |
var prosUl = el('ul'); |
| 172 |
(o.pros || []).forEach(function (text) { |
| 173 |
if (!text) return; |
| 174 |
var li = el('li'); |
| 175 |
append(li, |
| 176 |
el('i', { className: 'bkbg-vb-icon', style: { color: o.prosIconColor || '#22c55e' }, textContent: '✓' }), |
| 177 |
el('span', { textContent: text }) |
| 178 |
); |
| 179 |
append(prosUl, li); |
| 180 |
}); |
| 181 |
append(prosDiv, prosUl); |
| 182 |
|
| 183 |
// Cons |
| 184 |
var consDiv = el('div', { |
| 185 |
className: 'bkbg-vb-cons', |
| 186 |
style: { |
| 187 |
background: o.consBg || '#fef2f2', |
| 188 |
color: o.consColor || '#7f1d1d' |
| 189 |
} |
| 190 |
}); |
| 191 |
append(consDiv, el('span', { |
| 192 |
className: 'bkbg-vb-cons-label', |
| 193 |
textContent: o.consLabel || '❌ Cons' |
| 194 |
})); |
| 195 |
var consUl = el('ul'); |
| 196 |
(o.cons || []).forEach(function (text) { |
| 197 |
if (!text) return; |
| 198 |
var li = el('li'); |
| 199 |
append(li, |
| 200 |
el('i', { className: 'bkbg-vb-icon', style: { color: o.consIconColor || '#ef4444' }, textContent: '✗' }), |
| 201 |
el('span', { textContent: text }) |
| 202 |
); |
| 203 |
append(consUl, li); |
| 204 |
}); |
| 205 |
append(consDiv, consUl); |
| 206 |
|
| 207 |
append(wrapper, prosDiv, consDiv); |
| 208 |
return wrapper; |
| 209 |
} |
| 210 |
|
| 211 |
function buildVerdict(o) { |
| 212 |
var verdictWrap = el('div', { |
| 213 |
className: 'bkbg-vb-verdict', |
| 214 |
style: { |
| 215 |
background: o.verdictBg || '#f8fafc', |
| 216 |
color: o.verdictColor || '#374151', |
| 217 |
borderTop: '1px solid ' + (o.verdictBorderColor || '#e2e8f0') |
| 218 |
} |
| 219 |
}); |
| 220 |
append(verdictWrap, |
| 221 |
el('span', { className: 'bkbg-vb-verdict-icon', textContent: '💬' }), |
| 222 |
el('p', { className: 'bkbg-vb-verdict-text', textContent: o.verdictText || '' }) |
| 223 |
); |
| 224 |
return verdictWrap; |
| 225 |
} |
| 226 |
|
| 227 |
function buildCtas(o) { |
| 228 |
var ctasDiv = el('div', { |
| 229 |
className: 'bkbg-vb-ctas', |
| 230 |
style: { borderTop: '1px solid ' + (o.borderColor || '#e2e8f0') } |
| 231 |
}); |
| 232 |
|
| 233 |
if (o.showCta && o.ctaText) { |
| 234 |
var primary = el('a', { |
| 235 |
className: 'bkbg-vb-cta-primary', |
| 236 |
href: o.ctaUrl || '#', |
| 237 |
style: { |
| 238 |
background: o.ctaBg || '#6366f1', |
| 239 |
color: o.ctaColor || '#fff', |
| 240 |
borderRadius: (o.borderRadius || 12) + 'px' |
| 241 |
}, |
| 242 |
textContent: o.ctaText |
| 243 |
}); |
| 244 |
append(ctasDiv, primary); |
| 245 |
} |
| 246 |
|
| 247 |
if (o.showSecondaryCta && o.ctaSecondaryText) { |
| 248 |
var secondary = el('a', { |
| 249 |
className: 'bkbg-vb-cta-secondary', |
| 250 |
href: o.ctaSecondaryUrl || '#', |
| 251 |
style: { |
| 252 |
background: 'transparent', |
| 253 |
color: o.ctaSecondaryColor || '#6366f1', |
| 254 |
border: '2px solid ' + (o.ctaSecondaryColor || '#6366f1'), |
| 255 |
borderRadius: (o.borderRadius || 12) + 'px' |
| 256 |
}, |
| 257 |
textContent: o.ctaSecondaryText |
| 258 |
}); |
| 259 |
append(ctasDiv, secondary); |
| 260 |
} |
| 261 |
|
| 262 |
return ctasDiv; |
| 263 |
} |
| 264 |
|
| 265 |
function init() { |
| 266 |
document.querySelectorAll('.bkbg-verdict-box-app').forEach(function (appEl) { |
| 267 |
if (appEl.dataset.rendered) return; |
| 268 |
appEl.dataset.rendered = '1'; |
| 269 |
|
| 270 |
var opts; |
| 271 |
try { opts = JSON.parse(appEl.dataset.opts || '{}'); } catch (e) { opts = {}; } |
| 272 |
|
| 273 |
var o = Object.assign({ |
| 274 |
productName: 'Product', |
| 275 |
productTagline: '', |
| 276 |
showTagline: true, |
| 277 |
overallScore: 8.7, |
| 278 |
scoreMax: 10, |
| 279 |
scoreLabel: 'Overall Score', |
| 280 |
scoreDisplay: 'circle', |
| 281 |
badge: 'recommended', |
| 282 |
pros: [], |
| 283 |
cons: [], |
| 284 |
prosLabel: '� |
| 285 |
Pros', |
| 286 |
consLabel: '❌ Cons', |
| 287 |
verdictText: '', |
| 288 |
showVerdict: true, |
| 289 |
ctaText: '', |
| 290 |
ctaUrl: '#', |
| 291 |
showCta: false, |
| 292 |
ctaSecondaryText: '', |
| 293 |
ctaSecondaryUrl: '#', |
| 294 |
showSecondaryCta: false, |
| 295 |
layout: 'split', |
| 296 |
borderRadius: 12, |
| 297 |
fontSize: 15, |
| 298 |
productNameSize: 24, |
| 299 |
bgColor: '#fff', |
| 300 |
borderColor: '#e2e8f0', |
| 301 |
headerBg: '#0f172a', |
| 302 |
headerColor: '#fff', |
| 303 |
scoreCircleBg: '#6366f1', |
| 304 |
scoreCircleColor: '#fff', |
| 305 |
prosBg: '#f0fdf4', |
| 306 |
prosColor: '#14532d', |
| 307 |
prosIconColor: '#22c55e', |
| 308 |
consBg: '#fef2f2', |
| 309 |
consColor: '#7f1d1d', |
| 310 |
consIconColor: '#ef4444', |
| 311 |
verdictBg: '#f8fafc', |
| 312 |
verdictColor: '#374151', |
| 313 |
verdictBorderColor:'#e2e8f0', |
| 314 |
ctaBg: '#6366f1', |
| 315 |
ctaColor: '#fff', |
| 316 |
ctaSecondaryColor: '#6366f1' |
| 317 |
}, opts); |
| 318 |
|
| 319 |
var block = el('div', { |
| 320 |
className: 'bkbg-vb-block', |
| 321 |
style: { |
| 322 |
background: o.bgColor || '#fff', |
| 323 |
border: '1px solid ' + (o.borderColor || '#e2e8f0'), |
| 324 |
borderRadius: (o.borderRadius || 12) + 'px', |
| 325 |
overflow: 'hidden' |
| 326 |
} |
| 327 |
}); |
| 328 |
|
| 329 |
// Set CSS variable for responsive breakpoint border |
| 330 |
block.style.setProperty('--bkbg-vb-border', o.borderColor || '#e2e8f0'); |
| 331 |
|
| 332 |
append(block, buildHeader(o)); |
| 333 |
append(block, buildProsCons(o)); |
| 334 |
|
| 335 |
if (o.showVerdict && o.verdictText) { |
| 336 |
append(block, buildVerdict(o)); |
| 337 |
} |
| 338 |
|
| 339 |
if ((o.showCta && o.ctaText) || (o.showSecondaryCta && o.ctaSecondaryText)) { |
| 340 |
append(block, buildCtas(o)); |
| 341 |
} |
| 342 |
|
| 343 |
appEl.parentNode.insertBefore(block, appEl); |
| 344 |
appEl.style.display = 'none'; |
| 345 |
}); |
| 346 |
} |
| 347 |
|
| 348 |
if (document.readyState !== 'loading') { init(); } |
| 349 |
else { document.addEventListener('DOMContentLoaded', init); } |
| 350 |
})(); |
| 351 |
|