| 1 |
(function () { |
| 2 |
'use strict'; |
| 3 |
|
| 4 |
function initBlock(root) { |
| 5 |
var app = root.querySelector('.bkbg-psl-app'); |
| 6 |
if (!app) return; |
| 7 |
|
| 8 |
var raw = app.getAttribute('data-opts') || '{}'; |
| 9 |
var o; |
| 10 |
try { o = JSON.parse(raw); } catch (e) { o = {}; } |
| 11 |
|
| 12 |
var tiers = Array.isArray(o.tiers) ? o.tiers : []; |
| 13 |
var showBilling = o.showBilling !== false; |
| 14 |
var disc = Number(o.annualDiscount) || 20; |
| 15 |
var currency = o.currency || '$'; |
| 16 |
var period = o.period || '/month'; |
| 17 |
var ctaText = o.ctaText || 'Get Started'; |
| 18 |
var priceSize = Number(o.priceSize) || 56; |
| 19 |
var cardRadius = Number(o.cardRadius) || 24; |
| 20 |
var cardBg = o.cardBg || '#ffffff'; |
| 21 |
var headingColor = o.headingColor || '#1e1b4b'; |
| 22 |
var priceColor = o.priceColor || '#6366f1'; |
| 23 |
var featureColor = o.featureColor || '#374151'; |
| 24 |
var accentColor = o.accentColor || '#6366f1'; |
| 25 |
var ctaBg = o.ctaBg || '#6366f1'; |
| 26 |
var ctaColor = o.ctaColor || '#ffffff'; |
| 27 |
var sliderColor = o.sliderColor || '#6366f1'; |
| 28 |
var trackBg = o.trackBg || '#e5e7eb'; |
| 29 |
var checkColor = o.checkColor || '#6366f1'; |
| 30 |
|
| 31 |
var _typoKeys = { |
| 32 |
headingTypo: '--bkbg-psl-hd-', |
| 33 |
subheadingTypo: '--bkbg-psl-sh-', |
| 34 |
priceTypo: '--bkbg-psl-pr-', |
| 35 |
ctaTypo: '--bkbg-psl-ct-', |
| 36 |
featureTypo: '--bkbg-psl-ft-' |
| 37 |
}; |
| 38 |
function typoCssVarsForEl(el) { |
| 39 |
Object.keys(_typoKeys).forEach(function (attr) { |
| 40 |
var t = o[attr]; if (!t || typeof t !== 'object') return; |
| 41 |
var p = _typoKeys[attr]; |
| 42 |
if (t.family) el.style.setProperty(p + 'font-family', t.family); |
| 43 |
if (t.weight) el.style.setProperty(p + 'font-weight', t.weight); |
| 44 |
if (t.transform) el.style.setProperty(p + 'text-transform', t.transform); |
| 45 |
if (t.style) el.style.setProperty(p + 'font-style', t.style); |
| 46 |
if (t.decoration) el.style.setProperty(p + 'text-decoration', t.decoration); |
| 47 |
if (t.sizeDesktop) el.style.setProperty(p + 'font-size-d', t.sizeDesktop + (t.sizeUnit || 'px')); |
| 48 |
if (t.sizeTablet) el.style.setProperty(p + 'font-size-t', t.sizeTablet + (t.sizeUnit || 'px')); |
| 49 |
if (t.sizeMobile) el.style.setProperty(p + 'font-size-m', t.sizeMobile + (t.sizeUnit || 'px')); |
| 50 |
if (t.lineHeightDesktop) el.style.setProperty(p + 'line-height-d', t.lineHeightDesktop + (t.lineHeightUnit || '')); |
| 51 |
if (t.lineHeightTablet) el.style.setProperty(p + 'line-height-t', t.lineHeightTablet + (t.lineHeightUnit || '')); |
| 52 |
if (t.lineHeightMobile) el.style.setProperty(p + 'line-height-m', t.lineHeightMobile + (t.lineHeightUnit || '')); |
| 53 |
if (t.letterSpacingDesktop) el.style.setProperty(p + 'letter-spacing-d', t.letterSpacingDesktop + (t.letterSpacingUnit || 'px')); |
| 54 |
if (t.letterSpacingTablet) el.style.setProperty(p + 'letter-spacing-t', t.letterSpacingTablet + (t.letterSpacingUnit || 'px')); |
| 55 |
if (t.letterSpacingMobile) el.style.setProperty(p + 'letter-spacing-m', t.letterSpacingMobile + (t.letterSpacingUnit || 'px')); |
| 56 |
if (t.wordSpacingDesktop) el.style.setProperty(p + 'word-spacing-d', t.wordSpacingDesktop + (t.wordSpacingUnit || 'px')); |
| 57 |
if (t.wordSpacingTablet) el.style.setProperty(p + 'word-spacing-t', t.wordSpacingTablet + (t.wordSpacingUnit || 'px')); |
| 58 |
if (t.wordSpacingMobile) el.style.setProperty(p + 'word-spacing-m', t.wordSpacingMobile + (t.wordSpacingUnit || 'px')); |
| 59 |
}); |
| 60 |
} |
| 61 |
|
| 62 |
if (tiers.length === 0) return; |
| 63 |
|
| 64 |
var isAnnual = false; |
| 65 |
var tierIdx = 0; |
| 66 |
|
| 67 |
// Set CSS variable for slider accent |
| 68 |
app.style.setProperty('--bkbg-psl-accent', accentColor); |
| 69 |
typoCssVarsForEl(root); |
| 70 |
|
| 71 |
// Build full UI |
| 72 |
app.innerHTML = ''; |
| 73 |
var header = app.querySelector('.bkbg-psl-header'); |
| 74 |
// Header was saved as a child — re-append it |
| 75 |
// Actually we need to rebuild since we cleared innerHTML |
| 76 |
// The heading/subheading came from RichText.Content in save(), reconstructed via .bkbg-psl-header |
| 77 |
// Since we cleared, let's rebuild: |
| 78 |
|
| 79 |
// ── Header (rebuilt from opts) ────────────────────────────────────── |
| 80 |
// The header HTML was already rendered by WP; we just need the dynamic parts. |
| 81 |
// Re-read saved heading from the DOM before clearing |
| 82 |
var savedHeader = (function () { |
| 83 |
var tmp = document.createElement('div'); |
| 84 |
tmp.innerHTML = app.innerHTML; // empty now, so restore manually |
| 85 |
return null; |
| 86 |
})(); |
| 87 |
|
| 88 |
// Re-build everything programmatically from opts |
| 89 |
// (heading / subheading text not stored in opts — they're in the HTML already) |
| 90 |
// Approach: clone header before clearing |
| 91 |
var origHTML = app.parentElement ? null : null; |
| 92 |
|
| 93 |
// Better approach: append header back first |
| 94 |
// We can't — we already cleared. Let's read data from pre-built DOM instead. |
| 95 |
// Fix: read the header BEFORE clearing. |
| 96 |
|
| 97 |
// ── RESTART: read DOM before clear ───────────────────────────────── |
| 98 |
// This is handled by re-initializing cleanly without clearing the header. |
| 99 |
// We keep .bkbg-psl-header and append the rest below it. |
| 100 |
app.innerHTML = ''; // already done above, rebuild from scratch |
| 101 |
|
| 102 |
// Heading row (recreate from opts — note: rich text is lost; use a placeholder if undefined) |
| 103 |
var headingEl = document.createElement('div'); |
| 104 |
headingEl.className = 'bkbg-psl-header'; |
| 105 |
|
| 106 |
// Billing toggle |
| 107 |
var billingEl = null; |
| 108 |
if (showBilling) { |
| 109 |
billingEl = document.createElement('div'); |
| 110 |
billingEl.className = 'bkbg-psl-billing'; |
| 111 |
|
| 112 |
var lblMonthly = document.createElement('span'); |
| 113 |
lblMonthly.className = 'bkbg-psl-label-monthly'; |
| 114 |
lblMonthly.textContent = 'Monthly'; |
| 115 |
lblMonthly.style.fontWeight = '700'; |
| 116 |
lblMonthly.style.color = headingColor; |
| 117 |
|
| 118 |
var toggleWrap = document.createElement('div'); |
| 119 |
toggleWrap.className = 'bkbg-psl-toggle-wrap'; |
| 120 |
toggleWrap.style.background = '#d1d5db'; |
| 121 |
toggleWrap.setAttribute('role', 'switch'); |
| 122 |
toggleWrap.setAttribute('aria-checked', 'false'); |
| 123 |
toggleWrap.setAttribute('tabindex', '0'); |
| 124 |
|
| 125 |
var knob = document.createElement('div'); |
| 126 |
knob.className = 'bkbg-psl-toggle-knob'; |
| 127 |
toggleWrap.appendChild(knob); |
| 128 |
|
| 129 |
var lblAnnual = document.createElement('span'); |
| 130 |
lblAnnual.className = 'bkbg-psl-label-annual'; |
| 131 |
lblAnnual.style.color = headingColor; |
| 132 |
lblAnnual.innerHTML = 'Annual <span class="bkbg-psl-save-badge">-' + disc + '%</span>'; |
| 133 |
|
| 134 |
billingEl.appendChild(lblMonthly); |
| 135 |
billingEl.appendChild(toggleWrap); |
| 136 |
billingEl.appendChild(lblAnnual); |
| 137 |
|
| 138 |
function setBilling(annual) { |
| 139 |
isAnnual = annual; |
| 140 |
if (annual) { |
| 141 |
toggleWrap.classList.add('bkbg-psl-on'); |
| 142 |
toggleWrap.style.background = accentColor; |
| 143 |
toggleWrap.setAttribute('aria-checked', 'true'); |
| 144 |
lblMonthly.style.fontWeight = '400'; |
| 145 |
lblAnnual.style.fontWeight = '700'; |
| 146 |
} else { |
| 147 |
toggleWrap.classList.remove('bkbg-psl-on'); |
| 148 |
toggleWrap.style.background = '#d1d5db'; |
| 149 |
toggleWrap.setAttribute('aria-checked', 'false'); |
| 150 |
lblMonthly.style.fontWeight = '700'; |
| 151 |
lblAnnual.style.fontWeight = '400'; |
| 152 |
} |
| 153 |
updateCard(); |
| 154 |
} |
| 155 |
toggleWrap.addEventListener('click', function () { setBilling(!isAnnual); }); |
| 156 |
toggleWrap.addEventListener('keydown', function (e) { |
| 157 |
if (e.key === 'Enter' || e.key === ' ') { setBilling(!isAnnual); } |
| 158 |
}); |
| 159 |
} |
| 160 |
|
| 161 |
// Slider |
| 162 |
var sliderWrap = document.createElement('div'); |
| 163 |
sliderWrap.className = 'bkbg-psl-slider-wrap'; |
| 164 |
|
| 165 |
var labelsRow = document.createElement('div'); |
| 166 |
labelsRow.className = 'bkbg-psl-labels'; |
| 167 |
var labelEls = tiers.map(function (t, i) { |
| 168 |
var span = document.createElement('span'); |
| 169 |
span.className = 'bkbg-psl-label' + (i === 0 ? ' bkbg-psl-active' : ''); |
| 170 |
span.textContent = t.label || ''; |
| 171 |
span.style.color = i === 0 ? accentColor : ''; |
| 172 |
span.addEventListener('click', function () { setTierIdx(i); range.value = i; }); |
| 173 |
return span; |
| 174 |
}); |
| 175 |
labelEls.forEach(function (el) { labelsRow.appendChild(el); }); |
| 176 |
|
| 177 |
var range = document.createElement('input'); |
| 178 |
range.type = 'range'; |
| 179 |
range.min = 0; |
| 180 |
range.max = tiers.length - 1; |
| 181 |
range.step = 1; |
| 182 |
range.value = 0; |
| 183 |
range.className = 'bkbg-psl-range'; |
| 184 |
// Track gradient |
| 185 |
function updateRangeTrack() { |
| 186 |
var pct = (tierIdx / (tiers.length - 1)) * 100; |
| 187 |
range.style.background = 'linear-gradient(to right,' + sliderColor + ' 0%,' + sliderColor + ' ' + pct + '%,' + trackBg + ' ' + pct + '%,' + trackBg + ' 100%)'; |
| 188 |
} |
| 189 |
|
| 190 |
sliderWrap.appendChild(labelsRow); |
| 191 |
sliderWrap.appendChild(range); |
| 192 |
|
| 193 |
// Card |
| 194 |
var card = document.createElement('div'); |
| 195 |
card.className = 'bkbg-psl-card'; |
| 196 |
card.style.background = cardBg; |
| 197 |
card.style.borderRadius = cardRadius + 'px'; |
| 198 |
card.style.border = '2px solid ' + accentColor; |
| 199 |
card.style.boxShadow = '0 4px 24px rgba(0,0,0,0.08)'; |
| 200 |
|
| 201 |
var tierName = document.createElement('div'); tierName.className = 'bkbg-psl-tier-name'; tierName.style.color = accentColor; |
| 202 |
var tierUsers = document.createElement('div'); tierUsers.className = 'bkbg-psl-tier-users'; |
| 203 |
var priceRow = document.createElement('div'); priceRow.className = 'bkbg-psl-price-row'; |
| 204 |
var currencyEl = document.createElement('span'); currencyEl.className = 'bkbg-psl-currency'; currencyEl.textContent = currency; currencyEl.style.color = priceColor; |
| 205 |
var amountEl = document.createElement('span'); amountEl.className = 'bkbg-psl-amount'; amountEl.style.color = priceColor; |
| 206 |
var periodEl = document.createElement('span'); periodEl.className = 'bkbg-psl-period'; periodEl.textContent = period; |
| 207 |
var annualNote = document.createElement('div'); annualNote.className = 'bkbg-psl-annual-note'; |
| 208 |
var featureList = document.createElement('ul'); featureList.className = 'bkbg-psl-features'; |
| 209 |
var ctaBtn = document.createElement('button');ctaBtn.className = 'bkbg-psl-cta'; ctaBtn.textContent = ctaText; |
| 210 |
ctaBtn.style.background = ctaBg; ctaBtn.style.color = ctaColor; |
| 211 |
|
| 212 |
priceRow.appendChild(currencyEl); |
| 213 |
priceRow.appendChild(amountEl); |
| 214 |
priceRow.appendChild(periodEl); |
| 215 |
card.appendChild(tierName); |
| 216 |
card.appendChild(tierUsers); |
| 217 |
card.appendChild(priceRow); |
| 218 |
card.appendChild(annualNote); |
| 219 |
card.appendChild(featureList); |
| 220 |
card.appendChild(ctaBtn); |
| 221 |
|
| 222 |
function setTierIdx(i) { |
| 223 |
tierIdx = i; |
| 224 |
labelEls.forEach(function (el, idx) { |
| 225 |
if (idx === i) { |
| 226 |
el.classList.add('bkbg-psl-active'); |
| 227 |
el.style.color = accentColor; |
| 228 |
el.style.fontWeight = '700'; |
| 229 |
} else { |
| 230 |
el.classList.remove('bkbg-psl-active'); |
| 231 |
el.style.color = ''; |
| 232 |
el.style.fontWeight = ''; |
| 233 |
} |
| 234 |
}); |
| 235 |
updateRangeTrack(); |
| 236 |
updateCard(); |
| 237 |
} |
| 238 |
|
| 239 |
function updateCard() { |
| 240 |
var t = tiers[tierIdx] || {}; |
| 241 |
var rawPrice = Number(t.monthlyPrice) || 0; |
| 242 |
var price = isAnnual ? Math.round(rawPrice * (1 - disc / 100)) : rawPrice; |
| 243 |
|
| 244 |
tierName.textContent = t.label || ''; |
| 245 |
tierUsers.textContent = t.users || ''; |
| 246 |
|
| 247 |
// Animate price change |
| 248 |
amountEl.classList.add('bkbg-psl-updating'); |
| 249 |
setTimeout(function () { |
| 250 |
amountEl.textContent = price; |
| 251 |
amountEl.classList.remove('bkbg-psl-updating'); |
| 252 |
}, 150); |
| 253 |
|
| 254 |
annualNote.textContent = isAnnual ? 'Billed annually — save ' + disc + '%' : ''; |
| 255 |
|
| 256 |
// Features |
| 257 |
featureList.innerHTML = ''; |
| 258 |
(t.features || []).forEach(function (f) { |
| 259 |
var li = document.createElement('li'); |
| 260 |
li.style.color = featureColor; |
| 261 |
var check = document.createElement('span'); |
| 262 |
check.className = 'bkbg-psl-check'; |
| 263 |
check.textContent = '✓'; |
| 264 |
check.style.color = checkColor; |
| 265 |
li.appendChild(check); |
| 266 |
li.appendChild(document.createTextNode(f)); |
| 267 |
featureList.appendChild(li); |
| 268 |
}); |
| 269 |
} |
| 270 |
|
| 271 |
range.addEventListener('input', function () { |
| 272 |
setTierIdx(Number(this.value)); |
| 273 |
}); |
| 274 |
|
| 275 |
// Assemble |
| 276 |
if (billingEl) app.appendChild(billingEl); |
| 277 |
app.appendChild(sliderWrap); |
| 278 |
app.appendChild(card); |
| 279 |
|
| 280 |
// Init |
| 281 |
setTierIdx(0); |
| 282 |
updateRangeTrack(); |
| 283 |
} |
| 284 |
|
| 285 |
function init() { |
| 286 |
document.querySelectorAll('.wp-block-blockenberg-pricing-slider').forEach(initBlock); |
| 287 |
} |
| 288 |
|
| 289 |
if (document.readyState === 'loading') { |
| 290 |
document.addEventListener('DOMContentLoaded', init); |
| 291 |
} else { |
| 292 |
init(); |
| 293 |
} |
| 294 |
})(); |
| 295 |
|