| 1 |
(function () { |
| 2 |
const payload = window.kingAddonsAgeGate; |
| 3 |
|
| 4 |
if (!payload || !payload.enabled) { |
| 5 |
return; |
| 6 |
} |
| 7 |
|
| 8 |
const root = ensureRoot(); |
| 9 |
const card = root.querySelector('.king-addons-age-gate__card'); |
| 10 |
const content = root.querySelector('.king-addons-age-gate__content'); |
| 11 |
const state = { |
| 12 |
status: payload.status || '', |
| 13 |
cookieDays: resolveCookieDays(payload), |
| 14 |
}; |
| 15 |
|
| 16 |
applyDesign(root, card, payload); |
| 17 |
|
| 18 |
const needsBlock = payload.behaviour && payload.behaviour.denyAction === 'block' && state.status === 'denied'; |
| 19 |
|
| 20 |
if (!payload.shouldRender && !needsBlock) { |
| 21 |
return; |
| 22 |
} |
| 23 |
|
| 24 |
lockScroll(); |
| 25 |
showOverlay(root, payload); |
| 26 |
|
| 27 |
if (payload.elementorTemplate) { |
| 28 |
const templateWrapper = document.createElement('div'); |
| 29 |
templateWrapper.className = 'king-addons-age-gate__template'; |
| 30 |
templateWrapper.innerHTML = payload.elementorTemplate; |
| 31 |
content.appendChild(templateWrapper); |
| 32 |
} |
| 33 |
|
| 34 |
if (needsBlock) { |
| 35 |
renderBlocked(content, payload); |
| 36 |
return; |
| 37 |
} |
| 38 |
|
| 39 |
if (payload.mode === 'dob' && payload.isPremium) { |
| 40 |
renderDob(content, payload, state); |
| 41 |
} else { |
| 42 |
renderYesNo(content, payload, state); |
| 43 |
} |
| 44 |
|
| 45 |
/** |
| 46 |
* Creates root container if not present. |
| 47 |
*/ |
| 48 |
function ensureRoot() { |
| 49 |
let rootEl = document.getElementById('king-addons-age-gate'); |
| 50 |
if (!rootEl) { |
| 51 |
rootEl = document.createElement('div'); |
| 52 |
rootEl.id = 'king-addons-age-gate'; |
| 53 |
rootEl.className = 'king-addons-age-gate'; |
| 54 |
rootEl.innerHTML = '<div class="king-addons-age-gate__overlay"></div><div class="king-addons-age-gate__card"><div class="king-addons-age-gate__content"></div></div>'; |
| 55 |
document.body.appendChild(rootEl); |
| 56 |
} |
| 57 |
return rootEl; |
| 58 |
} |
| 59 |
|
| 60 |
/** |
| 61 |
* Applies design variables to the overlay. |
| 62 |
*/ |
| 63 |
function applyDesign(rootEl, cardEl, data) { |
| 64 |
const design = data.design || {}; |
| 65 |
rootEl.style.setProperty('--ka-age-overlay', design.overlayColor || '#0d0d0d'); |
| 66 |
rootEl.style.setProperty('--ka-age-overlay-opacity', design.overlayOpacity ?? 0.7); |
| 67 |
rootEl.style.setProperty('--ka-age-card-bg', design.cardBackground || '#ffffff'); |
| 68 |
rootEl.style.setProperty('--ka-age-card-width', (design.cardWidth || 520) + 'px'); |
| 69 |
rootEl.style.setProperty('--ka-age-text', design.textColor || '#111827'); |
| 70 |
rootEl.style.setProperty('--ka-age-title-size', (design.titleSize || 24) + 'px'); |
| 71 |
rootEl.style.setProperty('--ka-age-body-size', (design.bodySize || 16) + 'px'); |
| 72 |
rootEl.style.setProperty('--ka-age-title-weight', design.titleWeight || 700); |
| 73 |
rootEl.style.setProperty('--ka-age-body-weight', design.bodyWeight || 400); |
| 74 |
rootEl.style.setProperty('--ka-age-btn-yes-color', design.buttonYesColor || '#ffffff'); |
| 75 |
rootEl.style.setProperty('--ka-age-btn-yes-bg', design.buttonYesBg || '#10b981'); |
| 76 |
rootEl.style.setProperty('--ka-age-btn-no-color', design.buttonNoColor || '#ffffff'); |
| 77 |
rootEl.style.setProperty('--ka-age-btn-no-bg', design.buttonNoBg || '#ef4444'); |
| 78 |
rootEl.style.setProperty('--ka-age-btn-yes-hover-bg', design.buttonYesHoverBg || design.buttonYesBg || '#0f9c75'); |
| 79 |
rootEl.style.setProperty('--ka-age-btn-no-hover-bg', design.buttonNoHoverBg || design.buttonNoBg || '#d92d20'); |
| 80 |
rootEl.style.setProperty('--ka-age-btn-yes-hover-color', design.buttonYesHoverColor || design.buttonYesColor || '#ffffff'); |
| 81 |
rootEl.style.setProperty('--ka-age-btn-no-hover-color', design.buttonNoHoverColor || design.buttonNoColor || '#ffffff'); |
| 82 |
|
| 83 |
if (design.cardAlign === 'bottom') { |
| 84 |
cardEl.classList.add('king-addons-age-gate__card--bottom'); |
| 85 |
} |
| 86 |
|
| 87 |
if (design.backgroundImage) { |
| 88 |
const bg = document.createElement('div'); |
| 89 |
bg.className = 'king-addons-age-gate__background'; |
| 90 |
bg.style.backgroundImage = 'url(' + design.backgroundImage + ')'; |
| 91 |
cardEl.prepend(bg); |
| 92 |
} |
| 93 |
|
| 94 |
if (design.animation && design.animation !== 'none') { |
| 95 |
rootEl.classList.add('king-addons-age-gate--animate-' + design.animation); |
| 96 |
} |
| 97 |
} |
| 98 |
|
| 99 |
/** |
| 100 |
* Shows the overlay. |
| 101 |
*/ |
| 102 |
function showOverlay(rootEl, data) { |
| 103 |
rootEl.classList.add('king-addons-age-gate--visible'); |
| 104 |
rootEl.setAttribute('aria-hidden', 'false'); |
| 105 |
const overlay = rootEl.querySelector('.king-addons-age-gate__overlay'); |
| 106 |
if (overlay) { |
| 107 |
overlay.setAttribute('aria-hidden', 'true'); |
| 108 |
} |
| 109 |
} |
| 110 |
|
| 111 |
/** |
| 112 |
* Renders simple Yes/No mode. |
| 113 |
*/ |
| 114 |
function renderYesNo(wrapper, data, localState) { |
| 115 |
wrapper.innerHTML = ''; |
| 116 |
const title = document.createElement('h2'); |
| 117 |
title.className = 'king-addons-age-gate__title'; |
| 118 |
title.textContent = data.texts.title; |
| 119 |
|
| 120 |
const subtitle = document.createElement('p'); |
| 121 |
subtitle.className = 'king-addons-age-gate__subtitle'; |
| 122 |
subtitle.textContent = data.mode === 'minimum' |
| 123 |
? data.texts.subtitle + ' (' + data.minAge + '+)' |
| 124 |
: data.texts.subtitle; |
| 125 |
|
| 126 |
const buttons = document.createElement('div'); |
| 127 |
buttons.className = 'king-addons-age-gate__buttons'; |
| 128 |
|
| 129 |
const yesBtn = buildButton('king-addons-age-gate__button king-addons-age-gate__button--allow', data.texts.yes); |
| 130 |
const noBtn = buildButton('king-addons-age-gate__button king-addons-age-gate__button--deny', data.texts.no); |
| 131 |
|
| 132 |
const consent = buildConsent(data); |
| 133 |
|
| 134 |
yesBtn.addEventListener('click', () => { |
| 135 |
if (consent && !consent.checkbox.checked) { |
| 136 |
showMessage(wrapper, data.behaviour.blockMessage || 'Please confirm consent.'); |
| 137 |
return; |
| 138 |
} |
| 139 |
setStatus('allowed', localState.cookieDays); |
| 140 |
closeOverlay(); |
| 141 |
}); |
| 142 |
|
| 143 |
noBtn.addEventListener('click', () => { |
| 144 |
setStatus('denied', localState.cookieDays); |
| 145 |
if (data.behaviour.denyAction === 'redirect' && data.behaviour.denyRedirectUrl) { |
| 146 |
window.location.href = data.behaviour.denyRedirectUrl; |
| 147 |
} else { |
| 148 |
renderBlocked(wrapper, data); |
| 149 |
} |
| 150 |
}); |
| 151 |
|
| 152 |
buttons.appendChild(yesBtn); |
| 153 |
buttons.appendChild(noBtn); |
| 154 |
|
| 155 |
appendLogo(wrapper, data.design); |
| 156 |
wrapper.appendChild(title); |
| 157 |
wrapper.appendChild(subtitle); |
| 158 |
if (consent) { |
| 159 |
wrapper.appendChild(consent.container); |
| 160 |
} |
| 161 |
wrapper.appendChild(buttons); |
| 162 |
} |
| 163 |
|
| 164 |
/** |
| 165 |
* Renders DOB form. |
| 166 |
*/ |
| 167 |
function renderDob(wrapper, data, localState) { |
| 168 |
wrapper.innerHTML = ''; |
| 169 |
const dobConfig = data.dob || { format: 'dmy', errors: { invalid: 'Invalid date.', denied: 'Access denied.' } }; |
| 170 |
|
| 171 |
const title = document.createElement('h2'); |
| 172 |
title.className = 'king-addons-age-gate__title'; |
| 173 |
title.textContent = data.texts.title; |
| 174 |
|
| 175 |
const subtitle = document.createElement('p'); |
| 176 |
subtitle.className = 'king-addons-age-gate__subtitle'; |
| 177 |
subtitle.textContent = data.texts.subtitle + ' (' + data.minAge + '+)'; |
| 178 |
|
| 179 |
const grid = document.createElement('div'); |
| 180 |
grid.className = 'king-addons-age-gate__field-grid'; |
| 181 |
|
| 182 |
const inputs = buildDobInputs(dobConfig.format || 'dmy'); |
| 183 |
inputs.forEach((input) => grid.appendChild(input)); |
| 184 |
|
| 185 |
const consent = buildConsent(data); |
| 186 |
|
| 187 |
const buttons = document.createElement('div'); |
| 188 |
buttons.className = 'king-addons-age-gate__buttons'; |
| 189 |
const submitBtn = buildButton('king-addons-age-gate__button king-addons-age-gate__button--allow', data.texts.yes); |
| 190 |
const declineBtn = buildButton('king-addons-age-gate__button king-addons-age-gate__button--deny', data.texts.no); |
| 191 |
|
| 192 |
const message = document.createElement('div'); |
| 193 |
message.className = 'king-addons-age-gate__message'; |
| 194 |
message.style.display = 'none'; |
| 195 |
|
| 196 |
submitBtn.addEventListener('click', () => { |
| 197 |
if (consent && !consent.checkbox.checked) { |
| 198 |
showMessage(wrapper, data.behaviour.blockMessage || dobConfig.errors.invalid); |
| 199 |
return; |
| 200 |
} |
| 201 |
message.style.display = 'none'; |
| 202 |
const params = new URLSearchParams(); |
| 203 |
params.append('action', data.ajax.action); |
| 204 |
params.append('nonce', data.ajax.nonce); |
| 205 |
params.append('day', inputs.day.value || ''); |
| 206 |
params.append('month', inputs.month.value || ''); |
| 207 |
params.append('year', inputs.year.value || ''); |
| 208 |
|
| 209 |
submitBtn.disabled = true; |
| 210 |
submitBtn.textContent = data.texts.yes + '...'; |
| 211 |
|
| 212 |
fetch(data.ajax.url, { |
| 213 |
method: 'POST', |
| 214 |
headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}, |
| 215 |
body: params.toString(), |
| 216 |
}).then((response) => response.json()) |
| 217 |
.then((resp) => { |
| 218 |
if (resp && resp.success) { |
| 219 |
setStatus('allowed', localState.cookieDays); |
| 220 |
closeOverlay(); |
| 221 |
} else { |
| 222 |
const msg = resp && resp.data && resp.data.message ? resp.data.message : dobConfig.errors.denied; |
| 223 |
showInlineMessage(msg); |
| 224 |
setStatus('denied', localState.cookieDays); |
| 225 |
} |
| 226 |
}) |
| 227 |
.catch(() => { |
| 228 |
showInlineMessage(dobConfig.errors.invalid); |
| 229 |
}) |
| 230 |
.finally(() => { |
| 231 |
submitBtn.disabled = false; |
| 232 |
submitBtn.textContent = data.texts.yes; |
| 233 |
}); |
| 234 |
}); |
| 235 |
|
| 236 |
declineBtn.addEventListener('click', () => { |
| 237 |
setStatus('denied', localState.cookieDays); |
| 238 |
if (data.behaviour.denyAction === 'redirect' && data.behaviour.denyRedirectUrl) { |
| 239 |
window.location.href = data.behaviour.denyRedirectUrl; |
| 240 |
} else { |
| 241 |
renderBlocked(wrapper, data); |
| 242 |
} |
| 243 |
}); |
| 244 |
|
| 245 |
buttons.appendChild(submitBtn); |
| 246 |
buttons.appendChild(declineBtn); |
| 247 |
|
| 248 |
appendLogo(wrapper, data.design); |
| 249 |
wrapper.appendChild(title); |
| 250 |
wrapper.appendChild(subtitle); |
| 251 |
wrapper.appendChild(grid); |
| 252 |
if (consent) { |
| 253 |
wrapper.appendChild(consent.container); |
| 254 |
} |
| 255 |
wrapper.appendChild(buttons); |
| 256 |
wrapper.appendChild(message); |
| 257 |
|
| 258 |
function showInlineMessage(text) { |
| 259 |
message.textContent = text; |
| 260 |
message.style.display = 'block'; |
| 261 |
} |
| 262 |
} |
| 263 |
|
| 264 |
/** |
| 265 |
* Builds DOB inputs based on format. |
| 266 |
*/ |
| 267 |
function buildDobInputs(format) { |
| 268 |
const day = document.createElement('input'); |
| 269 |
day.type = 'number'; |
| 270 |
day.min = '1'; |
| 271 |
day.max = '31'; |
| 272 |
day.placeholder = 'DD'; |
| 273 |
day.className = 'king-addons-age-gate__input'; |
| 274 |
|
| 275 |
const month = document.createElement('input'); |
| 276 |
month.type = 'number'; |
| 277 |
month.min = '1'; |
| 278 |
month.max = '12'; |
| 279 |
month.placeholder = 'MM'; |
| 280 |
month.className = 'king-addons-age-gate__input'; |
| 281 |
|
| 282 |
const year = document.createElement('input'); |
| 283 |
year.type = 'number'; |
| 284 |
year.min = '1900'; |
| 285 |
year.placeholder = 'YYYY'; |
| 286 |
year.className = 'king-addons-age-gate__input'; |
| 287 |
|
| 288 |
const parts = []; |
| 289 |
if (format === 'mdy') { |
| 290 |
parts.push(month, day, year); |
| 291 |
} else if (format === 'ymd') { |
| 292 |
parts.push(year, month, day); |
| 293 |
} else { |
| 294 |
parts.push(day, month, year); |
| 295 |
} |
| 296 |
|
| 297 |
parts.day = day; |
| 298 |
parts.month = month; |
| 299 |
parts.year = year; |
| 300 |
return parts; |
| 301 |
} |
| 302 |
|
| 303 |
/** |
| 304 |
* Renders blocked message. |
| 305 |
*/ |
| 306 |
function renderBlocked(wrapper, data) { |
| 307 |
wrapper.innerHTML = ''; |
| 308 |
const title = document.createElement('h2'); |
| 309 |
title.className = 'king-addons-age-gate__title'; |
| 310 |
title.textContent = data.texts.title; |
| 311 |
|
| 312 |
const message = document.createElement('p'); |
| 313 |
message.className = 'king-addons-age-gate__message'; |
| 314 |
message.style.display = 'block'; |
| 315 |
message.textContent = data.behaviour.blockMessage || data.texts.block; |
| 316 |
|
| 317 |
appendLogo(wrapper, data.design); |
| 318 |
wrapper.appendChild(title); |
| 319 |
wrapper.appendChild(message); |
| 320 |
|
| 321 |
if (data.behaviour.denyRedirectUrl) { |
| 322 |
const buttons = document.createElement('div'); |
| 323 |
buttons.className = 'king-addons-age-gate__buttons'; |
| 324 |
const goBtn = buildButton('king-addons-age-gate__button king-addons-age-gate__button--allow', data.texts.no); |
| 325 |
goBtn.addEventListener('click', () => { |
| 326 |
window.location.href = data.behaviour.denyRedirectUrl; |
| 327 |
}); |
| 328 |
buttons.appendChild(goBtn); |
| 329 |
wrapper.appendChild(buttons); |
| 330 |
} |
| 331 |
} |
| 332 |
|
| 333 |
/** |
| 334 |
* Builds consent checkbox if required. |
| 335 |
*/ |
| 336 |
function buildConsent(data) { |
| 337 |
if (!data.behaviour || !data.behaviour.consentCheckbox) { |
| 338 |
return null; |
| 339 |
} |
| 340 |
const container = document.createElement('label'); |
| 341 |
container.className = 'king-addons-age-gate__subtitle'; |
| 342 |
const checkbox = document.createElement('input'); |
| 343 |
checkbox.type = 'checkbox'; |
| 344 |
checkbox.style.marginRight = '8px'; |
| 345 |
container.appendChild(checkbox); |
| 346 |
const labelText = data.behaviour.consentLabel || 'I agree to the policy.'; |
| 347 |
container.appendChild(document.createTextNode(labelText)); |
| 348 |
return {container, checkbox}; |
| 349 |
} |
| 350 |
|
| 351 |
/** |
| 352 |
* Utility to create button. |
| 353 |
*/ |
| 354 |
function buildButton(classes, text) { |
| 355 |
const button = document.createElement('button'); |
| 356 |
button.type = 'button'; |
| 357 |
button.className = classes; |
| 358 |
button.textContent = text; |
| 359 |
return button; |
| 360 |
} |
| 361 |
|
| 362 |
/** |
| 363 |
* Appends logo if provided. |
| 364 |
*/ |
| 365 |
function appendLogo(wrapper, design) { |
| 366 |
if (!design || !design.logo) { |
| 367 |
return; |
| 368 |
} |
| 369 |
const logo = document.createElement('img'); |
| 370 |
logo.src = design.logo; |
| 371 |
logo.alt = ''; |
| 372 |
logo.className = 'king-addons-age-gate__logo'; |
| 373 |
wrapper.appendChild(logo); |
| 374 |
} |
| 375 |
|
| 376 |
/** |
| 377 |
* Shows a toast-like message within wrapper. |
| 378 |
*/ |
| 379 |
function showMessage(wrapper, text) { |
| 380 |
let message = wrapper.querySelector('.king-addons-age-gate__message'); |
| 381 |
if (!message) { |
| 382 |
message = document.createElement('div'); |
| 383 |
message.className = 'king-addons-age-gate__message'; |
| 384 |
wrapper.appendChild(message); |
| 385 |
} |
| 386 |
message.textContent = text; |
| 387 |
message.style.display = 'block'; |
| 388 |
} |
| 389 |
|
| 390 |
/** |
| 391 |
* Sets cookie and updates state. |
| 392 |
*/ |
| 393 |
function setStatus(status, days) { |
| 394 |
const revision = payload.cookie && payload.cookie.revision ? payload.cookie.revision : ''; |
| 395 |
const value = encodeURIComponent(status + '|' + revision); |
| 396 |
let cookie = payload.cookie.name + '=' + value + '; path=/; SameSite=Lax'; |
| 397 |
if (payload.cookie.domain) { |
| 398 |
cookie += '; domain=' + payload.cookie.domain; |
| 399 |
} |
| 400 |
if (days > 0) { |
| 401 |
const date = new Date(); |
| 402 |
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); |
| 403 |
cookie += '; expires=' + date.toUTCString(); |
| 404 |
} |
| 405 |
document.cookie = cookie; |
| 406 |
state.status = status; |
| 407 |
} |
| 408 |
|
| 409 |
/** |
| 410 |
* Locks document scroll. |
| 411 |
*/ |
| 412 |
function lockScroll() { |
| 413 |
document.documentElement.dataset.kaAgeGateScroll = document.documentElement.style.overflow || ''; |
| 414 |
document.documentElement.style.overflow = 'hidden'; |
| 415 |
} |
| 416 |
|
| 417 |
/** |
| 418 |
* Restores scroll and hides overlay. |
| 419 |
*/ |
| 420 |
function closeOverlay() { |
| 421 |
const previous = document.documentElement.dataset.kaAgeGateScroll || ''; |
| 422 |
document.documentElement.style.overflow = previous; |
| 423 |
root.classList.remove('king-addons-age-gate--visible'); |
| 424 |
} |
| 425 |
|
| 426 |
/** |
| 427 |
* Cookie days resolver based on behaviour. |
| 428 |
*/ |
| 429 |
function resolveCookieDays(data) { |
| 430 |
if (data.behaviour && data.behaviour.repeatMode) { |
| 431 |
if (data.behaviour.repeatMode === 'session') { |
| 432 |
return 0; |
| 433 |
} |
| 434 |
if (data.behaviour.repeatMode === 'once') { |
| 435 |
return 3650; |
| 436 |
} |
| 437 |
if (data.behaviour.repeatMode === 'days') { |
| 438 |
return data.behaviour.repeatDays || data.cookie.days || 30; |
| 439 |
} |
| 440 |
} |
| 441 |
return data.cookie && data.cookie.days ? data.cookie.days : 30; |
| 442 |
} |
| 443 |
})(); |
| 444 |
|
| 445 |
|
| 446 |
|
| 447 |
|