/**
* Age Gate widget behavior.
*
* This script renders a global overlay. It must initialize once per page load,
* and should also work inside Elementor preview without duplicating DOM nodes.
*/
(function () {
"use strict";
const FLAG = "kingAddonsAgeGateInitialized";
/**
* Initialize Age Gate once.
*
* @returns {void}
*/
const init = () => {
const payload = window.kingAddonsAgeGate;
if (!payload || !payload.enabled) {
return;
}
if (window[FLAG]) {
return;
}
if (!document.body) {
// Defer until body is available.
setTimeout(init, 0);
return;
}
window[FLAG] = true;
const root = ensureRoot();
const card = root.querySelector('.king-addons-age-gate__card');
const content = root.querySelector('.king-addons-age-gate__content');
const state = {
status: payload.status || '',
cookieDays: resolveCookieDays(payload),
};
if (!card || !content) {
return;
}
applyDesign(root, card, payload);
const needsBlock =
payload.behaviour &&
payload.behaviour.denyAction === 'block' &&
state.status === 'denied';
if (!payload.shouldRender && !needsBlock) {
return;
}
lockScroll();
showOverlay(root, payload);
if (payload.elementorTemplate) {
const templateWrapper = document.createElement('div');
templateWrapper.className = 'king-addons-age-gate__template';
templateWrapper.innerHTML = payload.elementorTemplate;
content.appendChild(templateWrapper);
}
if (needsBlock) {
renderBlocked(content, payload);
return;
}
if (payload.mode === 'dob' && payload.isPremium) {
renderDob(content, payload, state);
} else {
renderYesNo(content, payload, state);
}
};
const initWhenReady = () => {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
return;
}
init();
};
initWhenReady();
// Elementor preview support: run init after Elementor frontend bootstrap.
if (typeof jQuery !== "undefined") {
jQuery(window).on("elementor/frontend/init", function () {
init();
});
}
/**
* Creates root container if not present.
*/
function ensureRoot() {
let rootEl = document.getElementById('king-addons-age-gate');
if (!rootEl) {
rootEl = document.createElement('div');
rootEl.id = 'king-addons-age-gate';
rootEl.className = 'king-addons-age-gate';
rootEl.innerHTML = '
';
document.body.appendChild(rootEl);
}
return rootEl;
}
/**
* Applies design variables to the overlay.
*/
function applyDesign(rootEl, cardEl, data) {
const design = data.design || {};
rootEl.style.setProperty('--ka-age-overlay', design.overlayColor || '#0d0d0d');
rootEl.style.setProperty('--ka-age-overlay-opacity', design.overlayOpacity ?? 0.7);
rootEl.style.setProperty('--ka-age-card-bg', design.cardBackground || '#ffffff');
rootEl.style.setProperty('--ka-age-card-width', (design.cardWidth || 520) + 'px');
rootEl.style.setProperty('--ka-age-text', design.textColor || '#111827');
rootEl.style.setProperty('--ka-age-title-size', (design.titleSize || 24) + 'px');
rootEl.style.setProperty('--ka-age-body-size', (design.bodySize || 16) + 'px');
rootEl.style.setProperty('--ka-age-title-weight', design.titleWeight || 700);
rootEl.style.setProperty('--ka-age-body-weight', design.bodyWeight || 400);
rootEl.style.setProperty('--ka-age-btn-yes-color', design.buttonYesColor || '#ffffff');
rootEl.style.setProperty('--ka-age-btn-yes-bg', design.buttonYesBg || '#10b981');
rootEl.style.setProperty('--ka-age-btn-no-color', design.buttonNoColor || '#ffffff');
rootEl.style.setProperty('--ka-age-btn-no-bg', design.buttonNoBg || '#ef4444');
rootEl.style.setProperty('--ka-age-btn-yes-hover-bg', design.buttonYesHoverBg || design.buttonYesBg || '#0f9c75');
rootEl.style.setProperty('--ka-age-btn-no-hover-bg', design.buttonNoHoverBg || design.buttonNoBg || '#d92d20');
rootEl.style.setProperty('--ka-age-btn-yes-hover-color', design.buttonYesHoverColor || design.buttonYesColor || '#ffffff');
rootEl.style.setProperty('--ka-age-btn-no-hover-color', design.buttonNoHoverColor || design.buttonNoColor || '#ffffff');
if (design.cardAlign === 'bottom') {
cardEl.classList.add('king-addons-age-gate__card--bottom');
}
if (design.backgroundImage) {
const bg = document.createElement('div');
bg.className = 'king-addons-age-gate__background';
bg.style.backgroundImage = 'url(' + design.backgroundImage + ')';
cardEl.prepend(bg);
}
if (design.animation && design.animation !== 'none') {
rootEl.classList.add('king-addons-age-gate--animate-' + design.animation);
}
}
/**
* Shows the overlay.
*/
function showOverlay(rootEl, data) {
rootEl.classList.add('king-addons-age-gate--visible');
rootEl.setAttribute('aria-hidden', 'false');
const overlay = rootEl.querySelector('.king-addons-age-gate__overlay');
if (overlay) {
overlay.setAttribute('aria-hidden', 'true');
}
}
/**
* Renders simple Yes/No mode.
*/
function renderYesNo(wrapper, data, localState) {
wrapper.innerHTML = '';
const title = document.createElement('h2');
title.className = 'king-addons-age-gate__title';
title.textContent = data.texts.title;
const subtitle = document.createElement('p');
subtitle.className = 'king-addons-age-gate__subtitle';
subtitle.textContent = data.mode === 'minimum'
? data.texts.subtitle + ' (' + data.minAge + '+)'
: data.texts.subtitle;
const buttons = document.createElement('div');
buttons.className = 'king-addons-age-gate__buttons';
const yesBtn = buildButton('king-addons-age-gate__button king-addons-age-gate__button--allow', data.texts.yes);
const noBtn = buildButton('king-addons-age-gate__button king-addons-age-gate__button--deny', data.texts.no);
const consent = buildConsent(data);
yesBtn.addEventListener('click', () => {
if (consent && !consent.checkbox.checked) {
showMessage(wrapper, data.behaviour.blockMessage || 'Please confirm consent.');
return;
}
setStatus('allowed', localState.cookieDays);
closeOverlay();
});
noBtn.addEventListener('click', () => {
setStatus('denied', localState.cookieDays);
if (data.behaviour.denyAction === 'redirect' && data.behaviour.denyRedirectUrl) {
window.location.href = data.behaviour.denyRedirectUrl;
} else {
renderBlocked(wrapper, data);
}
});
buttons.appendChild(yesBtn);
buttons.appendChild(noBtn);
appendLogo(wrapper, data.design);
wrapper.appendChild(title);
wrapper.appendChild(subtitle);
if (consent) {
wrapper.appendChild(consent.container);
}
wrapper.appendChild(buttons);
}
/**
* Renders DOB form.
*/
function renderDob(wrapper, data, localState) {
wrapper.innerHTML = '';
const dobConfig = data.dob || { format: 'dmy', errors: { invalid: 'Invalid date.', denied: 'Access denied.' } };
const title = document.createElement('h2');
title.className = 'king-addons-age-gate__title';
title.textContent = data.texts.title;
const subtitle = document.createElement('p');
subtitle.className = 'king-addons-age-gate__subtitle';
subtitle.textContent = data.texts.subtitle + ' (' + data.minAge + '+)';
const grid = document.createElement('div');
grid.className = 'king-addons-age-gate__field-grid';
const inputs = buildDobInputs(dobConfig.format || 'dmy');
inputs.forEach((input) => grid.appendChild(input));
const consent = buildConsent(data);
const buttons = document.createElement('div');
buttons.className = 'king-addons-age-gate__buttons';
const submitBtn = buildButton('king-addons-age-gate__button king-addons-age-gate__button--allow', data.texts.yes);
const declineBtn = buildButton('king-addons-age-gate__button king-addons-age-gate__button--deny', data.texts.no);
const message = document.createElement('div');
message.className = 'king-addons-age-gate__message';
message.style.display = 'none';
submitBtn.addEventListener('click', () => {
if (consent && !consent.checkbox.checked) {
showMessage(wrapper, data.behaviour.blockMessage || dobConfig.errors.invalid);
return;
}
message.style.display = 'none';
const params = new URLSearchParams();
params.append('action', data.ajax.action);
params.append('nonce', data.ajax.nonce);
params.append('day', inputs.day.value || '');
params.append('month', inputs.month.value || '');
params.append('year', inputs.year.value || '');
submitBtn.disabled = true;
submitBtn.textContent = data.texts.yes + '...';
fetch(data.ajax.url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
body: params.toString(),
}).then((response) => response.json())
.then((resp) => {
if (resp && resp.success) {
setStatus('allowed', localState.cookieDays);
closeOverlay();
} else {
const msg = resp && resp.data && resp.data.message ? resp.data.message : dobConfig.errors.denied;
showInlineMessage(msg);
setStatus('denied', localState.cookieDays);
}
})
.catch(() => {
showInlineMessage(dobConfig.errors.invalid);
})
.finally(() => {
submitBtn.disabled = false;
submitBtn.textContent = data.texts.yes;
});
});
declineBtn.addEventListener('click', () => {
setStatus('denied', localState.cookieDays);
if (data.behaviour.denyAction === 'redirect' && data.behaviour.denyRedirectUrl) {
window.location.href = data.behaviour.denyRedirectUrl;
} else {
renderBlocked(wrapper, data);
}
});
buttons.appendChild(submitBtn);
buttons.appendChild(declineBtn);
appendLogo(wrapper, data.design);
wrapper.appendChild(title);
wrapper.appendChild(subtitle);
wrapper.appendChild(grid);
if (consent) {
wrapper.appendChild(consent.container);
}
wrapper.appendChild(buttons);
wrapper.appendChild(message);
function showInlineMessage(text) {
message.textContent = text;
message.style.display = 'block';
}
}
/**
* Builds DOB inputs based on format.
*/
function buildDobInputs(format) {
const day = document.createElement('input');
day.type = 'number';
day.min = '1';
day.max = '31';
day.placeholder = 'DD';
day.className = 'king-addons-age-gate__input';
const month = document.createElement('input');
month.type = 'number';
month.min = '1';
month.max = '12';
month.placeholder = 'MM';
month.className = 'king-addons-age-gate__input';
const year = document.createElement('input');
year.type = 'number';
year.min = '1900';
year.placeholder = 'YYYY';
year.className = 'king-addons-age-gate__input';
const parts = [];
if (format === 'mdy') {
parts.push(month, day, year);
} else if (format === 'ymd') {
parts.push(year, month, day);
} else {
parts.push(day, month, year);
}
parts.day = day;
parts.month = month;
parts.year = year;
return parts;
}
/**
* Renders blocked message.
*/
function renderBlocked(wrapper, data) {
wrapper.innerHTML = '';
const title = document.createElement('h2');
title.className = 'king-addons-age-gate__title';
title.textContent = data.texts.title;
const message = document.createElement('p');
message.className = 'king-addons-age-gate__message';
message.style.display = 'block';
message.textContent = data.behaviour.blockMessage || data.texts.block;
appendLogo(wrapper, data.design);
wrapper.appendChild(title);
wrapper.appendChild(message);
if (data.behaviour.denyRedirectUrl) {
const buttons = document.createElement('div');
buttons.className = 'king-addons-age-gate__buttons';
const goBtn = buildButton('king-addons-age-gate__button king-addons-age-gate__button--allow', data.texts.no);
goBtn.addEventListener('click', () => {
window.location.href = data.behaviour.denyRedirectUrl;
});
buttons.appendChild(goBtn);
wrapper.appendChild(buttons);
}
}
/**
* Builds consent checkbox if required.
*/
function buildConsent(data) {
if (!data.behaviour || !data.behaviour.consentCheckbox) {
return null;
}
const container = document.createElement('label');
container.className = 'king-addons-age-gate__subtitle';
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.style.marginRight = '8px';
container.appendChild(checkbox);
const labelText = data.behaviour.consentLabel || 'I agree to the policy.';
container.appendChild(document.createTextNode(labelText));
return {container, checkbox};
}
/**
* Utility to create button.
*/
function buildButton(classes, text) {
const button = document.createElement('button');
button.type = 'button';
button.className = classes;
button.textContent = text;
return button;
}
/**
* Appends logo if provided.
*/
function appendLogo(wrapper, design) {
if (!design || !design.logo) {
return;
}
const logo = document.createElement('img');
logo.src = design.logo;
logo.alt = '';
logo.className = 'king-addons-age-gate__logo';
wrapper.appendChild(logo);
}
/**
* Shows a toast-like message within wrapper.
*/
function showMessage(wrapper, text) {
let message = wrapper.querySelector('.king-addons-age-gate__message');
if (!message) {
message = document.createElement('div');
message.className = 'king-addons-age-gate__message';
wrapper.appendChild(message);
}
message.textContent = text;
message.style.display = 'block';
}
/**
* Sets cookie and updates state.
*/
function setStatus(status, days) {
const revision = payload.cookie && payload.cookie.revision ? payload.cookie.revision : '';
const value = encodeURIComponent(status + '|' + revision);
let cookie = payload.cookie.name + '=' + value + '; path=/; SameSite=Lax';
if (payload.cookie.domain) {
cookie += '; domain=' + payload.cookie.domain;
}
if (days > 0) {
const date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
cookie += '; expires=' + date.toUTCString();
}
document.cookie = cookie;
state.status = status;
}
/**
* Locks document scroll.
*/
function lockScroll() {
document.documentElement.dataset.kaAgeGateScroll = document.documentElement.style.overflow || '';
document.documentElement.style.overflow = 'hidden';
}
/**
* Restores scroll and hides overlay.
*/
function closeOverlay() {
const previous = document.documentElement.dataset.kaAgeGateScroll || '';
document.documentElement.style.overflow = previous;
root.classList.remove('king-addons-age-gate--visible');
}
/**
* Cookie days resolver based on behaviour.
*/
function resolveCookieDays(data) {
if (data.behaviour && data.behaviour.repeatMode) {
if (data.behaviour.repeatMode === 'session') {
return 0;
}
if (data.behaviour.repeatMode === 'once') {
return 3650;
}
if (data.behaviour.repeatMode === 'days') {
return data.behaviour.repeatDays || data.cookie.days || 30;
}
}
return data.cookie && data.cookie.days ? data.cookie.days : 30;
}
})();