PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.63
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.63
51.1.86 51.1.84 51.1.85 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 All 40 releases
king-addons / includes / extensions / Cookie_Consent / assets / script.js

script.js in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.63, at includes/extensions/Cookie_Consent/assets/script.js

660 lines 23.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * King Addons Cookie / Consent Bar
3 * GDPR/CCPA compliant cookie consent management.
4 */
5 (function () {
6 'use strict';
7
8 const config = window.kingAddonsCookieConsent;
9 if (!config || !config.options) {
10 return;
11 }
12
13 const options = config.options;
14 const container = document.getElementById('king-addons-cookie-consent');
15 if (!container) {
16 return;
17 }
18
19 const consentName = options.advanced.cookie_name || 'ka_cookie_consent';
20 const consentLifetime = parseInt(options.consentLifetime, 10) || 365;
21 const isPremium = !!options.isPremium;
22 const logsEnabled = !!options.logsEnabled;
23 const region = config.region || 'all';
24
25 const categories = options.categories || [];
26 const necessaryKeys = categories.filter((cat) => cat.state === 'required').map((cat) => cat.key);
27
28 /**
29 * Utility functions
30 */
31 const encode = (value) => JSON.stringify(value);
32 const decode = (value) => {
33 try {
34 return JSON.parse(value);
35 } catch (e) {
36 return null;
37 }
38 };
39
40 /**
41 * Storage handler for consent data
42 */
43 const storage = {
44 save(consent) {
45 if (options.advanced.storage === 'local' && isPremium && window.localStorage) {
46 window.localStorage.setItem(consentName, encode(consent));
47 return;
48 }
49
50 const expires = new Date();
51 expires.setTime(expires.getTime() + consentLifetime * 24 * 60 * 60 * 1000);
52
53 const sameSite = options.advanced.same_site || 'Lax';
54 const secureFlag = !!options.advanced.secure || sameSite === 'None';
55 const parts = [
56 `${consentName}=${encodeURIComponent(encode(consent))}`,
57 `expires=${expires.toUTCString()}`,
58 `path=${options.advanced.cookie_path || '/'}`,
59 options.advanced.cookie_domain ? `domain=${options.advanced.cookie_domain}` : '',
60 `SameSite=${sameSite}`,
61 secureFlag ? 'Secure' : '',
62 ].filter(Boolean);
63 document.cookie = parts.join('; ');
64 },
65 read() {
66 if (options.advanced.storage === 'local' && isPremium && window.localStorage) {
67 const stored = window.localStorage.getItem(consentName);
68 return stored ? decode(stored) : null;
69 }
70
71 const cookies = document.cookie ? document.cookie.split('; ') : [];
72 for (const entry of cookies) {
73 if (entry.startsWith(`${consentName}=`)) {
74 return decode(decodeURIComponent(entry.substring(consentName.length + 1)));
75 }
76 }
77 return null;
78 },
79 };
80
81 /**
82 * Apply CSS variables from design options
83 */
84 const setCSSVariables = () => {
85 const colors = options.design.colors;
86 container.style.setProperty('--ka-bg', colors.background || '#111827');
87 container.style.setProperty('--ka-text', colors.text || '#f9fafb');
88 container.style.setProperty('--ka-link', colors.link || '#60a5fa');
89 container.style.setProperty('--ka-primary-bg', colors.primary_bg || '#2563eb');
90 container.style.setProperty('--ka-primary-text', colors.primary_text || '#ffffff');
91 container.style.setProperty('--ka-secondary-bg', colors.secondary_bg || '#374151');
92 container.style.setProperty('--ka-secondary-text', colors.secondary_text || '#ffffff');
93 container.style.setProperty('--ka-border', colors.border || '#374151');
94 container.style.setProperty('--ka-radius', `${options.design.border_radius || 12}px`);
95 container.style.setProperty('--ka-shadow', options.design.shadow ? '0 20px 50px rgba(0,0,0,0.3)' : 'none');
96 };
97
98 /**
99 * Apply layout class based on design options
100 */
101 const applyLayoutClass = () => {
102 const layoutMap = {
103 'top-bar': 'king-addons-cookie-consent--top-bar',
104 'bottom-left': 'king-addons-cookie-consent--bottom-left',
105 'bottom-right': 'king-addons-cookie-consent--bottom-right',
106 'modal': 'king-addons-cookie-consent--modal',
107 };
108 const layoutClass = layoutMap[options.design.layout] || 'king-addons-cookie-consent--bottom-bar';
109 container.classList.add(layoutClass);
110
111 // Apply preset class
112 const preset = options.design.preset || 'dark';
113 container.classList.add(`king-addons-cookie-consent--preset-${preset}`);
114
115 // Apply animation class
116 const animation = options.design.animation || 'fade';
117 container.classList.add(`king-addons-cookie-consent--anim-${animation}`);
118 };
119
120 const bannerEl = container.querySelector('.king-addons-cookie-consent__banner');
121 const modalEl = container.querySelector('.king-addons-cookie-consent__modal');
122
123 /**
124 * Build the consent banner
125 */
126 const buildBanner = () => {
127 const body = document.createElement('div');
128 body.className = 'king-addons-cookie-consent__body';
129
130 // Title
131 const title = document.createElement('h3');
132 title.className = 'king-addons-cookie-consent__title';
133 title.textContent = options.content.title;
134
135 // Message text
136 const text = document.createElement('p');
137 text.className = 'king-addons-cookie-consent__text';
138 text.textContent = options.content.message;
139
140 // Policy links
141 const links = document.createElement('div');
142 links.className = 'king-addons-cookie-consent__links';
143
144 if (options.content.privacy_url) {
145 const privacy = document.createElement('a');
146 privacy.href = options.content.privacy_url;
147 privacy.target = '_blank';
148 privacy.rel = 'noopener noreferrer';
149 privacy.textContent = options.content.privacy_label || 'Privacy Policy';
150 links.appendChild(privacy);
151 }
152
153 const cookieLink = options.content.cookie_url_custom || options.content.cookie_url;
154 if (cookieLink) {
155 const cookie = document.createElement('a');
156 cookie.href = cookieLink;
157 cookie.target = '_blank';
158 cookie.rel = 'noopener noreferrer';
159 cookie.textContent = options.content.cookie_label || 'Cookie Policy';
160 links.appendChild(cookie);
161 }
162
163 // Action buttons
164 const actions = document.createElement('div');
165 actions.className = 'king-addons-cookie-consent__actions';
166
167 const acceptBtn = document.createElement('button');
168 acceptBtn.type = 'button';
169 acceptBtn.className = 'king-addons-cookie-consent__btn king-addons-cookie-consent__btn--primary';
170 acceptBtn.textContent = options.buttons.accept;
171 acceptBtn.addEventListener('click', () => handleAcceptAll());
172
173 const rejectBtn = document.createElement('button');
174 rejectBtn.type = 'button';
175 rejectBtn.className = 'king-addons-cookie-consent__btn king-addons-cookie-consent__btn--secondary';
176 rejectBtn.textContent = options.buttons.reject;
177 rejectBtn.addEventListener('click', () => handleRejectAll());
178
179 const settingsBtn = document.createElement('button');
180 settingsBtn.type = 'button';
181 settingsBtn.className = 'king-addons-cookie-consent__btn king-addons-cookie-consent__btn--secondary';
182 settingsBtn.textContent = options.buttons.settings;
183 settingsBtn.addEventListener('click', () => openModal());
184
185 actions.append(acceptBtn, rejectBtn, settingsBtn);
186 body.append(title, text, links, actions);
187 bannerEl.appendChild(body);
188 };
189
190 /**
191 * Build the preferences modal
192 */
193 const buildModal = () => {
194 const inner = document.createElement('div');
195 inner.className = 'king-addons-cookie-consent__modal-inner';
196
197 // Header
198 const header = document.createElement('div');
199 header.className = 'king-addons-cookie-consent__modal-header';
200
201 const title = document.createElement('h3');
202 title.className = 'king-addons-cookie-consent__modal-title';
203 title.textContent = options.content.title;
204
205 const closeBtn = document.createElement('button');
206 closeBtn.type = 'button';
207 closeBtn.className = 'king-addons-cookie-consent__close';
208 closeBtn.setAttribute('aria-label', 'Close');
209 closeBtn.innerHTML = '×';
210 closeBtn.addEventListener('click', () => closeModal());
211
212 header.append(title, closeBtn);
213
214 // Body with description and links
215 const body = document.createElement('div');
216 body.className = 'king-addons-cookie-consent__modal-body';
217
218 const description = document.createElement('p');
219 description.className = 'king-addons-cookie-consent__modal-text';
220 description.textContent = options.content.message;
221
222 // Policy links in modal
223 const links = document.createElement('div');
224 links.className = 'king-addons-cookie-consent__modal-links';
225
226 if (options.content.privacy_url) {
227 const privacy = document.createElement('a');
228 privacy.href = options.content.privacy_url;
229 privacy.target = '_blank';
230 privacy.rel = 'noopener noreferrer';
231 privacy.textContent = options.content.privacy_label || 'Privacy Policy';
232 links.appendChild(privacy);
233 }
234
235 const cookieLink = options.content.cookie_url_custom || options.content.cookie_url;
236 if (cookieLink) {
237 const cookie = document.createElement('a');
238 cookie.href = cookieLink;
239 cookie.target = '_blank';
240 cookie.rel = 'noopener noreferrer';
241 cookie.textContent = options.content.cookie_label || 'Cookie Policy';
242 links.appendChild(cookie);
243 }
244
245 // Categories list
246 const categoriesWrap = document.createElement('div');
247 categoriesWrap.className = 'king-addons-cookie-consent__categories';
248
249 const storedConsent = storage.read();
250 const storedCategories = storedConsent?.categories || [];
251
252 categories.forEach((category) => {
253 if (category.display === false) {
254 return;
255 }
256
257 const item = document.createElement('div');
258 item.className = 'king-addons-cookie-consent__category';
259
260 const headerRow = document.createElement('div');
261 headerRow.className = 'king-addons-cookie-consent__category-header';
262
263 const label = document.createElement('h4');
264 label.textContent = category.label;
265
266 headerRow.appendChild(label);
267
268 // Toggle or required badge
269 if (category.state === 'required') {
270 const badge = document.createElement('span');
271 badge.className = 'king-addons-cookie-consent__required-badge';
272 badge.textContent = 'Required';
273 headerRow.appendChild(badge);
274 } else {
275 const toggleWrap = document.createElement('label');
276 toggleWrap.className = 'king-addons-cookie-consent__toggle';
277
278 const toggle = document.createElement('input');
279 toggle.type = 'checkbox';
280 toggle.value = category.key;
281 toggle.dataset.categoryKey = category.key;
282
283 // Check based on stored consent or default state
284 if (storedCategories.includes(category.key)) {
285 toggle.checked = true;
286 } else if (category.state === 'on') {
287 toggle.checked = true;
288 } else {
289 toggle.checked = false;
290 }
291
292 const slider = document.createElement('span');
293 slider.className = 'king-addons-cookie-consent__toggle-slider';
294
295 toggleWrap.append(toggle, slider);
296 headerRow.appendChild(toggleWrap);
297 }
298
299 const desc = document.createElement('p');
300 desc.textContent = category.description || '';
301
302 item.append(headerRow, desc);
303 categoriesWrap.appendChild(item);
304 });
305
306 body.append(description, links, categoriesWrap);
307
308 // Footer with actions
309 const footer = document.createElement('div');
310 footer.className = 'king-addons-cookie-consent__footer';
311
312 const footerActions = document.createElement('div');
313 footerActions.className = 'king-addons-cookie-consent__footer-actions';
314
315 const rejectBtn = document.createElement('button');
316 rejectBtn.type = 'button';
317 rejectBtn.className = 'king-addons-cookie-consent__btn king-addons-cookie-consent__btn--secondary';
318 rejectBtn.textContent = options.buttons.reject;
319 rejectBtn.addEventListener('click', () => handleRejectAll());
320
321 const acceptAllBtn = document.createElement('button');
322 acceptAllBtn.type = 'button';
323 acceptAllBtn.className = 'king-addons-cookie-consent__btn king-addons-cookie-consent__btn--secondary';
324 acceptAllBtn.textContent = options.buttons.accept;
325 acceptAllBtn.addEventListener('click', () => handleAcceptAll());
326
327 const saveBtn = document.createElement('button');
328 saveBtn.type = 'button';
329 saveBtn.className = 'king-addons-cookie-consent__btn king-addons-cookie-consent__btn--primary';
330 saveBtn.textContent = options.buttons.save;
331 saveBtn.addEventListener('click', () => {
332 const selected = [...categoriesWrap.querySelectorAll('input[type="checkbox"]')]
333 .filter((input) => input.checked)
334 .map((input) => input.value);
335 handleSave(selected, 'custom');
336 });
337
338 footerActions.append(rejectBtn, acceptAllBtn, saveBtn);
339 footer.appendChild(footerActions);
340
341 inner.append(header, body, footer);
342 modalEl.appendChild(inner);
343 };
344
345 /**
346 * Consent handlers
347 */
348 const applyConsent = (categoriesAllowed, actionType) => {
349 const consent = {
350 categories: categoriesAllowed,
351 version: options.policyVersion,
352 timestamp: Date.now(),
353 };
354 storage.save(consent);
355 activateScripts(categoriesAllowed);
356 activateDataAttributes(categoriesAllowed);
357 runManualBlocks(categoriesAllowed);
358
359 // Push to dataLayer for GTM integration
360 if (window.dataLayer && isPremium) {
361 window.dataLayer.push({
362 event: 'consent_update',
363 consentCategories: categoriesAllowed,
364 consentAction: actionType,
365 });
366 }
367
368 if (logsEnabled) {
369 logEvent(actionType, categoriesAllowed);
370 }
371 };
372
373 const hideAll = () => {
374 bannerEl.classList.remove('is-visible');
375 modalEl.classList.remove('is-visible');
376 document.body.style.overflow = '';
377 };
378
379 const handleAcceptAll = () => {
380 const allowed = categories.map((cat) => cat.key);
381 applyConsent(allowed, 'accept');
382 hideAll();
383 };
384
385 const handleRejectAll = () => {
386 const allowed = [...necessaryKeys];
387 applyConsent(allowed, 'reject');
388 hideAll();
389 };
390
391 const handleSave = (selected, actionType = 'custom') => {
392 const allowed = new Set(necessaryKeys);
393 selected.forEach((cat) => allowed.add(cat));
394 applyConsent(Array.from(allowed), actionType);
395 hideAll();
396 };
397
398 /**
399 * Check if we should re-show the banner
400 */
401 const shouldReshow = (stored) => {
402 if (!stored) {
403 return true;
404 }
405
406 // If stored timestamp is older than lifetime (mainly for localStorage), re-show.
407 if (stored.timestamp) {
408 const ts = parseInt(stored.timestamp, 10);
409 if (!Number.isNaN(ts)) {
410 const maxAgeMs = consentLifetime * 24 * 60 * 60 * 1000;
411 if (Date.now() - ts > maxAgeMs) {
412 return true;
413 }
414 }
415 }
416
417 // Check policy version, if resurface is enabled.
418 const resurface = options?.behavior?.resurface || 'version';
419 if (resurface !== 'never') {
420 if (stored.version !== options.policyVersion) {
421 return true;
422 }
423 }
424
425 return false;
426 };
427
428 /**
429 * Activate blocked scripts for allowed categories
430 */
431 const activateScripts = (allowed) => {
432 const scripts = document.querySelectorAll('script[type="text/plain"][data-ka-cookie-category]');
433 scripts.forEach((script) => {
434 const category = script.getAttribute('data-ka-cookie-category') || 'analytics';
435 if (!allowed.includes(category)) {
436 return;
437 }
438
439 const clone = document.createElement('script');
440
441 const originalType = script.getAttribute('data-ka-cookie-original-type');
442 clone.type = originalType || 'text/javascript';
443 for (const attr of script.attributes) {
444 if (attr.name === 'type' || attr.name === 'data-ka-cookie-category') {
445 continue;
446 }
447 if (attr.name === 'data-ka-cookie-original-type') {
448 continue;
449 }
450 if (attr.name === 'data-ka-cookie-src') {
451 continue;
452 }
453 clone.setAttribute(attr.name, attr.value);
454 }
455
456 const dataSrc = script.getAttribute('data-ka-cookie-src');
457 if (dataSrc) {
458 clone.src = dataSrc;
459 } else if (script.src) {
460 clone.src = script.src;
461 }
462 if (script.textContent) {
463 clone.textContent = script.textContent;
464 }
465 script.parentNode.replaceChild(clone, script);
466 });
467 };
468
469 /**
470 * Activate elements that opt-in via data attributes (Pro).
471 * Expected usage: set real src/href/etc in data-ka-cookie-* attributes and add data-ka-cookie-category.
472 */
473 const activateDataAttributes = (allowed) => {
474 if (!isPremium || !options.dataAttributes) {
475 return;
476 }
477
478 const elements = document.querySelectorAll('[data-ka-cookie-category]');
479 elements.forEach((el) => {
480 if (!el || el.tagName.toLowerCase() === 'script') {
481 return;
482 }
483
484 const category = (el.getAttribute('data-ka-cookie-category') || 'analytics').toString();
485 if (!allowed.includes(category) && category !== 'necessary') {
486 return;
487 }
488
489 const swaps = [
490 ['data-ka-cookie-src', 'src'],
491 ['data-ka-cookie-srcset', 'srcset'],
492 ['data-ka-cookie-href', 'href'],
493 ['data-ka-cookie-poster', 'poster'],
494 ];
495
496 swaps.forEach(([from, to]) => {
497 const val = el.getAttribute(from);
498 if (val) {
499 el.setAttribute(to, val);
500 el.removeAttribute(from);
501 }
502 });
503
504 if (el.hasAttribute('data-ka-cookie-show')) {
505 el.removeAttribute('hidden');
506 el.style.display = '';
507 el.removeAttribute('data-ka-cookie-show');
508 }
509 });
510 };
511
512 /**
513 * Execute manual code blocks (Pro)
514 */
515 const runManualBlocks = (allowed) => {
516 if (!isPremium || !options.manualBlocks || !options.manualBlocks.length) {
517 return;
518 }
519 options.manualBlocks.forEach((block) => {
520 if (!allowed.includes(block.category)) {
521 return;
522 }
523 if (block.type === 'html') {
524 const wrapper = document.createElement('div');
525 wrapper.innerHTML = block.code;
526 document.body.appendChild(wrapper);
527 } else {
528 const script = document.createElement('script');
529 script.type = 'text/javascript';
530 script.textContent = block.code;
531 document.body.appendChild(script);
532 }
533 });
534 };
535
536 /**
537 * Log consent event to server (Pro)
538 */
539 const logEvent = (actionType, allowed) => {
540 if (!config.ajax || !config.ajax.url || !logsEnabled) {
541 return;
542 }
543 const payload = new URLSearchParams();
544 payload.set('action', 'king_addons_cookie_consent_log');
545 payload.set('actionType', actionType);
546 allowed.forEach((category) => payload.append('categories[]', category));
547 payload.set('region', region);
548 payload.set('device', window.innerWidth < 768 ? 'mobile' : 'desktop');
549 payload.set('_ajax_nonce', config.ajax.nonce);
550
551 fetch(config.ajax.url, {
552 method: 'POST',
553 credentials: 'same-origin',
554 headers: {
555 'Content-Type': 'application/x-www-form-urlencoded',
556 },
557 body: payload.toString(),
558 }).catch(() => {
559 // Silently ignore logging errors
560 });
561 };
562
563 /**
564 * Modal controls
565 */
566 const openModal = () => {
567 modalEl.classList.add('is-visible');
568 document.body.style.overflow = 'hidden';
569 };
570
571 const closeModal = () => {
572 modalEl.classList.remove('is-visible');
573 document.body.style.overflow = '';
574 };
575
576 // Global function to open consent modal
577 window.kingAddonsOpenConsent = () => {
578 openModal();
579 };
580
581 // Custom event listener
582 document.addEventListener('king-addons-open-cookie-settings', () => {
583 openModal();
584 });
585
586 /**
587 * Set up manage buttons
588 */
589 const setupManageButtons = () => {
590 document.querySelectorAll('[data-ka-cookie-manage]').forEach((btn) => {
591 btn.addEventListener('click', (e) => {
592 e.preventDefault();
593 openModal();
594 });
595 });
596 };
597
598 /**
599 * Bind Pro behavior triggers (scroll/click consent)
600 */
601 const bindBehaviorTriggers = () => {
602 if (!isPremium) {
603 return;
604 }
605
606 if (options.behavior.scroll_consent) {
607 const scrollHandler = () => {
608 window.removeEventListener('scroll', scrollHandler);
609 handleAcceptAll();
610 };
611 window.addEventListener('scroll', scrollHandler, { once: true, passive: true });
612 }
613
614 if (options.behavior.click_consent) {
615 const clickHandler = (event) => {
616 if (container.contains(event.target)) {
617 return;
618 }
619 document.removeEventListener('click', clickHandler);
620 handleAcceptAll();
621 };
622 setTimeout(() => {
623 document.addEventListener('click', clickHandler);
624 }, 1000);
625 }
626 };
627
628 /**
629 * Initialize the consent system
630 */
631 const init = () => {
632 setCSSVariables();
633 applyLayoutClass();
634 buildBanner();
635 buildModal();
636 setupManageButtons();
637
638 const stored = storage.read();
639 if (!shouldReshow(stored)) {
640 // Already consented, activate scripts
641 const allowed = stored.categories || [];
642 activateScripts(allowed);
643 activateDataAttributes(allowed);
644 runManualBlocks(allowed);
645 return;
646 }
647
648 // Show the banner
649 bindBehaviorTriggers();
650 bannerEl.classList.add('is-visible');
651 };
652
653 // Initialize when DOM is ready
654 if (document.readyState === 'loading') {
655 document.addEventListener('DOMContentLoaded', init);
656 } else {
657 init();
658 }
659 })();
660