1document.addEventListener('DOMContentLoaded', function() { 2 var floatingHideStorageKey = 'gdpr_floating_btn_hidden'; 3 var cookieName = (window.cnArgs && cnArgs.cookieName) ? cnArgs.cookieName : 'opmc_gdpr_accepted'; 4 // Initialize script unblocking on page load based on currently accepted cookies 5 releaseScripts(); 6 7 // Show banner if user has not yet made a consent choice 8 if (!hasUserConsented()) { 9 slideDownNotice(); 10 } 11 12 // Sync floating button visibility on load 13 syncFloatingButton(); 14 15 // Utility to handle AJAX without jQuery 16 function sendConsentAction(action, data) { 17 data = data || {}; 18 var payload = new URLSearchParams(); 19 payload.append('action', 'gdpr_cookie_action'); 20 payload.append('consent_action', action); 21 payload.append('nonce', cnArgs.gdpr_nonce); 22 23 for (var key in data) { 24 if (Object.prototype.hasOwnProperty.call(data, key)) { 25 var value = data[key]; 26 // Handle arrays (e.g. categories sent as repeated keys) 27 if (Array.isArray(value)) { 28 value.forEach(function(item) { 29 payload.append(key + '[]', item); 30 }); 31 } else { 32 payload.append(key, value); 33 } 34 } 35 } 36 37 return fetch(cnArgs.ajaxurl, { 38 method: 'POST', 39 body: payload, 40 credentials: 'same-origin', 41 headers: { 42 'Content-Type': 'application/x-www-form-urlencoded' 43 } 44 }).then(function(res) { 45 return res.text().then(function(text) { 46 var parsed;
47 try { 48 parsed = text ? JSON.parse(text) : {}; 49 } catch (err) { 50 parsed = null; 51 } 52 53 if (!res.ok || !parsed) { 54 throw new Error('Consent request failed'); 55 } 56 57 return parsed; 58 }); 59 }); 60 } 61 62 function slideUpNotice() { 63 var notice = document.getElementById('cookie-notice'); 64 if (notice) { 65 notice.style.display = 'none'; 66 } 67 } 68 69 function slideDownNotice() { 70 var notice = document.getElementById('cookie-notice'); 71 if (notice) { 72 notice.style.display = 'block'; 73 } 74 } 75 76 function hasUserConsented() { 77 if (!window.cnArgs) { 78 return hasLegacyConsentCookie(); 79 } 80 81 // A stored timestamp indicates the user has previously made a choice. 82 if ( cnArgs.consent_payload && !!cnArgs.consent_payload.timestamp ) { 83 return true; 84 } 85 86 if ( cnArgs.hasConsent ) { 87 return true; 88 } 89 90 return hasLegacyConsentCookie(); 91 } 92 93 function hasLegacyConsentCookie() { 94 return readCookie(cookieName) !== null; 95 } 96 97 function readCookie(name) { 98 var parts = ('; ' + document.cookie).split('; ' + name + '='); 99 if (parts.length === 2) { 100 return parts.pop().split(';').shift(); 101 } 102 return null; 103 } 104 105 function setLegacyConsentCookie(isAccepted) { 106 var expires = new Date(); 107 expires.setTime(expires.getTime() + parseInt(cnArgs.cookieTime || 0, 10) * 1000); 108 document.cookie = cookieName + '=' + (isAccepted ? 'true' : 'false') + ';expires=' + expires.toUTCString() + ';' + 109 (cnArgs.cookieDomain ? 'domain=' + cnArgs.cookieDomain + ';' : '') + 110 (cnArgs.cookiePath ? 'path=' + cnArgs.cookiePath + ';' : 'path=/;') + 111 (cnArgs.secure === 1 || cnArgs.secure === '1' ? 'secure;' : ''); 112 } 113 114 function getFallbackConsentPayload(acceptedCategories) { 115 var categories = cnArgs.cookie_categories || {}; 116 var consent = {}; 117 acceptedCategories = acceptedCategories || []; 118 119 Object.keys(categories).forEach(function(key) { 120 consent[key] = !!categories[key].required || acceptedCategories.indexOf(key) !== -1; 121 }); 122 123 consent.timestamp = Math.floor(Date.now() / 1000); 124 consent.version = (cnArgs.consent_payload && cnArgs.consent_payload.version) || '1.0'; 125 126 return consent; 127 } 128 129 /** 130 * Show or hide the persistent floating "Cookie Settings" button. 131 * It should be visible whenever the banner is hidden (i.e. user has consented). 132 */ 133 function syncFloatingButton() { 134 var btn = document.getElementById('gdpr-floating-btn'); 135 if (!btn) return; 136 if (hasUserConsented() && !isFloatingButtonHidden()) { 137 btn.style.display = 'block'; 138 btn.setAttribute('aria-hidden', 'false'); 139 } else { 140 btn.style.display = 'none'; 141 btn.setAttribute('aria-hidden', 'true'); 142 } 143 } 144 145 function isFloatingButtonHidden() { 146 try { 147 return window.localStorage.getItem(floatingHideStorageKey) === '1'; 148 } catch (e) { 149 return false; 150 } 151 } 152 153 function hideFloatingButtonForUser() { 154 try { 155 window.localStorage.setItem(floatingHideStorageKey, '1'); 156 } catch (e) { 157 // Ignore storage errors in restricted modes. 158 } 159 syncFloatingButton(); 160 } 161 162 // ----------------------------------------------------------------------- 163 // Preferences Modal 164 // ----------------------------------------------------------------------- 165 166 function openPreferencesModal() { 167 var modal = document.getElementById('gdpr-preferences-modal'); 168 var strings = cnArgs.strings || {}; 169 if (!modal) { 170 modal = buildPreferencesModal(); 171 document.body.appendChild(modal); 172 } 173 174 // Reset action buttons every time modal opens (modal DOM is reused). 175 var saveBtn = modal.querySelector('#gdpr-save-preferences'); 176 if (saveBtn) { 177 saveBtn.disabled = false; 178 saveBtn.textContent = strings.save_preferences || 'Save Preferences'; 179 } 180 var acceptAllBtn = modal.querySelector('#gdpr-modal-accept-all'); 181 if (acceptAllBtn) { 182 acceptAllBtn.disabled = false; 183 } 184 185 syncCheckboxesToCurrentConsent(modal); 186 modal.style.display = 'flex'; 187 document.body.classList.add('gdpr-modal-open'); 188 } 189 190 function closePreferencesModal() { 191 var modal = document.getElementById('gdpr-preferences-modal'); 192 if (modal) { 193 modal.style.display = 'none'; 194 } 195 document.body.classList.remove('gdpr-modal-open'); 196 } 197 198 /** 199 * Build the preferences modal DOM from cnArgs.cookie_categories. 200 */ 201 function buildPreferencesModal() { 202 var categories = cnArgs.cookie_categories || {}; 203 var strings = cnArgs.strings || {}; 204
205 var overlay = document.createElement('div'); 206 overlay.id = 'gdpr-preferences-modal'; 207 overlay.setAttribute('role', 'dialog'); 208 overlay.setAttribute('aria-modal', 'true'); 209 overlay.setAttribute('aria-labelledby', 'gdpr-modal-title'); 210 211 var box = document.createElement('div'); 212 box.className = 'gdpr-modal-box'; 213 214 // --- Header --- 215 var header = document.createElement('div'); 216 header.className = 'gdpr-modal-header'; 217 218 var title = document.createElement('h2'); 219 title.id = 'gdpr-modal-title'; 220 title.textContent = strings.modal_title || 'Cookie Preferences'; 221 222 var closeBtn = document.createElement('button'); 223 closeBtn.className = 'gdpr-modal-close'; 224 closeBtn.setAttribute('aria-label', 'Close'); 225 closeBtn.innerHTML = '×'; 226 closeBtn.addEventListener('click', closePreferencesModal); 227 228 header.appendChild(title); 229 header.appendChild(closeBtn); 230 231 // --- Body --- 232 var body = document.createElement('div'); 233 body.className = 'gdpr-modal-body'; 234 235 var intro = document.createElement('p'); 236 intro.className = 'gdpr-modal-intro'; 237 intro.textContent = strings.modal_intro || 238 'Manage your cookie preferences below. Essential cookies cannot be disabled as they are required for the site to function.'; 239 body.appendChild(intro); 240 241 var list = document.createElement('ul'); 242 list.className = 'gdpr-category-list'; 243 244 Object.keys(categories).forEach(function(key) { 245 var cat = categories[key]; 246 247 var item = document.createElement('li'); 248 item.className = 'gdpr-category-item'; 249 250 var row = document.createElement('div'); 251 row.className = 'gdpr-category-row'; 252 253 var labelWrap = document.createElement('label'); 254 labelWrap.className = 'gdpr-category-label'; 255 labelWrap.setAttribute('for', 'gdpr-cat-' + key); 256 257 var checkbox = document.createElement('input'); 258 checkbox.type = 'checkbox'; 259 checkbox.id = 'gdpr-cat-' + key; 260 checkbox.value = key; 261 checkbox.className = 'gdpr-category-checkbox'; 262 263 if (cat.required) { 264 checkbox.checked = true; 265 checkbox.disabled = true; 266 } 267 268 var labelText = document.createElement('span'); 269 labelText.className = 'gdpr-category-name'; 270 labelText.textContent = cat.label || key; 271 272 labelWrap.appendChild(checkbox); 273 labelWrap.appendChild(labelText); 274 275 var statusBadge = document.createElement('span'); 276 if (cat.required) { 277 statusBadge.className = 'gdpr-status-badge gdpr-always-active'; 278 statusBadge.textContent = strings.always_active || 'Always Active'; 279 } else { 280 statusBadge.className = 'gdpr-status-badge gdpr-status-toggle'; 281 statusBadge.setAttribute('data-cat', key); 282 statusBadge.textContent = strings.inactive || 'Inactive'; 283 } 284 285 row.appendChild(labelWrap); 286 row.appendChild(statusBadge); 287 288 var desc = document.createElement('p'); 289 desc.className = 'gdpr-category-desc'; 290 desc.textContent = cat.description || ''; 291 292 item.appendChild(row); 293 item.appendChild(desc); 294 list.appendChild(item); 295 296 // Keep status badge text in sync with checkbox state 297 if (!cat.required) { 298 checkbox.addEventListener('change', function() { 299 updateStatusBadge(key, checkbox.checked, strings); 300 }); 301 } 302 }); 303 304 body.appendChild(list); 305 306 // --- Footer --- 307 var footer = document.createElement('div'); 308 footer.className = 'gdpr-modal-footer'; 309 310 var saveBtn = document.createElement('button'); 311 saveBtn.id = 'gdpr-save-preferences'; 312 saveBtn.className = 'gdpr-btn gdpr-btn-primary'; 313 saveBtn.textContent = strings.save_preferences || 'Save Preferences'; 314 315 var acceptAllBtn = document.createElement('button'); 316 acceptAllBtn.id = 'gdpr-modal-accept-all'; 317 acceptAllBtn.className = 'gdpr-btn gdpr-btn-secondary'; 318 acceptAllBtn.textContent = strings.accept_all || 'Accept All'; 319 320 footer.appendChild(saveBtn); 321 footer.appendChild(acceptAllBtn); 322 323 box.appendChild(header); 324 box.appendChild(body); 325 box.appendChild(footer); 326 overlay.appendChild(box); 327 328 // --- Save Preferences --- 329 saveBtn.addEventListener('click', function() { 330 var checked = overlay.querySelectorAll('.gdpr-category-checkbox:checked:not(:disabled)'); 331 var selectedCategories = []; 332 checked.forEach(function(cb) { 333 selectedCategories.push(cb.value); 334 }); 335 336 saveBtn.disabled = true; 337 saveBtn.textContent = strings.saving || 'Saving...'; 338 setLegacyConsentCookie(false); 339 cnArgs.hasConsent = true; 340 341 // 'categories' key becomes 'categories[]' inside sendConsentAction 342 // so PHP receives it as $_POST['categories'] array 343 sendConsentAction('save_preferences', { categories: selectedCategories }) 344 .then(function(response) { 345 if (response.success) { 346 cnArgs.consent_payload = response.data.consent; 347 releaseScripts(); 348 saveBtn.disabled = false;
349 saveBtn.textContent = strings.save_preferences || 'Save Preferences'; 350 closePreferencesModal(); 351 slideUpNotice(); 352 syncFloatingButton(); 353 dispatchSpecificEvent('gdpr_preferences_saved', response.data.consent); 354 } else { 355 // AJAX returned but reported failure â re-enable button 356 cnArgs.consent_payload = getFallbackConsentPayload(selectedCategories); 357 slideUpNotice(); 358 syncFloatingButton(); 359 closePreferencesModal(); 360 saveBtn.disabled = false; 361 saveBtn.textContent = strings.save_preferences || 'Save Preferences'; 362 } 363 }, function() { 364 // Network / parse error â re-enable button 365 cnArgs.consent_payload = getFallbackConsentPayload(selectedCategories); 366 slideUpNotice(); 367 syncFloatingButton(); 368 closePreferencesModal(); 369 saveBtn.disabled = false; 370 saveBtn.textContent = strings.save_preferences || 'Save Preferences'; 371 }); 372 }); 373 374 // --- Accept All from inside modal --- 375 acceptAllBtn.addEventListener('click', function() { 376 acceptAllBtn.disabled = true; 377 setLegacyConsentCookie(true); 378 cnArgs.hasConsent = true; 379 380 sendConsentAction('accept_all').then(function(response) { 381 if (response.success) { 382 cnArgs.consent_payload = response.data.consent; 383 releaseScripts(); 384 closePreferencesModal(); 385 slideUpNotice(); 386 syncFloatingButton(); 387 dispatchSpecificEvent('gdpr_consent_accepted', response.data.consent); 388 389 if (cnArgs.redirection) { 390 window.location.reload(); 391 } 392 } else { 393 cnArgs.consent_payload = getFallbackConsentPayload(Object.keys(cnArgs.cookie_categories || {})); 394 releaseScripts(); 395 closePreferencesModal(); 396 slideUpNotice(); 397 syncFloatingButton(); 398 } 399 acceptAllBtn.disabled = false; 400 }, function() { 401 cnArgs.consent_payload = getFallbackConsentPayload(Object.keys(cnArgs.cookie_categories || {})); 402 releaseScripts(); 403 closePreferencesModal(); 404 slideUpNotice(); 405 syncFloatingButton(); 406 acceptAllBtn.disabled = false; 407 }); 408 }); 409 410 // Close on overlay backdrop click 411 overlay.addEventListener('click', function(e) { 412 if (e.target === overlay) { 413 closePreferencesModal(); 414 } 415 }); 416 417 return overlay; 418 } 419 420 /** 421 * Update the status badge text for a category. 422 */ 423 function updateStatusBadge(key, isActive, strings) { 424 strings = strings || cnArgs.strings || {}; 425 var badge = document.querySelector('.gdpr-status-badge[data-cat="' + key + '"]'); 426 if (badge) { 427 if (isActive) { 428 badge.classList.add('gdpr-is-active'); 429 badge.textContent = strings.active || 'Active'; 430 } else { 431 badge.classList.remove('gdpr-is-active'); 432 badge.textContent = strings.inactive || 'Inactive'; 433 } 434 } 435 } 436 437 /** 438 * Sync checkbox states to the current saved consent payload. 439 */ 440 function syncCheckboxesToCurrentConsent(modal) { 441 var consent = cnArgs.consent_payload || {}; 442 var strings = cnArgs.strings || {}; 443 444 var checkboxes = modal.querySelectorAll('.gdpr-category-checkbox:not(:disabled)'); 445 checkboxes.forEach(function(cb) { 446 var isActive = consent[cb.value] === true; 447 cb.checked = isActive; 448 updateStatusBadge(cb.value, isActive, strings); 449 }); 450 } 451 452 // ----------------------------------------------------------------------- 453 // Banner Button Handlers 454 // ----------------------------------------------------------------------- 455 456 // Accept All 457 // stopImmediatePropagation prevents front.js jQuery handler from also firing 458 document.addEventListener('click', function(e) { 459 var target = e.target.closest('.cn-set-cookie[data-cookie-set="accept"]'); 460 if (!target) return; 461 e.preventDefault(); 462 e.stopImmediatePropagation(); 463 setLegacyConsentCookie(true); 464 cnArgs.hasConsent = true; 465 466 sendConsentAction('accept_all').then(function(response) { 467 if (response.success) { 468 slideUpNotice(); 469 cnArgs.consent_payload = response.data.consent; 470 releaseScripts(); 471 syncFloatingButton(); 472 dispatchSpecificEvent('gdpr_consent_accepted', response.data.consent); 473 474 if (cnArgs.redirection) { 475 window.location.reload(); 476 } 477 } else { 478 slideUpNotice(); 479 cnArgs.consent_payload = getFallbackConsentPayload(Object.keys(cnArgs.cookie_categories || {})); 480 releaseScripts(); 481 syncFloatingButton(); 482 } 483 }, function() { 484 slideUpNotice(); 485 cnArgs.consent_payload = getFallbackConsentPayload(Object.keys(cnArgs.cookie_categories || {})); 486 releaseScripts(); 487 syncFloatingButton(); 488 }); 489 }); 490 491 // Reject Non-Essential 492 // stopImmediatePropagation prevents front.js jQuery handler from also firing 493 document.addEventListener('click', function(e) { 494 var target = e.target.closest('.cn-set-cookie[data-cookie-set="refuse"]'); 495 if (!target) return; 496 e.preventDefault(); 497 e.stopImmediatePropagation(); 498 setLegacyConsentCookie(false); 499 cnArgs.hasConsent = true; 500 501 sendConsentAction('reject_non_essential').then(function(response) { 502 if (response.success) { 503 slideUpNotice(); 504 cnArgs.consent_payload = response.data.consent; 505 releaseScripts(); 506 syncFloatingButton(); 507 dispatchSpecificEvent('gdpr_consent_rejected', response.data.consent); 508 } else { 509 slideUpNotice(); 510 cnArgs.consent_payload = getFallbackConsentPayload([]); 511 releaseScripts(); 512 syncFloatingButton(); 513 } 514 }, function() { 515 slideUpNotice(); 516 cnArgs.consent_payload = getFallbackConsentPayload([]); 517 releaseScripts(); 518 syncFloatingButton(); 519 }); 520 }); 521 522 // Open Preferences Modal â any element with data-gdpr="preferences" or .cn-manage-preferences 523 document.addEventListener('click', function(e) { 524 var target = e.target.closest('[data-gdpr="preferences"], .cn-manage-preferences'); 525 if (!target) return; 526 e.preventDefault(); 527 openPreferencesModal(); 528 }); 529 530 // Hide floating button for this browser/user. 531 document.addEventListener('click', function(e) { 532 var target = e.target.closest('.gdpr-floating-hide'); 533 if (!target) return; 534 e.preventDefault(); 535 e.stopImmediatePropagation(); 536 hideFloatingButtonForUser(); 537 }); 538 539 // Revoke / re-show the banner 540 document.addEventListener('click', function(e) { 541 var target = e.target.closest('.cn-revoke-cookie'); 542 if (!target) return; 543 e.preventDefault(); 544 slideDownNotice(); 545 }); 546 547 // Close modal on Escape key 548 document.addEventListener('keydown', function(e) { 549 if (e.key === 'Escape') { 550 closePreferencesModal(); 551 } 552 }); 553 554 // ----------------------------------------------------------------------- 555 // Helpers 556 // ----------------------------------------------------------------------- 557 558 function dispatchSpecificEvent(eventName, payload) { 559 var event = new CustomEvent(eventName, { detail: payload }); 560 document.dispatchEvent(event); 561 } 562 563 // Script release logic 564 function releaseScripts() { 565 if (!cnArgs.consent_payload) { 566 return; 567 } 568 569 var scripts = document.querySelectorAll( 570 'script[type="text/plain"][data-cookie-category]:not([data-gdpr-executed="true"])' 571 ); 572
573 scripts.forEach(function(script) { 574 var category = script.getAttribute('data-cookie-category'); 575 576 if (cnArgs.consent_payload[category] === true || category === 'essential') { 577 var newScript = document.createElement('script'); 578 newScript.type = 'text/javascript'; 579 580 var src = script.getAttribute('src'); 581 if (src) { 582 newScript.src = src; 583 } 584 585 Array.from(script.attributes).forEach(function(attr) { 586 var name = attr.name; 587 if (name !== 'type' && name !== 'data-cookie-category' && name !== 'src') { 588 newScript.setAttribute(name, attr.value); 589 } 590 }); 591 592 if (script.innerHTML) { 593 newScript.innerHTML = script.innerHTML; 594 } 595 596 newScript.setAttribute('data-gdpr-executed', 'true'); 597 598 if (script.parentNode) { 599 script.parentNode.replaceChild(newScript, script); 600 } 601 } 602 }); 603 604 dispatchSpecificEvent('gdpr_consent_updated', cnArgs.consent_payload); 605 } 606});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.