| 1 |
(function () { |
| 2 |
function init() { |
| 3 |
document.querySelectorAll('.bkbg-ps-app').forEach(function (appEl) { |
| 4 |
if (appEl.dataset.rendered) return; |
| 5 |
appEl.dataset.rendered = '1'; |
| 6 |
|
| 7 |
var opts; |
| 8 |
try { opts = JSON.parse(appEl.dataset.opts || '{}'); } catch (e) { opts = {}; } |
| 9 |
var o = Object.assign({ |
| 10 |
plans: [], |
| 11 |
showToggle: true, |
| 12 |
toggleLabelMonthly: 'Monthly', |
| 13 |
toggleLabelYearly: 'Yearly', |
| 14 |
savingsBadge: 'Save 25%', |
| 15 |
showSavingsBadge: true, |
| 16 |
defaultYearly: false, |
| 17 |
sectionHeading: '', |
| 18 |
showSectionHeading: true, |
| 19 |
sectionSubtext: '', |
| 20 |
showSectionSubtext: true, |
| 21 |
headingTag: 'h2', |
| 22 |
columns: 3, |
| 23 |
gap: 24, |
| 24 |
maxWidth: 1100, |
| 25 |
containerPadding: 48, |
| 26 |
cardRadius: 16, |
| 27 |
cardPadding: 32, |
| 28 |
cardStyle: 'elevated', |
| 29 |
showEyebrow: true, |
| 30 |
showDescription: true, |
| 31 |
showFeatures: true, |
| 32 |
includedIcon: '✓', |
| 33 |
notIncludedIcon: '✗', |
| 34 |
currencySymbol: '$', |
| 35 |
nameSize: 20, |
| 36 |
priceSize: 48, |
| 37 |
descSize: 14, |
| 38 |
featureSize: 14, |
| 39 |
containerBg: '', |
| 40 |
cardBg: '#ffffff', |
| 41 |
popularBg: '#6366f1', |
| 42 |
nameColor: '#111827', |
| 43 |
priceColor: '#111827', |
| 44 |
periodColor: '#6b7280', |
| 45 |
descColor: '#6b7280', |
| 46 |
featureColor: '#374151', |
| 47 |
featureMissingColor: '#d1d5db', |
| 48 |
includedIconColor: '#10b981', |
| 49 |
popularNameColor: '#ffffff', |
| 50 |
popularPriceColor: '#ffffff', |
| 51 |
popularBadgeBg: 'rgba(255,255,255,0.2)', |
| 52 |
popularBadgeColor: '#ffffff', |
| 53 |
toggleBg: '#f3f4f6', |
| 54 |
toggleActiveBg: '#6366f1', |
| 55 |
toggleActiveColor: '#ffffff', |
| 56 |
toggleInactiveColor: '#6b7280', |
| 57 |
headingColor: '#111827', |
| 58 |
subtextColor: '#6b7280', |
| 59 |
savingsBadgeBg: '#dcfce7', |
| 60 |
savingsBadgeColor: '#166534', |
| 61 |
}, opts); |
| 62 |
|
| 63 |
var _typoKeys = { |
| 64 |
headingTypo: '--bkbg-ps-hd-', |
| 65 |
subtextTypo: '--bkbg-ps-st-', |
| 66 |
nameTypo: '--bkbg-ps-nm-', |
| 67 |
priceTypo: '--bkbg-ps-pr-', |
| 68 |
descTypo: '--bkbg-ps-ds-', |
| 69 |
featureTypo: '--bkbg-ps-ft-' |
| 70 |
}; |
| 71 |
function typoCssVarsForEl(el) { |
| 72 |
Object.keys(_typoKeys).forEach(function (attr) { |
| 73 |
var t = o[attr]; if (!t || typeof t !== 'object') return; |
| 74 |
var p = _typoKeys[attr]; |
| 75 |
if (t.family) el.style.setProperty(p + 'font-family', t.family); |
| 76 |
if (t.weight) el.style.setProperty(p + 'font-weight', t.weight); |
| 77 |
if (t.transform) el.style.setProperty(p + 'text-transform', t.transform); |
| 78 |
if (t.style) el.style.setProperty(p + 'font-style', t.style); |
| 79 |
if (t.decoration) el.style.setProperty(p + 'text-decoration', t.decoration); |
| 80 |
if (t.sizeDesktop) el.style.setProperty(p + 'font-size-d', t.sizeDesktop + (t.sizeUnit || 'px')); |
| 81 |
if (t.sizeTablet) el.style.setProperty(p + 'font-size-t', t.sizeTablet + (t.sizeUnit || 'px')); |
| 82 |
if (t.sizeMobile) el.style.setProperty(p + 'font-size-m', t.sizeMobile + (t.sizeUnit || 'px')); |
| 83 |
if (t.lineHeightDesktop) el.style.setProperty(p + 'line-height-d', t.lineHeightDesktop + (t.lineHeightUnit || '')); |
| 84 |
if (t.lineHeightTablet) el.style.setProperty(p + 'line-height-t', t.lineHeightTablet + (t.lineHeightUnit || '')); |
| 85 |
if (t.lineHeightMobile) el.style.setProperty(p + 'line-height-m', t.lineHeightMobile + (t.lineHeightUnit || '')); |
| 86 |
if (t.letterSpacingDesktop) el.style.setProperty(p + 'letter-spacing-d', t.letterSpacingDesktop + (t.letterSpacingUnit || 'px')); |
| 87 |
if (t.letterSpacingTablet) el.style.setProperty(p + 'letter-spacing-t', t.letterSpacingTablet + (t.letterSpacingUnit || 'px')); |
| 88 |
if (t.letterSpacingMobile) el.style.setProperty(p + 'letter-spacing-m', t.letterSpacingMobile + (t.letterSpacingUnit || 'px')); |
| 89 |
if (t.wordSpacingDesktop) el.style.setProperty(p + 'word-spacing-d', t.wordSpacingDesktop + (t.wordSpacingUnit || 'px')); |
| 90 |
if (t.wordSpacingTablet) el.style.setProperty(p + 'word-spacing-t', t.wordSpacingTablet + (t.wordSpacingUnit || 'px')); |
| 91 |
if (t.wordSpacingMobile) el.style.setProperty(p + 'word-spacing-m', t.wordSpacingMobile + (t.wordSpacingUnit || 'px')); |
| 92 |
}); |
| 93 |
} |
| 94 |
|
| 95 |
if (!o.plans || !o.plans.length) return; |
| 96 |
|
| 97 |
var isYearly = o.defaultYearly; |
| 98 |
|
| 99 |
// Root wrapper |
| 100 |
var wrap = document.createElement('div'); |
| 101 |
wrap.className = 'bkbg-ps-wrap'; |
| 102 |
if (o.containerBg) wrap.style.background = o.containerBg; |
| 103 |
wrap.style.paddingTop = o.containerPadding + 'px'; |
| 104 |
wrap.style.paddingBottom = o.containerPadding + 'px'; |
| 105 |
|
| 106 |
var inner = document.createElement('div'); |
| 107 |
inner.className = 'bkbg-ps-inner'; |
| 108 |
inner.style.maxWidth = o.maxWidth + 'px'; |
| 109 |
wrap.appendChild(inner); |
| 110 |
|
| 111 |
// Header |
| 112 |
var showHeader = o.showSectionHeading || o.showSectionSubtext || o.showToggle; |
| 113 |
var header = null; |
| 114 |
if (showHeader) { |
| 115 |
header = document.createElement('div'); |
| 116 |
header.className = 'bkbg-ps-header'; |
| 117 |
|
| 118 |
if (o.showSectionHeading && o.sectionHeading) { |
| 119 |
var heading = document.createElement(o.headingTag || 'h2'); |
| 120 |
heading.className = 'bkbg-ps-heading'; |
| 121 |
heading.style.color = o.headingColor; |
| 122 |
heading.textContent = o.sectionHeading; |
| 123 |
header.appendChild(heading); |
| 124 |
} |
| 125 |
|
| 126 |
if (o.showSectionSubtext && o.sectionSubtext) { |
| 127 |
var sub = document.createElement('p'); |
| 128 |
sub.className = 'bkbg-ps-subtext'; |
| 129 |
sub.style.color = o.subtextColor; |
| 130 |
sub.textContent = o.sectionSubtext; |
| 131 |
header.appendChild(sub); |
| 132 |
} |
| 133 |
|
| 134 |
if (o.showToggle) { |
| 135 |
var toggleWrap = document.createElement('div'); |
| 136 |
toggleWrap.className = 'bkbg-ps-toggle-wrap'; |
| 137 |
toggleWrap.style.background = o.toggleBg; |
| 138 |
|
| 139 |
var btnMonthly = document.createElement('button'); |
| 140 |
var btnYearly = document.createElement('button'); |
| 141 |
btnMonthly.className = 'bkbg-ps-toggle-btn'; |
| 142 |
btnYearly.className = 'bkbg-ps-toggle-btn'; |
| 143 |
btnMonthly.textContent = o.toggleLabelMonthly; |
| 144 |
|
| 145 |
// Yearly button with optional savings badge |
| 146 |
btnYearly.textContent = o.toggleLabelYearly; |
| 147 |
if (o.showSavingsBadge && o.savingsBadge) { |
| 148 |
var badge = document.createElement('span'); |
| 149 |
badge.className = 'bkbg-ps-savings-badge'; |
| 150 |
badge.style.background = o.savingsBadgeBg; |
| 151 |
badge.style.color = o.savingsBadgeColor; |
| 152 |
badge.textContent = o.savingsBadge; |
| 153 |
btnYearly.appendChild(badge); |
| 154 |
} |
| 155 |
|
| 156 |
function applyToggleStyles() { |
| 157 |
btnMonthly.style.background = !isYearly ? o.toggleActiveBg : 'transparent'; |
| 158 |
btnMonthly.style.color = !isYearly ? o.toggleActiveColor : o.toggleInactiveColor; |
| 159 |
btnYearly.style.background = isYearly ? o.toggleActiveBg : 'transparent'; |
| 160 |
btnYearly.style.color = isYearly ? o.toggleActiveColor : o.toggleInactiveColor; |
| 161 |
} |
| 162 |
|
| 163 |
applyToggleStyles(); |
| 164 |
|
| 165 |
btnMonthly.addEventListener('click', function () { |
| 166 |
if (isYearly) { isYearly = false; applyToggleStyles(); updatePrices(); } |
| 167 |
}); |
| 168 |
btnYearly.addEventListener('click', function () { |
| 169 |
if (!isYearly) { isYearly = true; applyToggleStyles(); updatePrices(); } |
| 170 |
}); |
| 171 |
|
| 172 |
toggleWrap.appendChild(btnMonthly); |
| 173 |
toggleWrap.appendChild(btnYearly); |
| 174 |
header.appendChild(toggleWrap); |
| 175 |
} |
| 176 |
|
| 177 |
inner.appendChild(header); |
| 178 |
} |
| 179 |
|
| 180 |
// Plans grid |
| 181 |
var grid = document.createElement('div'); |
| 182 |
grid.className = 'bkbg-ps-grid'; |
| 183 |
grid.style.gridTemplateColumns = 'repeat(' + Math.min(o.columns, o.plans.length) + ', 1fr)'; |
| 184 |
grid.style.gap = o.gap + 'px'; |
| 185 |
inner.appendChild(grid); |
| 186 |
|
| 187 |
// Price els for toggle update |
| 188 |
var priceEls = []; |
| 189 |
var periodEls = []; |
| 190 |
|
| 191 |
o.plans.forEach(function (plan, i) { |
| 192 |
var isPopular = plan.isPopular; |
| 193 |
var cardBg = isPopular ? o.popularBg : o.cardBg; |
| 194 |
var nameColor = isPopular ? o.popularNameColor : o.nameColor; |
| 195 |
var priceColor = isPopular ? o.popularPriceColor : o.priceColor; |
| 196 |
var descColor = isPopular ? 'rgba(255,255,255,0.75)' : o.descColor; |
| 197 |
var featureColor = isPopular ? 'rgba(255,255,255,0.9)' : o.featureColor; |
| 198 |
var missingColor = isPopular ? 'rgba(255,255,255,0.3)' : o.featureMissingColor; |
| 199 |
var periodColor = isPopular ? 'rgba(255,255,255,0.6)' : o.periodColor; |
| 200 |
var dividerColor = isPopular ? 'rgba(255,255,255,0.15)' : '#f3f4f6'; |
| 201 |
|
| 202 |
var card = document.createElement('div'); |
| 203 |
card.className = 'bkbg-ps-card style-' + o.cardStyle + (isPopular ? ' is-popular' : ''); |
| 204 |
card.style.background = cardBg; |
| 205 |
card.style.borderRadius = o.cardRadius + 'px'; |
| 206 |
card.style.padding = o.cardPadding + 'px'; |
| 207 |
|
| 208 |
// Popular badge |
| 209 |
if (isPopular && o.showEyebrow) { |
| 210 |
var popBadge = document.createElement('div'); |
| 211 |
popBadge.className = 'bkbg-ps-popular-badge'; |
| 212 |
popBadge.style.background = o.popularBadgeBg; |
| 213 |
popBadge.style.color = o.popularBadgeColor; |
| 214 |
popBadge.textContent = plan.popularLabel || 'Most Popular'; |
| 215 |
card.appendChild(popBadge); |
| 216 |
} else if (!isPopular && o.showEyebrow && plan.eyebrow) { |
| 217 |
var eyebrow = document.createElement('div'); |
| 218 |
eyebrow.className = 'bkbg-ps-eyebrow'; |
| 219 |
eyebrow.style.color = o.periodColor; |
| 220 |
eyebrow.textContent = plan.eyebrow; |
| 221 |
card.appendChild(eyebrow); |
| 222 |
} |
| 223 |
|
| 224 |
// Name |
| 225 |
var nameEl = document.createElement('div'); |
| 226 |
nameEl.className = 'bkbg-ps-name'; |
| 227 |
nameEl.style.color = nameColor; |
| 228 |
nameEl.textContent = plan.name; |
| 229 |
card.appendChild(nameEl); |
| 230 |
|
| 231 |
// Price row |
| 232 |
var priceRow = document.createElement('div'); |
| 233 |
priceRow.className = 'bkbg-ps-price-row'; |
| 234 |
|
| 235 |
var currencyEl = document.createElement('span'); |
| 236 |
currencyEl.className = 'bkbg-ps-currency'; |
| 237 |
currencyEl.style.color = priceColor; |
| 238 |
currencyEl.textContent = o.currencySymbol; |
| 239 |
|
| 240 |
var priceEl = document.createElement('span'); |
| 241 |
priceEl.className = 'bkbg-ps-price'; |
| 242 |
priceEl.style.color = priceColor; |
| 243 |
priceEl.textContent = isYearly ? plan.priceYearly : plan.price; |
| 244 |
priceEls.push({ el: priceEl, plan: plan }); |
| 245 |
|
| 246 |
var periodEl = document.createElement('span'); |
| 247 |
periodEl.className = 'bkbg-ps-period'; |
| 248 |
periodEl.style.color = periodColor; |
| 249 |
periodEl.textContent = isYearly ? plan.yearlyPeriod : plan.period; |
| 250 |
periodEls.push({ el: periodEl, plan: plan }); |
| 251 |
|
| 252 |
priceRow.appendChild(currencyEl); |
| 253 |
priceRow.appendChild(priceEl); |
| 254 |
priceRow.appendChild(periodEl); |
| 255 |
card.appendChild(priceRow); |
| 256 |
|
| 257 |
// Description |
| 258 |
if (o.showDescription && plan.description) { |
| 259 |
var desc = document.createElement('p'); |
| 260 |
desc.className = 'bkbg-ps-desc'; |
| 261 |
desc.style.color = descColor; |
| 262 |
desc.textContent = plan.description; |
| 263 |
card.appendChild(desc); |
| 264 |
} |
| 265 |
|
| 266 |
// CTA |
| 267 |
var ctaEl = document.createElement('a'); |
| 268 |
ctaEl.className = 'bkbg-ps-cta style-' + (plan.ctaStyle || 'filled'); |
| 269 |
ctaEl.href = plan.ctaUrl || '#'; |
| 270 |
ctaEl.textContent = plan.ctaLabel || 'Get started'; |
| 271 |
|
| 272 |
var ctaBg = 'transparent'; |
| 273 |
var ctaColor = o.popularBg; |
| 274 |
var ctaBorder = 'none'; |
| 275 |
|
| 276 |
if (plan.ctaStyle === 'filled') { |
| 277 |
ctaBg = isPopular ? '#ffffff' : o.popularBg; |
| 278 |
ctaColor = isPopular ? o.popularBg : '#ffffff'; |
| 279 |
} else if (plan.ctaStyle === 'outline') { |
| 280 |
ctaBorder = '2px solid ' + (isPopular ? 'rgba(255,255,255,0.5)' : o.popularBg); |
| 281 |
ctaColor = isPopular ? '#ffffff' : o.popularBg; |
| 282 |
} else if (plan.ctaStyle === 'subtle') { |
| 283 |
ctaBg = isPopular ? 'rgba(255,255,255,0.15)' : o.popularBg + '18'; |
| 284 |
ctaColor = isPopular ? '#ffffff' : o.popularBg; |
| 285 |
} else if (plan.ctaStyle === 'link') { |
| 286 |
ctaColor = isPopular ? '#ffffff' : o.popularBg; |
| 287 |
} |
| 288 |
|
| 289 |
ctaEl.style.background = ctaBg; |
| 290 |
ctaEl.style.color = ctaColor; |
| 291 |
ctaEl.style.border = ctaBorder; |
| 292 |
|
| 293 |
card.appendChild(ctaEl); |
| 294 |
|
| 295 |
// Features |
| 296 |
if (o.showFeatures && plan.features && plan.features.length) { |
| 297 |
var divider = document.createElement('div'); |
| 298 |
divider.className = 'bkbg-ps-divider'; |
| 299 |
divider.style.background = dividerColor; |
| 300 |
card.appendChild(divider); |
| 301 |
|
| 302 |
var featList = document.createElement('div'); |
| 303 |
featList.className = 'bkbg-ps-features'; |
| 304 |
|
| 305 |
plan.features.forEach(function (feat) { |
| 306 |
var featRow = document.createElement('div'); |
| 307 |
featRow.className = 'bkbg-ps-feature'; |
| 308 |
|
| 309 |
var iconEl = document.createElement('span'); |
| 310 |
iconEl.className = 'bkbg-ps-feat-icon'; |
| 311 |
iconEl.style.color = feat.included ? o.includedIconColor : missingColor; |
| 312 |
var _IP = window.bkbgIconPicker; |
| 313 |
var _it = feat.included ? (o.includedIconType || 'custom-char') : (o.notIncludedIconType || 'custom-char'); |
| 314 |
var _ic = feat.included ? o.includedIcon : o.notIncludedIcon; |
| 315 |
var _id = feat.included ? o.includedIconDashicon : o.notIncludedIconDashicon; |
| 316 |
var _iu = feat.included ? o.includedIconImageUrl : o.notIncludedIconImageUrl; |
| 317 |
var _idc = feat.included ? o.includedIconDashiconColor : o.notIncludedIconDashiconColor; |
| 318 |
if (_IP && _it !== 'custom-char') { |
| 319 |
var _in = _IP.buildFrontendIcon(_it, _ic, _id, _iu, _idc); |
| 320 |
if (_in) iconEl.appendChild(_in); |
| 321 |
else iconEl.textContent = _ic; |
| 322 |
} else { |
| 323 |
iconEl.textContent = _ic; |
| 324 |
} |
| 325 |
|
| 326 |
var textEl = document.createElement('span'); |
| 327 |
textEl.className = 'bkbg-ps-feat-text'; |
| 328 |
textEl.style.color = feat.included ? featureColor : missingColor; |
| 329 |
textEl.textContent = feat.text; |
| 330 |
|
| 331 |
featRow.appendChild(iconEl); |
| 332 |
featRow.appendChild(textEl); |
| 333 |
featList.appendChild(featRow); |
| 334 |
}); |
| 335 |
|
| 336 |
card.appendChild(featList); |
| 337 |
} |
| 338 |
|
| 339 |
grid.appendChild(card); |
| 340 |
}); |
| 341 |
|
| 342 |
// Price update on toggle |
| 343 |
function updatePrices() { |
| 344 |
priceEls.forEach(function (item) { |
| 345 |
item.el.textContent = isYearly ? item.plan.priceYearly : item.plan.price; |
| 346 |
}); |
| 347 |
periodEls.forEach(function (item) { |
| 348 |
item.el.textContent = isYearly ? item.plan.yearlyPeriod : item.plan.period; |
| 349 |
}); |
| 350 |
} |
| 351 |
|
| 352 |
appEl.parentNode.insertBefore(wrap, appEl); |
| 353 |
typoCssVarsForEl(wrap); |
| 354 |
appEl.style.display = 'none'; |
| 355 |
}); |
| 356 |
} |
| 357 |
|
| 358 |
if (document.readyState !== 'loading') { init(); } |
| 359 |
else { document.addEventListener('DOMContentLoaded', init); } |
| 360 |
})(); |
| 361 |
|