PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.49
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.49
51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 51.1.46 51.1.47 51.1.49 All 37 releases
king-addons / includes / widgets / Age_Gate / script.js

script.js in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.49, at includes/widgets/Age_Gate/script.js

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