PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.44
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.44
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.44, at includes/extensions/Cookie_Consent/assets/script.js

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