1/** 2 * WordPress localize object mapped to a constant. 3 */ 4const _ckyStore = window._ckyConfig; 5const _ckyStyle = window._ckyStyles; 6 7_ckyStore._backupNodes = []; 8_ckyStore._resetConsentID = false; 9_ckyStore._bannerState = false; 10_ckyStore._preferenceOriginTag = false; 11_ckyStore._optoutSuccessCountdownInterval = null; 12_ckyStore._optoutSuccessAutoCloseTimer = null; 13_ckyStore._optoutSuccessSubtextTemplate = ""; 14 15const CKY_OPTOUT_SUCCESS_DISMISS_MS = 15000; 16const CKY_OPTOUT_SUCCESS_SECONDS = 15; 17 18window.cookieyes = window.cookieyes || {}; 19const ref = window.cookieyes; 20ref._ckyConsentStore = new Map(); 21 22ref._ckyGetCookieMap = function () { 23 const cookieMap = {}; 24 try { 25 document.cookie.split(";").map((cookie) => { 26 const [key, value] = cookie.split("="); 27 if (!key) return; 28 cookieMap[key.trim()] = value; 29 return value; 30 }); 31 } catch (error) { } 32 return cookieMap; 33}; 34 35const currentCookieMap = ref._ckyGetCookieMap(); 36ref._ckyGetFromStore = function (key) { 37 return ref._ckyConsentStore.get(key) || ""; 38}; 39 40ref._ckySetInStore = function (key, value) { 41 ref._ckyConsentStore.set(key, value); 42 let cookieStringArray = []; 43 for (const [key, value] of ref._ckyConsentStore) { 44 cookieStringArray.push(`${key}:${value}`); 45 } 46 const scriptExpiry = 47 _ckyStore && _ckyStore._expiry 48 ? _ckyStore._expiry 49 : 365; 50 ref._ckySetCookie( 51 "cookieyes-consent", 52 cookieStringArray.join(","), 53 scriptExpiry 54 ); 55}; 56 57const cookieyesConsentMap = (currentCookieMap["cookieyes-consent"] || "") 58 .split(",") 59 .reduce((prev, curr) => { 60 if (!curr) return prev; 61 const [key, value] = curr.split(":"); 62 prev[key] = value; 63 return prev; 64 }, {}); 65["consentid", "consent", "action"] 66 .concat(_ckyStore._categories.map(({ slug }) => slug)) 67 .map((item) => 68 ref._ckyConsentStore.set(item, cookieyesConsentMap[item] || "") 69 ); 70 71 72/** 73 * Get the value of cookie by it's name. 74 * 75 * @param {string} name Name of the cookie 76 * @returns {string} 77 */ 78ref._ckyGetCookie = function (name) { 79 const value = new RegExp(`${name}=([^;]+)`).exec(document.cookie); 80 return value && Array.isArray(value) && value[1] ? unescape(value[1]) : null; 81} 82 83/** 84 * Set a cookie on document.cookie object. 85 * 86 * @param {*} name Name of the cookie. 87 * @param {*} value Value to be set. 88 * @param {*} days Expiry in days. 89 * @param {*} domain Cookie domain. 90 */ 91ref._ckySetCookie = function (name, value, days = 0, domain = _ckyStore._rootDomain) { 92 const date = new Date(); 93 if (!!domain) { 94 domain = `domain=${domain}`; 95 } 96 const toSetTime = 97 days === 0 ? 0 : date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); 98 document.cookie = `${name}=${value}; expires=${new Date( 99 toSetTime 100 ).toUTCString()}; path=/;${domain}; SameSite=Strict;`; 101} 102 103function _ckySetConsentID() { 104 const cookieyesID = ref._ckyGetFromStore("consentid"); 105 if (cookieyesID) return; 106 const consentID = ref._ckyRandomString(32); 107 ref._ckySetInStore("consentid", consentID); 108 _ckyStore._resetConsentID = true; 109} 110 111const _revisitCkyConsent = function () { 112 const type = _ckyGetType(); 113 if (type === 'classic') { 114 _ckyShowBanner(); 115 } 116 _ckySetPreferenceAction(); 117 _ckyToggleRevisit(); 118}; 119/** 120 * Search an element by it's data-cky-tag attribute 121 * 122 * @param {string} tag data-cky-tag of an element. 123 * @returns {object} 124 */ 125function _ckyGetElementByTag(tag) { 126 const item = document.querySelector('[data-cky-tag=' + tag + ']'); 127 return item ? item : false; 128} 129 130function _ckyEscapeRegex(literal) { 131 return literal.replace(/[.*+?^${}()[\]\\]/g, "\\$&"); 132} 133 134/** 135 * Bind click event to banner elements. 136 * 137 * @param {string} tag data-cky-tag of the element 138 * @param {function} fn callback function 139 */ 140function _ckyAttachListener(selector, fn) { 141 const item = _ckyFindElement(selector); 142 item && item.addEventListener("click", fn); 143} 144 145function _ckyClassAdd() { 146 return _ckyClassAction("add", ...arguments); 147} 148 149function _ckyClassRemove() { 150 return _ckyClassAction("remove", ...arguments); 151} 152
153function _ckyClassToggle() { 154 return _ckyClassAction("toggle", ...arguments); 155} 156 157function _ckyClassAction(action, selector, className, forParent = true) { 158 const item = _ckyFindElement(selector, forParent); 159 return item && item.classList[action](className); 160} 161 162function _ckyFindElement(selector, forParent) { 163 let createdSelector = selector; 164 switch (true) { 165 case selector.startsWith("="): 166 createdSelector = `[data-cky-tag="${selector.substring(1)}"]`; 167 break; 168 default: 169 break; 170 } 171 const element = document.querySelector(createdSelector); 172 if (!element || (forParent && !element.parentElement)) return null; 173 return forParent ? element.parentElement : element; 174} 175/** 176 * Remove an element from the DOM. 177 * 178 * @param {string} tag data-cky-tag of the element. 179 */ 180function _ckyRemoveElement(tag) { 181 const item = _ckyGetElementByTag(tag); 182 item && item.remove(); 183} 184 185function _ckyFireEvent(responseCategories) { 186 const consentUpdate = new CustomEvent("cookieyes_consent_update", { 187 detail: responseCategories 188 }); 189 document.dispatchEvent(consentUpdate); 190} 191 192/** 193 * Remove styles by it's id. 194 */ 195function _ckyRemoveStyles() { 196 const item = document.getElementById('cky-style-inline'); 197 item && item.remove(); 198} 199 200/** 201 * Generate a random string for logging purposes. 202 * 203 * @param {integer} length Length of the string to be generated. 204 * @returns 205 */ 206ref._ckyRandomString = function (length, allChars = true) { 207 const chars = `${allChars ? `0123456789` : "" 208 }ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghiklmnopqrstuvwxyz`; 209 const response = []; 210 for (let i = 0; i < length; i++) 211 response.push(chars[Math.floor(Math.random() * chars.length)]); 212 if (!allChars) return response.join(""); 213 return btoa(response.join("")).replace(/\=+$/, ""); 214} 215 216/** 217 * Remove banner if necessary. 218 */ 219function _ckyRemoveBanner() { 220 _ckyHideBanner(); 221 if (_ckyStore._bannerConfig.config.revisitConsent.status === true) { 222 _ckyShowRevisit(); 223 } 224} 225 226/** 227 * Initialize the plugin front-end operations. 228 * 229 * @returns {boolean} 230 */ 231function _ckyInitOperations() { 232 _ckyAttachNoticeStyles(); 233 _ckyAttachShortCodeStyles(); 234 _ckyRenderBanner(); 235 _ckySetShowMoreLess(); 236 if (!ref._ckyGetFromStore("action") || _ckyPreviewEnabled()) { 237 _ckyShowBanner(); 238 _ckySetInitialState(); 239 _ckySetConsentID(); 240 } else { 241 _ckyRemoveBanner(); 242 } 243} 244function _ckyPreviewEnabled() { 245 let params = (new URL(document.location)).searchParams; 246 return params.get("cky_preview") && params.get("cky_preview") === 'true'; 247} 248function _ckyToggleAriaExpandStatus(selector, forceDefault = null) { 249 const element = _ckyFindElement(selector); 250 251 if (!element) return; 252 253 if (element.classList.contains('cky-accordion-btn')) { 254 const accordionItem = element.closest('.cky-accordion'); 255 if (accordionItem) { 256 const accordionBody = accordionItem.querySelector('.cky-accordion-body'); 257 if (accordionBody) { 258 // Generate unique ID for the accordion body if it doesn't have one 259 let bodyId = accordionBody.id; 260 if (!bodyId) { 261 bodyId = `ckyDetailCategory${accordionItem.id.replace('ckyDetailCategory', '')}Body`; 262 accordionBody.id = bodyId; 263 } 264 // Always set aria-controls - the relationship is permanent 265 element.setAttribute("aria-controls", bodyId); 266 } 267 } 268 } 269 270 const currentExpanded = element.getAttribute("aria-expanded"); 271 const newExpandedValue = forceDefault || (currentExpanded === "true" ? "false" : "true"); 272 element.setAttribute("aria-expanded", newExpandedValue); 273} 274/** 275 * Sets the initial state of the plugin. 276 */ 277function _ckySetInitialState() { 278 const activeLaw = _ckyGetLaw() 279 ref._ckySetInStore("consent", "no"); 280 const ccpaCheckBoxValue = _ckyFindCheckBoxValue(); 281 const responseCategories = { accepted: [], rejected: [] }; 282 let valueToSet = "yes"; 283 for (const category of _ckyStore._categories) { 284 if ( 285 (activeLaw === "gdpr" && 286 !category.isNecessary && 287 !category.defaultConsent[activeLaw]) || 288 (activeLaw === "ccpa" && 289 ccpaCheckBoxValue && 290 !category.defaultConsent.ccpa) 291 ) { 292 valueToSet = "no"; 293 } 294 if (valueToSet === "no") responseCategories.rejected.push(category.slug); 295 else responseCategories.accepted.push(category.slug); 296 ref._ckySetInStore(`${category.slug}`, valueToSet); 297 } 298 _ckyUnblock(); 299 _ckyFireEvent(responseCategories); 300} 301 302/** 303 * Add a class based on the banner type and position. Eg: 'cky-banner-top' 304 * 305 * @returns {boolean} 306 */ 307function _ckyAddPositionClass() { 308 const notice = _ckyGetElementByTag('notice'); 309 if (!notice) return false;
310 const container = notice.closest('.cky-consent-container'); 311 if (!container) return false; 312 313 container.setAttribute("aria-label", "We value your privacy"); 314 container.setAttribute("role", "region"); 315 316 const type = _ckyStore._bannerConfig.settings.type; 317 let position = _ckyStore._bannerConfig.settings.position; 318 let bannerType = type; 319 if (bannerType === 'popup') { 320 position = 'center'; 321 } 322 bannerType = (_ckyGetPtype() === "pushdown") ? 'classic' : bannerType; 323 const noticeClass = `cky-${bannerType}-${position}`; 324 container.classList.add(noticeClass); 325 const revisitConsent = _ckyGetElementByTag('revisit-consent'); 326 if (!revisitConsent) return false; 327 const revisitPosition = 'cky-revisit-' + _ckyStore._bannerConfig.config.revisitConsent.position; 328 revisitConsent.classList.add(revisitPosition); 329} 330 331/** 332 * Add a class based on the preference center type and position. Eg: 'cky-sidebar-left' 333 * 334 * @returns {boolean} 335 */ 336function _ckyAddPreferenceCenterClass() { 337 const detail = _ckyGetLaw() === 'ccpa' ? _ckyGetElementByTag("optout-popup") : _ckyGetElementByTag("detail"); 338 if (!detail) return false; 339 const modal = detail.closest('.cky-modal'); 340 if (!modal) return false; 341 if (_ckyGetPtype() !== "pushdown" && _ckyGetPtype() !== "popup") { 342 const pType = _ckyStore._bannerConfig.settings.preferenceCenterType; 343 const modalClass = `cky-${pType}`; 344 modal.classList.add(modalClass); 345 } 346 347 // Ensure ARIA attributes are always present on the preference center div 348 const preferenceCenter = modal.querySelector('.cky-preference-center'); 349 if (preferenceCenter) { 350 const preferenceCenterId = _ckyGetLaw() === 'ccpa' ? 'ckyOptoutPreferenceCenter' : 'ckyPreferenceCenter'; 351 preferenceCenter.setAttribute('id', preferenceCenterId); 352 preferenceCenter.setAttribute('role', 'dialog'); 353 preferenceCenter.setAttribute('aria-modal', 'true'); 354 const ariaLabel = _ckyGetLaw() === 'ccpa' ? 'Opt-out Preferences' : 'Customise Consent Preferences'; 355 preferenceCenter.setAttribute('aria-label', ariaLabel); 356 } 357} 358 359/** 360 * Initialize the plugin operations. 361 */ 362async function _ckyInit() { 363 try { 364 _ckyInitOperations(); 365 _ckyRemoveAllDeadCookies(); 366 _ckyWatchBannerElement(); 367 } catch (err) { 368 console.error(err); 369 } 370} 371 372/** 373 * Domready event, alternative to jQuery(document).ready() function 374 * 375 * @param {function} callback 376 * @returns 377 */ 378function _ckyDomReady(callback) { 379 if (typeof document === 'undefined') { 380 return; 381 } 382 if (document.readyState === 'complete' || /** DOMContentLoaded + Images/Styles/etc loaded, so we call directly. */ 383 document.readyState === 'interactive' /** DOMContentLoaded fires at this point, so we call directly. */ 384 ) { 385 return void callback(); 386 } /** DOMContentLoaded has not fired yet, delay callback until then. */ 387 document.addEventListener('DOMContentLoaded', callback); 388} 389 390/** 391 * Callback function to Domready event. 392 */ 393_ckyDomReady(async function () { 394 try { 395 await _ckyInit(); 396 } catch (err) { 397 console.error(err); 398 } 399}); 400 401/** 402 * Register event handler for all the action elements. 403 */ 404function _ckyRegisterListeners() { 405 for (const { slug } of _ckyStore._categories) { 406 _ckyAttachListener('detail-category-title', () => 407 document 408 .getElementById(`ckyCategory${slug}`) 409 .classList.toggle("cky-tab-active") 410 ); 411 } 412 _ckyAttachListener("=settings-button", () => _ckySetPreferenceAction('settings-button')); 413 _ckyAttachListener("=detail-close", () => _ckyHidePreferenceCenter()); 414 _ckyAttachListener("=optout-cancel-button", () => _ckyHidePreferenceCenter()); 415 _ckyAttachListener("=close-button", () => _ckyActionClose()); 416 _ckyAttachListener("=donotsell-button", () => _ckySetPreferenceAction('donotsell-button')); 417 _ckyAttachListener("=reject-button", _ckyAcceptReject("reject")); 418 _ckyAttachListener("=accept-button", _ckyAcceptReject("all")); 419 _ckyAttachListener("=detail-accept-button", _ckyAcceptReject("all")); 420 _ckyAttachListener("=detail-save-button", _ckyAcceptReject()); 421 _ckyAttachListener("=detail-category-preview-save-button", _ckyAcceptReject()); 422 _ckyAttachListener("=optout-confirm-button", _ckyHandleOptoutConfirm()); 423 _ckyAttachListener("=detail-reject-button", _ckyAcceptReject("reject")); 424 _ckyAttachListener("=revisit-consent", () => _revisitCkyConsent()); 425 _ckyAttachListener("=optout-close", () => _ckyHandleOptoutPopupClose()); 426} 427 428function _ckyAttachCategoryListeners() { 429 if (!_ckyStore._bannerConfig.config.auditTable.status) return; 430 const categoryNames = _ckyStore._categories.map(({ slug }) => slug); 431 categoryNames.map((category) => { 432 const selector = `#ckyDetailCategory${category}`; 433 const accordionButtonSelector = `${selector} .cky-accordion-btn`;
434 435 // Set initial aria-controls and aria-expanded for accordion buttons 436 const accordionButton = document.querySelector(accordionButtonSelector); 437 if (accordionButton) { 438 const accordionItem = accordionButton.closest('.cky-accordion'); 439 if (accordionItem) { 440 const accordionBody = accordionItem.querySelector('.cky-accordion-body'); 441 if (accordionBody) { 442 // Generate unique ID for the accordion body if it doesn't have one 443 let bodyId = accordionBody.id; 444 if (!bodyId) { 445 bodyId = `ckyDetailCategory${accordionItem.id.replace('ckyDetailCategory', '')}Body`; 446 accordionBody.id = bodyId; 447 } 448 // Always set aria-controls - the relationship is permanent 449 accordionButton.setAttribute("aria-controls", bodyId); 450 } 451 } 452 } 453 454 _ckyToggleAriaExpandStatus(accordionButtonSelector, "false"); 455 _ckyAttachListener(selector, ({ target: { id } }) => { 456 if ( 457 id === `ckySwitch${category}` || 458 !_ckyClassToggle(selector, "cky-accordion-active", false) 459 ) { 460 _ckyToggleAriaExpandStatus(accordionButtonSelector, "false"); 461 return; 462 } 463 _ckyToggleAriaExpandStatus(accordionButtonSelector, "true"); 464 categoryNames 465 .filter((categoryName) => categoryName !== category) 466 .map(filteredName => { 467 _ckyClassRemove( 468 `#ckyDetailCategory${filteredName}`, 469 "cky-accordion-active", 470 false 471 ); 472 _ckyToggleAriaExpandStatus( 473 `#ckyDetailCategory${filteredName} .cky-accordion-btn`, 474 "false" 475 ); 476 return filteredName; 477 }); 478 }); 479 return category; 480 }); 481} 482/** 483 * Add support for accordion tabs on the privacy overview screen. 484 */ 485function _ckyInitiAccordionTabs() { 486 document.querySelectorAll(".cky-accordion").forEach((item) => ( 487 item.addEventListener('click', function (event) { 488 if (event.target.type === 'checkbox') return; 489 this.classList.toggle('cky-accordion-active'); 490 }) 491 )); 492} 493 494function _ckyToggleBanner(force = false) { 495 const notice = _ckyGetElementByTag('notice'); 496 const container = notice && notice.closest('.cky-consent-container') || false; 497 if (container) { 498 force === true ? container.classList.add('cky-hide') : container.classList.toggle('cky-hide'); 499 } 500 501} 502 503function _ckyToggleRevisit(force = false) { 504 const revisit = _ckyGetRevisit(); 505 if (revisit) { 506 force === true ? _ckyRevisitHide() : revisit.classList.toggle('cky-revisit-hide'); 507 } 508} 509function _ckyGetLaw() { 510 return _ckyStore._bannerConfig.settings.applicableLaw; 511} 512function _ckyGetType() { 513 return _ckyStore._bannerConfig.settings.type; 514} 515function _ckyGetPtype() { 516 if (_ckyGetType() === 'classic') { 517 return 'pushdown'; 518 } 519 return _ckyStore._bannerConfig.settings.preferenceCenterType; 520} 521function _ckyGetBanner() { 522 const notice = _ckyGetElementByTag('notice'); 523 const container = notice && notice.closest('.cky-consent-container') || false; 524 return container && container || false; 525} 526function _ckyHideBanner() { 527 const notice = _ckyGetBanner(); 528 notice && notice.classList.add('cky-hide'); 529} 530function _ckyShowBanner() { 531 const notice = _ckyGetBanner(); 532 notice && notice.classList.remove('cky-hide'); 533} 534function _ckyHideOverLay() { 535 const overlay = document.querySelector('.cky-overlay'); 536 overlay && overlay.classList.add('cky-hide'); 537} 538function _ckyShowOverLay() { 539 const overlay = document.querySelector('.cky-overlay'); 540 overlay && overlay.classList.remove('cky-hide'); 541} 542function _ckyToggleOverLay() { 543 const overlay = document.querySelector('.cky-overlay'); 544 overlay && overlay.classList.toggle('cky-hide'); 545} 546function _ckyGetPreferenceCenter() { 547 if (_ckyGetType() === 'classic') { 548 return _ckyGetBanner(); 549 } 550 let element = _ckyGetLaw() === 'ccpa' ? _ckyGetElementByTag("optout-popup") : _ckyGetElementByTag("detail"); 551 return element && element.closest('.cky-modal') || false; 552} 553function _ckyHidePreferenceCenter() { 554 _ckyResetOptoutSuccessMessage(); 555 const element = _ckyGetPreferenceCenter(); 556 element && element.classList.remove(_ckyGetPreferenceClass()); 557 558 // ARIA attributes remain always present - only aria-expanded on settings button changes 559 // The modal relationship is permanent, only visibility changes 560 561 if (_ckyGetType() !== 'classic') { 562 _ckyHideOverLay();
563 if (!ref._ckyGetFromStore("action")) _ckyShowBanner(); 564 } else { 565 _ckyToggleAriaExpandStatus("=settings-button", "false"); 566 _ckyClassRemove("=notice", "cky-consent-bar-expand"); 567 } 568 if (ref._ckyGetFromStore("action")) _ckyShowRevisit(); 569 const origin = _ckyStore._preferenceOriginTag; 570 origin && _ckySetFocus(origin) 571} 572function _ckyShowPreferenceCenter() { 573 const element = _ckyGetPreferenceCenter(); 574 element && element.classList.add(_ckyGetPreferenceClass()); 575 576 // Ensure ARIA attributes are always present on the preference center div 577 if (element) { 578 const preferenceCenter = element.querySelector('.cky-preference-center'); 579 if (preferenceCenter) { 580 const preferenceCenterId = _ckyGetLaw() === 'ccpa' ? 'ckyOptoutPreferenceCenter' : 'ckyPreferenceCenter'; 581 preferenceCenter.setAttribute('id', preferenceCenterId); 582 preferenceCenter.setAttribute('role', 'dialog'); 583 preferenceCenter.setAttribute('aria-modal', 'true'); 584 const ariaLabel = _ckyGetLaw() === 'ccpa' ? 'Opt-out Preferences' : 'Customise Consent Preferences'; 585 preferenceCenter.setAttribute('aria-label', ariaLabel); 586 } 587 } 588 589 if (_ckyGetType() !== 'classic') { 590 _ckyShowOverLay(); 591 _ckyHideBanner(); 592 } else { 593 _ckyToggleAriaExpandStatus("=settings-button"); 594 _ckyClassAdd("=notice", "cky-consent-bar-expand"); 595 } 596} 597function _ckyTogglePreferenceCenter() { 598 const element = _ckyGetPreferenceCenter(); 599 if (!element) return; 600 element.classList.toggle(_ckyGetPreferenceClass()); 601 if (_ckyGetPtype() !== 'pushdown') _ckyToggleOverLay(); 602 603 const isOpen = element.classList.contains(_ckyGetPreferenceClass()); 604 element.classList.toggle(_ckyGetPreferenceClass()); 605 if (_ckyGetType() === 'classic') { 606 const preferenceCenter = element.querySelector('.cky-preference-center'); 607 if (preferenceCenter) { 608 const preferenceCenterId = _ckyGetLaw() === 'ccpa' ? 'ckyOptoutPreferenceCenter' : 'ckyPreferenceCenter'; 609 preferenceCenter.setAttribute('id', preferenceCenterId); 610 preferenceCenter.setAttribute('role', 'dialog'); 611 preferenceCenter.setAttribute('aria-modal', 'true'); 612 const ariaLabel = _ckyGetLaw() === 'ccpa' ? 'Opt-out Preferences' : 'Customise Consent Preferences'; 613 preferenceCenter.setAttribute('aria-label', ariaLabel); 614 } 615 _ckyToggleAriaExpandStatus("=settings-button"); 616 _ckyClassToggle("=notice", "cky-consent-bar-expand"); 617 } else { 618 if (!isOpen) { 619 _ckyShowOverLay(); 620 _ckyHideBanner(); 621 } else { 622 _ckyHideOverLay(); 623 if (!ref._ckyGetFromStore("action")) _ckyShowBanner(); 624 } 625 } 626 if (ref._ckyGetFromStore("action")) _ckyShowRevisit(); 627 const origin = _ckyStore._preferenceOriginTag; 628 origin && _ckySetFocus(origin) 629} 630function _ckyGetPreferenceClass() { 631 return _ckyGetPtype() === 'pushdown' ? 'cky-consent-bar-expand' : 'cky-modal-open'; 632} 633 634function _ckyGetRevisit() { 635 const revisit = _ckyGetElementByTag('revisit-consent'); 636 return revisit && revisit || false; 637} 638function _ckyHideRevisit() { 639 const revisit = _ckyGetRevisit(); 640 revisit && revisit.classList.add('cky-revisit-hide') 641} 642function _ckyShowRevisit() { 643 const revisit = _ckyGetRevisit(); 644 revisit && revisit.classList.remove('cky-revisit-hide') 645} 646function _ckySetPreferenceAction(tagName = false) { 647 _ckyStore._preferenceOriginTag = tagName; 648 if (_ckyGetType() === 'classic') { 649 _ckyTogglePreferenceCenter(); 650 _ckyToggleAriaExpandStatus("=settings-button"); 651 } else { 652 _ckyShowPreferenceCenter(); 653 } 654} 655function _ckyGetFocusableElements(element) { 656 const wrapperElement = document.querySelector(`[data-cky-tag="${element}"]`); 657 if (!wrapperElement) return []; 658 const focussableElements = Array.from( 659 wrapperElement.querySelectorAll( 660 'a:not([disabled]), button:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])' 661 ) 662 ).filter((element) => element.style.display !== "none"); 663 if (focussableElements.length <= 0) return []; 664 return [ 665 focussableElements[0], 666 focussableElements[focussableElements.length - 1], 667 ]; 668} 669function _ckyLoopFocus() { 670 const activeLaw = _ckyGetLaw(); 671 const bannerType = _ckyGetType(); 672 if (bannerType === "classic") return; 673 if (bannerType === "popup") { 674 const [firstElementBanner, lastElementBanner] = 675 _ckyGetFocusableElements("notice"); 676 _ckyAttachFocusLoop(firstElementBanner, lastElementBanner, true); 677 _ckyAttachFocusLoop(lastElementBanner, firstElementBanner); 678 } 679 const [firstElementPopup, lastElementPopup] = _ckyGetFocusableElements( 680 activeLaw === "ccpa" ? "optout-popup" : "detail" 681 ); 682 _ckyAttachFocusLoop(firstElementPopup, lastElementPopup, true); 683 _ckyAttachFocusLoop(lastElementPopup, firstElementPopup); 684} 685function _ckyAttachFocusLoop(element, targetElement, isReverse = false) { 686 if (!element || !targetElement) return; 687 element.addEventListener("keydown", (event) => { 688 if ( 689 event.which !== 9 || 690 (isReverse && !event.shiftKey) || 691 (!isReverse && event.shiftKey) 692 ) 693 return; 694 event.preventDefault(); 695 targetElement.focus(); 696 }); 697} 698 699/** 700 * Replace footer shadow with current preference center background. 701 * 702 * @param {object} $doc Dom node. 703 * @returns 704 */ 705function _ckySetFooterShadow($doc) { 706 const footer = $doc.querySelector('[data-cky-tag="detail"] .cky-footer-shadow'); 707 const preference = $doc.querySelector('[data-cky-tag="detail"]'); 708 if (!footer) return; 709 const background = preference && preference.style.backgroundColor || '#ffffff'; 710 footer.style.background = `linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, ${background 711 } 100%)`; 712} 713 714/** 715 * Remove all the rejected cookies. 716 * 717 * @param {object} cookies Cookies list. 718 */
719function _ckyRemoveDeadCookies({ cookies }) { 720 const currentCookieMap = ref._ckyGetCookieMap(); 721 for (const { cookieID, domain } of cookies) 722 if (currentCookieMap[cookieID]) 723 [domain, ""].map((cookieDomain) => 724 ref._ckySetCookie(cookieID, "", 0, cookieDomain) 725 ); 726} 727function _ckySetPreferenceCheckBoxStates(revisit = false) { 728 for (const category of _ckyStore._categories) { 729 const cookieValue = ref._ckyGetFromStore(category.slug); 730 const checked = 731 cookieValue === "yes" || 732 (!cookieValue && 733 category.defaultConsent[_ckyGetLaw()]) || category.isNecessary; 734 735 const disabled = category.isNecessary; 736 const shortCodeData = _ckyStore._shortCodes.find( 737 (code) => code.key === 'cky_category_toggle_label' 738 ); 739 const toggleTextFormatted = shortCodeData.content.replace( 740 `[cky_preference_{{category_slug}}_title]`, 741 category.name 742 ); 743 _ckySetCheckboxes( 744 category, 745 checked, 746 disabled, 747 toggleTextFormatted, 748 revisit 749 ); 750 _ckySetPreferenceState(category); 751 } 752} 753 754function _ckySetCheckboxes( 755 category, 756 checked, 757 disabled, 758 formattedLabel, 759 revisit = false 760) { 761 const toggle = _ckyStore._bannerConfig.config.preferenceCenter.toggle; 762 const activeColor = toggle.states.active.styles['background-color']; 763 const inactiveColor = toggle.states.inactive.styles['background-color']; 764 765 [`ckyCategoryDirect`, `ckySwitch`].map((key) => { 766 const boxElem = document.getElementById(`${key}${category.slug}`); 767 if (!boxElem) return; 768 _ckySetCategoryToggle( 769 boxElem, 770 category, 771 revisit); 772 boxElem.checked = checked; 773 boxElem.disabled = disabled; 774 boxElem.style.backgroundColor = checked ? activeColor : inactiveColor; 775 _ckySetCheckBoxAriaLabel(boxElem, checked, formattedLabel); 776 if (revisit) return; 777 boxElem.addEventListener("change", ({ currentTarget: elem }) => { 778 const isChecked = elem.checked; 779 elem.style.backgroundColor = isChecked ? activeColor : inactiveColor; 780 _ckySetCheckBoxAriaLabel(boxElem, isChecked, formattedLabel); 781 }); 782 return boxElem; 783 }); 784} 785function _ckySetCategoryToggle(element, category = {}, revisit = false) { 786 if (revisit) return; 787 if (element.parentElement.getAttribute('data-cky-tag') === 'detail-category-toggle') { 788 _ckySetCategoryPreferenceToggle(element, category); 789 } else if (element.parentElement.getAttribute('data-cky-tag') === 'detail-category-preview-toggle') { 790 _ckySetCategoryPreview(element, category); 791 } 792 if (!category.isNecessary) { 793 const categoryName = category.name; 794 const categoryTitle = document.querySelector(`[data-cky-tag="detail-category-title"][aria-label="${categoryName}"]`); 795 if (categoryTitle) { 796 const toggleContainer = categoryTitle.closest('.cky-accordion-item'); 797 const necessaryText = toggleContainer.querySelector('.cky-always-active'); 798 necessaryText && necessaryText.remove(); 799 } 800 } 801} 802function _ckySetCategoryPreferenceToggle(element, category) { 803 let toggleContainer = element.closest('.cky-accordion-item'); 804 if (!toggleContainer) return; 805 const toggleSwitch = toggleContainer.querySelector('.cky-switch'); 806 if (category.isNecessary) { 807 toggleSwitch && toggleSwitch.remove(); 808 } else { 809 if (_ckyGetType() === 'classic' && _ckyStore._bannerConfig.config.categoryPreview.status || (category.cookies && category.cookies.length === 0)) { 810 toggleSwitch && toggleSwitch.remove(); 811 } 812 } 813} 814function _ckySetPreferenceState(category) { 815 if (_ckyStore._bannerConfig.config.auditTable.status === false) { 816 const tableElement = document.querySelector( 817 `#ckyDetailCategory${category.slug} [data-cky-tag="audit-table"]` 818 ); 819 tableElement && tableElement.remove(); 820 const chevronElement = document.querySelector( 821 `#ckyDetailCategory${category.slug} .cky-accordion-chevron` 822 ); 823 chevronElement && chevronElement.classList.add("cky-accordion-chevron-hide"); 824 } 825} 826function _ckySetCategoryPreview(element, category) {
827 if ((category.cookies && category.cookies.length === 0) && !category.isNecessary) 828 element.parentElement.parentElement.remove(); 829} 830 831function _ckySetCheckBoxAriaLabel(boxElem, isChecked, formattedLabel, isCCPA = false) { 832 833 if (!boxElem) return; 834 const keyName = isChecked ? "disable" : "enable"; 835 const textCode = `cky_${keyName}_${isCCPA ? "optout" : "category"}_label`; 836 const shortCodeData = _ckyStore._shortCodes.find( 837 (code) => code.key === textCode 838 ); 839 if (!shortCodeData) return; 840 const labelText = formattedLabel 841 .replace(/{{status}}/g, keyName) 842 .replace(`[${textCode}]`, shortCodeData.content); 843 boxElem.setAttribute("aria-label", labelText); 844} 845/** 846 * Clear opt-out success countdown timers (interval + timeout). 847 * 848 * @returns {void} 849 */ 850function _ckyClearOptoutSuccessTimers() { 851 if (_ckyStore._optoutSuccessCountdownInterval) { 852 clearInterval(_ckyStore._optoutSuccessCountdownInterval); 853 _ckyStore._optoutSuccessCountdownInterval = null; 854 } 855 if (_ckyStore._optoutSuccessAutoCloseTimer) { 856 clearTimeout(_ckyStore._optoutSuccessAutoCloseTimer); 857 _ckyStore._optoutSuccessAutoCloseTimer = null; 858 } 859} 860 861function _ckyIsOptoutSuccessVisible() { 862 const el = _ckyGetElementByTag("optout-success"); 863 return !!(el && !el.classList.contains("cky-hide")); 864} 865 866function _ckyDismissOptoutSuccessCountdown() { 867 _ckyRemoveBanner(); 868 _ckyHidePreferenceCenter(); 869 _ckyAfterConsent(); 870} 871 872/** 873 * Show opt-out success UI after save, run countdown, then dismiss banner. 874 * 875 * @returns {void} 876 */ 877function _ckyShowOptoutSuccessMessage() { 878 _ckyClearOptoutSuccessTimers(); 879 880 const buttonWrapper = _ckyGetElementByTag("optout-buttons"); 881 const successMessage = _ckyGetElementByTag("optout-success"); 882 const countdownElement = _ckyGetElementByTag("optout-success-subtext"); 883 const ccpaCheckbox = document.getElementById("ckyCCPAOptOut"); 884 885 if (!buttonWrapper || !successMessage) { 886 _ckyDismissOptoutSuccessCountdown(); 887 return; 888 } 889 890 buttonWrapper.style.display = "none"; 891 successMessage.classList.remove("cky-hide"); 892 successMessage.setAttribute("aria-live", "polite"); 893 successMessage.focus(); 894 if (ccpaCheckbox) ccpaCheckbox.disabled = true; 895 _ckyClassAdd("=optout-option", "cky-disabled", false); 896 897 const countdownTimerEl = 898 (countdownElement && 899 countdownElement.querySelector("#ckyCountdownTimer")) || 900 document.getElementById("ckyCountdownTimer"); 901 902 let timeRemaining = CKY_OPTOUT_SUCCESS_SECONDS; 903 if ( 904 countdownElement && 905 !countdownTimerEl && 906 !_ckyStore._optoutSuccessSubtextTemplate 907 ) { 908 _ckyStore._optoutSuccessSubtextTemplate = 909 countdownElement.textContent || 910 `Banner closes automatically in ${CKY_OPTOUT_SUCCESS_SECONDS} s...`; 911 } 912 const template = _ckyStore._optoutSuccessSubtextTemplate; 913 const hasDigit = template && /\d+/.test(template); 914 const updateSubtext = () => { 915 if (!countdownElement) return; 916 if (countdownTimerEl) { 917 countdownTimerEl.textContent = String(timeRemaining); 918 return; 919 } 920 countdownElement.textContent = hasDigit 921 ? template.replace(/\d+/, String(timeRemaining)) 922 : `Banner closes automatically in ${timeRemaining} s...`; 923 }; 924 updateSubtext(); 925 926 _ckyStore._optoutSuccessCountdownInterval = setInterval(() => { 927 timeRemaining -= 1; 928 if (timeRemaining >= 0) updateSubtext(); 929 }, 1000); 930 931 _ckyStore._optoutSuccessAutoCloseTimer = setTimeout( 932 _ckyDismissOptoutSuccessCountdown, 933 CKY_OPTOUT_SUCCESS_DISMISS_MS 934 ); 935} 936 937/** 938 * Reset opt-out success UI (timers, visibility, checkbox, subtext). 939 * 940 * @returns {void} 941 */ 942function _ckyResetOptoutSuccessMessage() { 943 _ckyClearOptoutSuccessTimers(); 944 945 const buttonWrapper = _ckyGetElementByTag("optout-buttons"); 946 const successMessage = _ckyGetElementByTag("optout-success"); 947 const countdownElement = _ckyGetElementByTag("optout-success-subtext"); 948 const ccpaCheckbox = document.getElementById("ckyCCPAOptOut"); 949 950 if (buttonWrapper) buttonWrapper.style.display = ""; 951 if (successMessage) successMessage.classList.add("cky-hide"); 952 if (ccpaCheckbox) ccpaCheckbox.disabled = false;
953 _ckyClassRemove("=optout-option", "cky-disabled", false); 954 const resetTimerEl = 955 (countdownElement && 956 countdownElement.querySelector("#ckyCountdownTimer")) || 957 document.getElementById("ckyCountdownTimer"); 958 if (resetTimerEl) { 959 resetTimerEl.textContent = ""; 960 } else if (countdownElement && _ckyStore._optoutSuccessSubtextTemplate) { 961 countdownElement.textContent = _ckyStore._optoutSuccessSubtextTemplate; 962 } 963} 964 965/** 966 * Opt-out confirm: if CCPA + opted out, save consent then show success + countdown; else normal save/close. 967 * 968 * @returns {Function} Click handler 969 */ 970function _ckyHandleOptoutConfirm() { 971 return () => { 972 if (_ckyGetLaw() !== "ccpa" || !_ckyFindCheckBoxValue()) { 973 _ckyAcceptReject()(); 974 return; 975 } 976 _ckyAcceptCookies(); 977 _ckyShowOptoutSuccessMessage(); 978 }; 979} 980 981/** 982 * Opt-out popup close: dismiss countdown if success is visible, else normal close. 983 * 984 * @returns {void} 985 */ 986function _ckyHandleOptoutPopupClose() { 987 if (_ckyIsOptoutSuccessVisible()) { 988 ref._ckySetInStore("action", "yes"); 989 _ckyDismissOptoutSuccessCountdown(); 990 return; 991 } 992 _ckyHidePreferenceCenter(); 993} 994 995/** 996 * Render banner after processing. 997 */ 998function _ckyRenderBanner() { 999 const template = document.getElementById('ckyBannerTemplate'); 1000 const templateHtml = template.innerHTML; 1001 const doc = new DOMParser().parseFromString(templateHtml, 'text/html'); 1002 _ckySetFooterShadow(doc); 1003 document.body.insertAdjacentHTML( 1004 "afterbegin", 1005 doc.body.innerHTML 1006 ); 1007 if (_ckyGetType() === "classic") _ckyToggleAriaExpandStatus("=settings-button", "false"); 1008 _ckySetPreferenceCheckBoxStates(); 1009 _ckyAttachCategoryListeners(); 1010 _ckyRegisterListeners(); 1011 _ckySetCCPAOptions(); 1012 _ckySetPlaceHolder(); 1013 _ckyAttachReadMore(); 1014 _ckyAttachShowMoreLessStyles(); 1015 _ckyAttachAlwaysActiveStyles(); 1016 _ckyAttachManualLinksStyles(); 1017 _ckyRemoveStyles(); 1018 _ckyAddPositionClass();
1019 _ckyAddRtlClass(); 1020 _ckySetPoweredBy(); 1021 _ckyLoopFocus(); 1022 _ckyAddPreferenceCenterClass(); 1023} 1024 1025/** 1026 * Accept or reject the consent based on the option. 1027 * 1028 * @param {string} option Type of consent. 1029 * @returns {void} 1030 */ 1031function _ckyAcceptReject(option = "custom") { 1032 return () => { 1033 _ckyAcceptCookies(option); 1034 _ckyRemoveBanner(); 1035 _ckyHidePreferenceCenter(); 1036 _ckyAfterConsent(); 1037 }; 1038} 1039 1040function _ckyActionClose() { 1041 ref._ckySetInStore("action", "yes"); 1042 if (_ckyIsOptoutSuccessVisible()) { 1043 _ckyDismissOptoutSuccessCountdown(); 1044 return; 1045 } 1046 _ckyRemoveBanner(); 1047} 1048/** 1049 * Consent accept callback. 1050 * 1051 * @param {string} choice Type of consent. 1052 */ 1053function _ckyAcceptCookies(choice = "all") { 1054 const activeLaw = _ckyGetLaw(); 1055 const ccpaCheckBoxValue = _ckyFindCheckBoxValue(); 1056 1057 ref._ckySetInStore("action", "yes"); 1058 if (activeLaw === 'gdpr') { 1059 ref._ckySetInStore("consent", choice === "reject" ? "no" : "yes"); 1060 } else { 1061 ref._ckySetInStore("consent", ccpaCheckBoxValue ? "yes" : "no"); 1062 } 1063 const responseCategories = { accepted: [], rejected: [] }; 1064 for (const category of _ckyStore._categories) { 1065 let valueToSet = "no"; 1066 if (activeLaw === 'gdpr') { 1067 valueToSet = 1068 !category.isNecessary && 1069 (choice === "reject" || 1070 (choice === "custom" && !_ckyFindCheckBoxValue(category.slug))) 1071 ? "no" 1072 : "yes"; 1073 } else { 1074 valueToSet = ccpaCheckBoxValue && !category.defaultConsent.ccpa ? "no" : "yes"; 1075 } 1076 ref._ckySetInStore(`${category.slug}`, valueToSet); 1077 if (valueToSet === "no") { 1078 responseCategories.rejected.push(category.slug); 1079 _ckyRemoveDeadCookies(category); 1080 } else responseCategories.accepted.push(category.slug); 1081 } 1082 _ckyUnblock(); 1083 _ckyFireEvent(responseCategories); 1084} 1085function _ckySetShowMoreLess() { 1086 const activeLaw = _ckyGetLaw(); 1087 const showCode = _ckyStore._shortCodes.find( 1088 (code) => code.key === "cky_show_desc" 1089 ); 1090 const hideCode = _ckyStore._shortCodes.find( 1091 (code) => code.key === "cky_hide_desc" 1092 ); 1093 1094 if (!showCode || !hideCode) return; 1095 const hideButtonContent = hideCode.content; 1096 const showButtonContent = showCode.content; 1097 1098 const contentLimit = window.innerWidth < 376 ? 150 : 300; 1099 const element = document.querySelector( 1100 `[data-cky-tag="${activeLaw === "gdpr" ? "detail" : "optout"}-description"]` 1101 ); 1102 if (!element) return; 1103 const content = element.textContent; 1104 if (content.length < contentLimit) return; 1105 const contentHTML = element.innerHTML; 1106 const htmlDoc = new DOMParser().parseFromString(contentHTML, "text/html"); 1107 const innerElements = htmlDoc.querySelectorAll("body > p"); 1108 if (innerElements.length <= 1) return; 1109 let strippedContent = ``; 1110 for (let index = 0; index < innerElements.length; index++) { 1111 if (index === innerElements.length - 1) continue; 1112 const el = innerElements[index]; 1113 if (`${strippedContent}${el.outerHTML}`.length > contentLimit) 1114 el.insertAdjacentHTML("beforeend", `... ${showButtonContent}`); 1115 strippedContent = `${strippedContent}${el.outerHTML}`; 1116 if (strippedContent.length > contentLimit) break; 1117 } 1118 1119 function showMoreHandler() { 1120 element.innerHTML = `${contentHTML}${hideButtonContent}`; 1121 _ckyAttachListener("=hide-desc-button", showLessHandler); 1122 _ckyAttachShowMoreLessStyles(); 1123 } 1124 function showLessHandler() { 1125 element.innerHTML = strippedContent; 1126 _ckyAttachListener("=show-desc-button", showMoreHandler); 1127 _ckyAttachShowMoreLessStyles(); 1128 } 1129 showLessHandler(); 1130} 1131/** 1132 * Toggle show more or less on click event. 1133 * 1134 * @param {object} object Object containing toggle buttons and texts. 1135 * @param {*} element Target element. 1136 */ 1137function _ckyToggleMoreLess(object, element) { 1138 let { 1139 currentTarget, 1140 target 1141 } = element; 1142 if (target && target.tagName.toUpperCase() !== 'BUTTON') return; 1143 const ariaExpanded = currentTarget.getAttribute('aria-expanded'); 1144 const trimmed = ariaExpanded === 'false'; 1145 let btn = object.btnTrim; 1146 let text = object.originalText; 1147 if (!trimmed) { 1148 btn = object.btnExpand; 1149 text = object.truncatedText; 1150 } 1151 currentTarget.innerHTML = `${text}${btn}`; 1152 currentTarget.ariaExpanded = trimmed; 1153} 1154 1155/** 1156 * Add styles to the shortcode HTML rendered outside of the banner. 1157 * 1158 * @returns {void} 1159 */
1160function _ckyAttachShortCodeStyles() { 1161 const shortCodes = _ckyStore._tags; 1162 Array.prototype.forEach.call(shortCodes, function (shortcode) { 1163 document.querySelectorAll('[data-cky-tag=' + shortcode.tag + ']').forEach(function (item) { 1164 let styles = ''; 1165 for (const key in shortcode.styles) { 1166 styles += `${key}: ${shortcode.styles[key]};`; 1167 } 1168 item.style.cssText = styles; 1169 }); 1170 }); 1171} 1172 1173/** Script blocker Version 2 */ 1174 1175const _ckyCreateElementBackup = document.createElement; 1176document.createElement = (...args) => { 1177 const createdElement = _ckyCreateElementBackup.call(document, ...args); 1178 if (createdElement.nodeName.toLowerCase() !== "script") return createdElement; 1179 const originalSetAttribute = createdElement.setAttribute.bind(createdElement); 1180 Object.defineProperties(createdElement, { 1181 src: { 1182 get: function () { 1183 return createdElement.getAttribute("src"); 1184 }, 1185 set: function (value) { 1186 if (_ckyShouldChangeType(createdElement, value)) 1187 originalSetAttribute("type", "javascript/blocked"); 1188 originalSetAttribute("src", value); 1189 return true; 1190 }, 1191 }, 1192 type: { 1193 get: function () { 1194 return createdElement.getAttribute("type"); 1195 }, 1196 set: function (value) { 1197 value = _ckyShouldChangeType(createdElement) 1198 ? "javascript/blocked" 1199 : value; 1200 originalSetAttribute("type", value); 1201 return true; 1202 }, 1203 }, 1204 }); 1205 createdElement.setAttribute = (name, value) => { 1206 if (name === "type" || name === "src") 1207 return (createdElement[name] = value); 1208 originalSetAttribute(name, value); 1209 if (name === "data-cookieyes" && !_ckyShouldChangeType(createdElement)) 1210 originalSetAttribute("type", "text/javascript"); 1211 }; 1212 return createdElement; 1213}; 1214 1215function _ckyMutationObserver(mutations) { 1216 for (const { addedNodes } of mutations) { 1217 for (const node of addedNodes) { 1218 if ( 1219 !node.src || 1220 !node.nodeName || 1221 !["script", "iframe"].includes(node.nodeName.toLowerCase()) 1222 ) 1223 continue; 1224 try { 1225 const urlToParse = node.src.startsWith("//") 1226 ? `${window.location.protocol}${node.src}` 1227 : node.src; 1228 const { hostname, pathname } = new URL(urlToParse); 1229 const cleanedHostname = _ckyCleanHostName(`${hostname}${pathname}`); 1230 _ckyAddProviderToList(node, cleanedHostname); 1231 if (!_ckyShouldBlockProvider(cleanedHostname)) continue; 1232 const uniqueID = ref._ckyRandomString(8, false); 1233 if (node.nodeName.toLowerCase() === "iframe") 1234 _ckyAddPlaceholder(node, uniqueID); 1235 else { 1236 node.type = "javascript/blocked"; 1237 const scriptEventListener = function (event) { 1238 event.preventDefault(); 1239 node.removeEventListener( 1240 "beforescriptexecute", 1241 scriptEventListener 1242 ); 1243 }; 1244 node.addEventListener("beforescriptexecute", scriptEventListener); 1245 } 1246 const position = 1247 document.head.compareDocumentPosition(node) & 1248 Node.DOCUMENT_POSITION_CONTAINED_BY 1249 ? "head" 1250 : "body"; 1251 node.remove(); 1252 _ckyStore._backupNodes.push({ 1253 position: position, 1254 node: node.cloneNode(), 1255 uniqueID, 1256 }); 1257 } catch (error) { 1258 } 1259 } 1260 } 1261} 1262 1263function _ckyUnblock() { 1264 if (navigator.doNotTrack === 1) return; 1265 const ckyconsent = ref._ckyGetFromStore("consent"); 1266 if ( 1267 _ckyGetLaw() === "gdpr" && 1268 (!ckyconsent || ckyconsent !== "yes") 1269 ) 1270 return; 1271 _ckyStore._backupNodes = _ckyStore._backupNodes.filter( 1272 ({ position, node, uniqueID }) => { 1273 try { 1274 if (_ckyShouldBlockProvider(node.src)) return true; 1275 if (node.nodeName.toLowerCase() === "script") { 1276 const scriptNode = document.createElement("script"); 1277 scriptNode.src = node.src; 1278 scriptNode.type = "text/javascript"; 1279 document[position].appendChild(scriptNode); 1280 } else { 1281 const frame = document.getElementById(uniqueID); 1282 if (!frame) return false;
1283 const iframe = document.createElement("iframe"); 1284 iframe.src = node.src; 1285 iframe.width = frame.offsetWidth; 1286 iframe.height = frame.offsetHeight; 1287 frame.parentNode.insertBefore(iframe, frame); 1288 frame.parentNode.removeChild(frame); 1289 } 1290 return false; 1291 } catch (error) { 1292 console.error(error); 1293 return false; 1294 } 1295 } 1296 ); 1297} 1298 1299function _ckyClearListeners() { 1300 _nodeListObserver.disconnect(); 1301 document.createElement = _ckyCreateElementBackup; 1302} 1303function _ckyAddProviderToList(node, cleanedHostname) { 1304 const nodeCategory = 1305 node.hasAttribute("data-cookieyes") && node.getAttribute("data-cookieyes"); 1306 if (!nodeCategory) return; 1307 const categoryName = nodeCategory.replace("cookieyes-", ""); 1308 for (const category of _ckyStore._categories) 1309 if (category.isNecessary && category.slug === categoryName) return; 1310 const provider = _ckyStore._providersToBlock.find( 1311 ({ re }) => re === cleanedHostname 1312 ); 1313 if (!provider) 1314 _ckyStore._providersToBlock.push({ 1315 re: cleanedHostname, 1316 categories: [categoryName], 1317 fullPath: false, 1318 }); 1319 else if (!provider.isOverridden) { 1320 provider.categories = [categoryName]; 1321 provider.isOverridden = true; 1322 } else if (!provider.categories.includes(categoryName)) 1323 provider.categories.push(categoryName); 1324} 1325
1326const _nodeListObserver = new MutationObserver(_ckyMutationObserver); 1327_nodeListObserver.observe(document.documentElement, { 1328 childList: true, 1329 subtree: true, 1330}); 1331function _ckyCleanHostName(name) { 1332 return name.replace(/^www./, ""); 1333} 1334 1335function _ckyIsCategoryToBeBlocked(category) { 1336 const cookieValue = ref._ckyGetFromStore(category); 1337 return ( 1338 cookieValue === "no" || 1339 (!cookieValue && 1340 _ckyStore._categories.some( 1341 (cat) => cat.slug === category && !cat.isNecessary 1342 )) 1343 ); 1344} 1345 1346function _ckyShouldBlockProvider(formattedRE) { 1347 const provider = _ckyStore._providersToBlock.find(({ re }) => 1348 new RegExp(_ckyEscapeRegex(re)).test(formattedRE) 1349 ); 1350 return ( 1351 provider && 1352 provider.categories.some((category) => _ckyIsCategoryToBeBlocked(category)) 1353 ); 1354} 1355function _ckyShouldChangeType(element, src) { 1356 return ( 1357 (element.hasAttribute("data-cookieyes") && 1358 _ckyIsCategoryToBeBlocked( 1359 element.getAttribute("data-cookieyes").replace("cookieyes-", "") 1360 )) || 1361 _ckyShouldBlockProvider(src ? src : element.src) 1362 ); 1363} 1364 1365/** 1366 * Add readmore button to consent notice. 1367 * 1368 * @returns void 1369 */ 1370function _ckyAttachReadMore() { 1371 const readMoreButton = _ckyStore._shortCodes.find( 1372 (code) => code.key === "cky_readmore" 1373 ); 1374 if (!readMoreButton.status) return; 1375 const content = readMoreButton.content; 1376 const styles = _ckyStore._bannerConfig.config.readMore.styles; 1377 const readMoreElement = document.querySelector( 1378 '[data-cky-tag="description"]' 1379 ); 1380 if (!readMoreElement) return; 1381 if (readMoreElement.childNodes.length > 1) { 1382 const innerElement = document.querySelector( 1383 '[data-cky-tag="description"] p:last-child' 1384 ); 1385 innerElement && innerElement.insertAdjacentHTML( 1386 "beforeend", 1387 ` ${content}` 1388 ); 1389 } else { 1390 readMoreElement.insertAdjacentHTML( 1391 "beforeend", 1392 ` ${content}` 1393 ); 1394 } 1395 const placeHolders = document.querySelectorAll( 1396 `[data-cky-tag="readmore-button"]` 1397 ); 1398 if (placeHolders.length < 1) return; 1399 Array.from(placeHolders).forEach((placeHolder) => { 1400 for (const style in styles) { 1401 if (!styles[style]) continue; 1402 placeHolder.style[style] = styles[style]; 1403 } 1404 }); 1405} 1406 1407/** 1408 * Apply styles to show more/show less buttons. 1409 * 1410 * @returns void 1411 */ 1412function _ckyAttachShowMoreLessStyles() { 1413 if (!_ckyStore._bannerConfig.config.showMore || !_ckyStore._bannerConfig.config.showLess) return; 1414 1415 const showMoreStyles = _ckyStore._bannerConfig.config.showMore.styles; 1416 const showLessStyles = _ckyStore._bannerConfig.config.showLess.styles; 1417 1418 if (showMoreStyles) { 1419 const showMoreButtons = document.querySelectorAll('[data-cky-tag="show-desc-button"]'); 1420 if (showMoreButtons.length > 0) { 1421 Array.from(showMoreButtons).forEach((button) => { 1422 for (const style in showMoreStyles) { 1423 if (!showMoreStyles[style]) continue; 1424 button.style[style] = showMoreStyles[style]; 1425 } 1426 }); 1427 } 1428 } 1429 1430 if (showLessStyles) { 1431 const showLessButtons = document.querySelectorAll('[data-cky-tag="hide-desc-button"]'); 1432 if (showLessButtons.length > 0) { 1433 Array.from(showLessButtons).forEach((button) => { 1434 for (const style in showLessStyles) { 1435 if (!showLessStyles[style]) continue; 1436 button.style[style] = showLessStyles[style]; 1437 } 1438 }); 1439 } 1440 } 1441} 1442 1443/** 1444 * Apply styles to Always Active text. 1445 * 1446 * @returns void 1447 */ 1448function _ckyAttachAlwaysActiveStyles() { 1449 if (!_ckyStore._bannerConfig.config.alwaysActive) return; 1450 1451 const alwaysActiveStyles = _ckyStore._bannerConfig.config.alwaysActive.styles; 1452 if (!alwaysActiveStyles) return; 1453 1454 const alwaysActiveElements = document.querySelectorAll('.cky-always-active'); 1455 if (alwaysActiveElements.length < 1) return;
1456 Array.from(alwaysActiveElements).forEach((element) => { 1457 for (const style in alwaysActiveStyles) { 1458 if (!alwaysActiveStyles[style]) continue; 1459 element.style[style] = alwaysActiveStyles[style]; 1460 } 1461 }); 1462} 1463 1464/** 1465 * Apply styles to manually added links. 1466 * 1467 * @returns void 1468 */ 1469function _ckyAttachManualLinksStyles() { 1470 if (!_ckyStore._bannerConfig.config.manualLinks) return; 1471 1472 const manualLinksStyles = _ckyStore._bannerConfig.config.manualLinks.styles; 1473 if (!manualLinksStyles) return; 1474 1475 const manualLinks = document.querySelectorAll('.cky-link, a.cky-link, [data-cky-tag="detail"] a, [data-cky-tag="optout-popup"] a, [data-cky-tag="notice"] a'); 1476 if (manualLinks.length < 1) return; 1477 Array.from(manualLinks).forEach((link) => { 1478 if (link.getAttribute('data-cky-tag') === 'readmore-button') return; 1479 for (const style in manualLinksStyles) { 1480 if (!manualLinksStyles[style]) continue; 1481 link.style[style] = manualLinksStyles[style]; 1482 } 1483 if (manualLinksStyles.color) { 1484 link.style.textDecorationColor = manualLinksStyles.color; 1485 } 1486 }); 1487} 1488 1489function _ckyAfterConsent() { 1490 if (_ckyGetLaw() === 'gdpr') _ckySetPreferenceCheckBoxStates(true); 1491 1492 if (_ckyStore._bannerConfig.behaviours.reloadBannerOnAccept === true) { 1493 window.location.reload(); 1494 } 1495} 1496 1497function _ckyAttachNoticeStyles() { 1498 if (document.getElementById("cky-style") || !_ckyStyle) return; 1499 document.head.insertAdjacentHTML( 1500 "beforeend", 1501 ` <style id="cky-style">${_ckyStyle.css}</style>` 1502 ); 1503} 1504 1505function _ckyFindCheckBoxValue(id = "") { 1506 const elementsToCheck = id 1507 ? [`ckySwitch`, `ckyCategoryDirect`] 1508 : [`ckyCCPAOptOut`]; 1509 return elementsToCheck.some((key) => { 1510 const checkBox = document.getElementById(`${key}${id}`); 1511 return checkBox && checkBox.checked; 1512 }); 1513} 1514 1515function _ckyAddPlaceholder(htmlElm, uniqueID) { 1516 const shortCodeData = _ckyStore._shortCodes.find( 1517 (code) => code.key === 'cky_video_placeholder' 1518 ); 1519 const videoPlaceHolderDataCode = shortCodeData.content; 1520 const { offsetWidth, offsetHeight } = htmlElm; 1521 if (offsetWidth === 0 || offsetHeight === 0) return; 1522 htmlElm.insertAdjacentHTML( 1523 "beforebegin", 1524 `${videoPlaceHolderDataCode}`.replace("[UNIQUEID]", uniqueID) 1525 ); 1526 const addedNode = document.getElementById(uniqueID); 1527 addedNode.style.width = `${offsetWidth}px`; 1528 addedNode.style.height = `${offsetHeight}px`; 1529 const innerTextElement = document.querySelector( 1530 `#${uniqueID} .video-placeholder-text-normal` 1531 ); 1532 innerTextElement.style.display = "none"; 1533 const youtubeID = _ckyGetYoutubeID(htmlElm.src); 1534 if (!youtubeID) return; 1535 addedNode.classList.replace( 1536 "video-placeholder-normal", 1537 "video-placeholder-youtube" 1538 ); 1539 addedNode.style.backgroundImage = `linear-gradient(rgba(76,72,72,.7),rgba(76,72,72,.7)),url('https://img.youtube.com/vi/${youtubeID}/maxresdefault.jpg')`; 1540 innerTextElement.classList.replace( 1541 "video-placeholder-text-normal", 1542 "video-placeholder-text-youtube" 1543 ); 1544} 1545function _ckyGetYoutubeID(src) { 1546 const match = src.match( 1547 /^.*(youtu\.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/ 1548 ); 1549 if (match && Array.isArray(match) && match[2] && match[2].length === 11) 1550 return match[2]; 1551 return false; 1552} 1553 1554function _ckySetPlaceHolder() { 1555 const status = _ckyStore._bannerConfig.config.videoPlaceholder.status; 1556 const styles = _ckyStore._bannerConfig.config.videoPlaceholder.styles; 1557 if (!status) return; 1558 if (!status) return; 1559 const placeHolders = document.querySelectorAll( 1560 `[data-cky-tag="placeholder-title"]` 1561 ); 1562 if (placeHolders.length < 1) return; 1563 Array.from(placeHolders).forEach((placeHolder) => { 1564 placeHolder.style.display = "block"; 1565 placeHolder.addEventListener("click", () => { 1566 if (ref._ckyGetFromStore("action")) _revisitCkyConsent(); 1567 }); 1568 for (const style in styles) { 1569 if (!styles[style]) continue; 1570 placeHolder.style[style] = styles[style]; 1571 } 1572 }); 1573}
1574function _ckyAddRtlClass() { 1575 if (!_ckyStore._rtl) return; 1576 const rtlElements = ['notice', 'detail', 'optout-popup', 'revisit-consent', 'video-placeholder'] 1577 rtlElements.forEach(function (item) { 1578 _ckyGetElementByTag(item) && _ckyGetElementByTag(item).classList.add('cky-rtl'); 1579 }); 1580} 1581 1582function _ckySetFocus(tagName) { 1583 const element = _ckyGetElementByTag(tagName); 1584 if (!element) return; 1585 element.focus(); 1586} 1587 1588function _ckySetPoweredBy() { 1589 let position = 'flex-end'; 1590 ['detail-powered-by', 'optout-powered-by'].map((key) => { 1591 const element = document.querySelector( 1592 `[data-cky-tag="${key}"]` 1593 ); 1594 if (!element) return; 1595 element.style.display = "flex"; 1596 element.style.justifyContent = position; 1597 element.style.alignItems = "center"; 1598 return element; 1599 }); 1600 1601} 1602function _ckyWatchBannerElement() { 1603 document.querySelector("body").addEventListener("click", (event) => { 1604 const selector = ".cky-banner-element, .cky-banner-element *"; 1605 if ( 1606 event.target.matches 1607 ? event.target.matches(selector) 1608 : event.target.msMatchesSelector(selector) 1609 ) 1610 _revisitCkyConsent(); 1611 }); 1612} 1613 1614function _ckyRemoveAllDeadCookies() { 1615 for (const category of _ckyStore._categories) { 1616 if (ref._ckyGetFromStore(category.slug) !== "yes") 1617 _ckyRemoveDeadCookies(category); 1618 } 1619} 1620 1621function _ckySetCCPAOptions() { 1622 const toggle = _ckyStore._bannerConfig.config.optOption.toggle; 1623 const activeColor = toggle.states.active.styles['background-color']; 1624 const inactiveColor = toggle.states.inactive.styles['background-color']; 1625 _ckyClassRemove("=optout-option", "cky-disabled", false); 1626 const toggleDataCode = _ckyStore._shortCodes.find( 1627 (code) => code.key === "cky_optout_toggle_label" 1628 ); 1629 const optOutTitle = _ckyStore._shortCodes.find( 1630 (code) => code.key === "cky_optout_option_title" 1631 ); 1632 const formattedLabel = toggleDataCode.content.replace( 1633 `[cky_optout_option_title]`, 1634 optOutTitle.content 1635 ); 1636 const checked = ref._ckyGetFromStore("consent") === "yes"; 1637 _ckySetCheckBoxInfo( 1638 document.getElementById(`ckyCCPAOptOut`), 1639 formattedLabel, 1640 { 1641 checked, 1642 disabled: false, 1643 addListeners: true, 1644 }, 1645 { activeColor, inactiveColor }, 1646 true 1647 ); 1648} 1649function _ckySetCheckBoxInfo( 1650 boxElem, 1651 formattedLabel, 1652 { checked, disabled, addListeners }, 1653 { activeColor, inactiveColor }, 1654 isCCPA = false 1655) { 1656 if (!boxElem) return; 1657 if (isCCPA && addListeners) 1658 _ckyAttachListener("=optout-option-title", () => boxElem.click()); 1659 boxElem.checked = checked; 1660 boxElem.disabled = disabled; 1661 boxElem.style.backgroundColor = checked ? activeColor : inactiveColor; 1662 _ckySetCheckBoxAriaLabel(boxElem, checked, formattedLabel, isCCPA); 1663 if (!addListeners) return; 1664 boxElem.addEventListener("change", ({ currentTarget: elem }) => { 1665 const isChecked = elem.checked; 1666 elem.style.backgroundColor = isChecked ? activeColor : inactiveColor; 1667 _ckySetCheckBoxAriaLabel(boxElem, isChecked, formattedLabel, isCCPA); 1668 }); 1669} 1670 1671window.revisitCkyConsent = () => _revisitCkyConsent(); 1672 1673window.getCkyConsent = function () { 1674 const cookieConsent = { 1675 activeLaw: "", 1676 categories: {}, 1677 isUserActionCompleted: false, 1678 consentID: "", 1679 languageCode: "" 1680 }; 1681 1682 try { 1683 cookieConsent.activeLaw = _ckyGetLaw(); 1684 1685 _ckyStore._categories.forEach(category => { 1686 cookieConsent.categories[category.slug] = ref._ckyGetFromStore(category.slug) === "yes"; 1687 }); 1688 1689 cookieConsent.isUserActionCompleted = ref._ckyGetFromStore("action") === "yes"; 1690 cookieConsent.consentID = ref._ckyGetFromStore("consentid") || ""; 1691 cookieConsent.languageCode = _ckyStore._language || ""; 1692 } catch (e) { } 1693 1694 return cookieConsent; 1695};
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.