1window.adFreeSubscription || (function () { 2 3 window.adFreeSubscription = { 4 5 bannerInitialized: false, 6 7 sendLog: function (message) { 8 if (typeof(adFreeSubscriptionConfig.pluginUrl) === 'undefined') { 9 console.warn('Missing plugin url, cannot send error.'); 10 return; 11 } 12 13 // Define the target URL for the AJAX request 14 const url = adFreeSubscriptionConfig.pluginUrl + 'api/api.php'; 15 // Prepare the JSON payload 16 const data = { 17 message: message, 18 nonce: (typeof adFreeSubscriptionConfig.nonce !== 'undefined' ? adFreeSubscriptionConfig.nonce : null) 19 }; 20 21 // Send the POST request using Fetch API 22 fetch(url, { 23 method: 'POST', 24 headers: { 25 'Content-Type': 'application/json' 26 }, 27 body: JSON.stringify(data) // Convert the error data to JSON 28 }) 29 .then(response => { 30 if (!response.ok) { 31 console.warn(`Server responded with status ${response.status}`); 32 } 33 return response.json(); // Parse the JSON response 34 }) 35 .then(data => { 36 console.log('Response from server:', data); 37 }) 38 .catch(error => { 39 console.error('Error while posting error to server:', error); 40 }); 41 }, 42 43 sendInfo: function (message) { 44 if (!adFreeSubscriptionConfig.extendedLogging) { 45 return; 46 } 47 adFreeSubscription.sendLog(message); 48 }, 49 50 sendError: function (e) { 51 adFreeSubscription.sendLog(e.toString() + (typeof (e.stack) !== "undefined" ? "\n" + e.stack.toString() : "")); 52 }, 53 54 checkElement: async selector => { 55 while (document.querySelector(selector) === null) { 56 await new Promise(resolve => requestAnimationFrame(resolve)); 57 } 58 return document.querySelector(selector); 59 }, 60 61 onCookiebotReady: function (e) { 62 window.removeEventListener('CookiebotOnDialogInit', adFreeSubscription.onCookiebotReady); 63 console.log('Cookiebot is ready.'); 64 if (typeof (CookieConsent) !== 'undefined' && CookieConsent.regulationRegions && CookieConsent.regulationRegions.gdpr) { 65 // Add Switzerland to the list of regulation regions to ensure full ad delivery 66 if (!CookieConsent.regulationRegions.gdpr.includes('ch')) { 67 CookieConsent.regulationRegions.gdpr.push('ch'); 68 } 69 } 70 adFreeSubscription.showBanner(); 71 }, 72 73 showBanner: function () { 74 if (adFreeSubscription.isAcceptAll()) { 75 return; 76 } 77 console.log('Showing Ad-Free-Subscription banner ...'); 78 adFreeSubscription.sendInfo('Showing Ad-Free-Subscription banner ...'); 79 80 const banner = document.querySelector('.ad-free-sub-banner-container'); 81 if (!banner) { 82 adFreeSubscription.checkElement('.ad-free-sub-banner-container').then((banner) => { 83 banner.style.display = ''; 84 adFreeSubscription.initBanner(); 85 adFreeSubscription.checkAcceptAllButton(); 86 }); 87 } else { 88 banner.style.display = ''; 89 adFreeSubscription.initBanner(); 90 adFreeSubscription.checkAcceptAllButton(); 91 } 92 }, 93 94 checkAcceptAllButton: function () { 95 const buttonId = 'ad-free-sub-banner-btn-accept-all'; 96 const button = document.getElementById(buttonId); 97 98 if (!button) { 99 adFreeSubscription.sendInfo(`Button with ID '${buttonId}' not found`); 100 return false; 101 } 102 103 // Check if button is visible 104 const isVisible = button.offsetWidth > 0 && 105 button.offsetHeight > 0 && 106 getComputedStyle(button).display !== 'none' && 107 getComputedStyle(button).visibility !== 'hidden'; 108 109 // Check if button is clickable (not disabled) 110 const isClickable = !button.disabled && 111 !button.hasAttribute('disabled') && 112 getComputedStyle(button).pointerEvents !== 'none'; 113 114 const isReady = isVisible && isClickable; 115 116 if (isReady) { 117 adFreeSubscription.sendInfo(`Button (${buttonId}) is visible and clickable.`); 118 console.log(`Button (${buttonId}) is visible and clickable.`); 119 } else { 120 adFreeSubscription.sendInfo(`Button (${buttonId}) is not ready.`); 121 console.warn(`Button (${buttonId}) is not ready.`); 122 } 123 124 return isReady; 125 }, 126 127 getClientId: function () { 128 let clientId = localStorage.getItem("ad-free-subscription-client-id"); 129 if (!clientId) { 130 clientId = crypto.randomUUID(); 131 localStorage.setItem("ad-free-subscription-client-id", clientId); 132 } 133 return clientId; 134 }, 135 136 initBanner: function () { 137 if (adFreeSubscription.bannerInitialized) { 138 return; 139 } 140 adFreeSubscription.bannerInitialized = true; 141 142 const acceptAllButton = document.querySelector('#ad-free-sub-banner-btn-accept-all'); 143 if (acceptAllButton) { 144 acceptAllButton.addEventListener('click', adFreeSubscription.acceptAll); 145 } 146 147 const revokeLink = document.querySelectorAll('.ad-free-subscription-revoke'); 148 if (revokeLink) { 149 revokeLink.forEach(elem => { 150 elem.addEventListener('click', adFreeSubscription.revoke); 151 }); 152 } 153 154 const showPartnerLink = document.querySelectorAll('.ad-free-sub-show-partner-link'); 155 if (showPartnerLink) { 156 showPartnerLink.forEach(elem => { 157 elem.addEventListener('click', adFreeSubscription.showPartner); 158 }) 159 } 160 161 adFreeSubscription.initTcf(); 162 const accordion = document.getElementsByClassName("ad-free-sub-accordion-item-button"); 163 for (var i = 0; i < accordion.length; i++) { 164 accordion[i].addEventListener("click", adFreeSubscription.toggleAccordtionItem); 165 } 166 }, 167 168 toggleAccordtionItem: function () { 169 this.classList.toggle("active"); 170 var panel = this.nextElementSibling; 171 if (panel.style.display === "block") { 172 panel.style.display = "none"; 173 } else { 174 panel.style.display = "block"; 175 } 176 }, 177 178 hideBanner: function () { 179 const banner = document.querySelector('.ad-free-sub-banner-container'); 180 if (banner) { 181 banner.style.display = 'none'; 182 } 183 }, 184 185 acceptAll: function (e) { 186 try { 187 if (typeof(CookieConsentIABCMP) === 'undefined') { 188 const error = new Error("CookieConsentIABCMP is not defined. User will see banner again on next page load."); 189 throw error; 190 } 191 CookieConsentIABCMP.initFramework(); 192 CookieConsentIABCMP.updateConsentFullOptIn(); 193 Cookiebot.submitCustomConsent(true, true, true); 194 adFreeSubscription.sendInfo('Full consent given.'); 195 } catch (e) { 196 console.warn("Error when giving full consent.", e); 197 adFreeSubscription.sendError(e); 198 } finally { 199 adFreeSubscription.hideBanner(); 200 } 201 }, 202 203 isAcceptAll: function () { 204 if (!Cookiebot?.consent) { 205 return false;
206 } 207 return Cookiebot.consent.necessary && 208 Cookiebot.consent.preferences && 209 Cookiebot.consent.statistics && 210 Cookiebot.consent.marketing; 211 }, 212 213 revoke: function (e) { 214 e?.preventDefault(); 215 window.addEventListener('CookiebotOnDecline', function (e) { 216 window.location.reload(); 217 }); 218 Cookiebot.withdraw(); 219 }, 220 221 showPartner: function (e) { 222 e?.preventDefault(); 223 const style = document.querySelector("#ad-free-sub-banner-style"); 224 const sheet = style.sheet ? style.sheet : style.styleSheet; 225 if (sheet?.cssRules) { 226 for (var i = 0; i < sheet.cssRules.length; i++) { 227 if (sheet.cssRules[i].selectorText === '#CybotCookiebotDialog') { 228 sheet.deleteRule(i); 229 break; 230 } 231 } 232 } 233 window.removeEventListener('CookiebotOnDialogInit', adFreeSubscription.onCookiebotReady); 234 Cookiebot.show(); 235 236 const overlay = document.querySelector('#ad-free-sub-banner-container'); 237 if (overlay) { 238 overlay.style.display = 'none'; 239 } 240 }, 241 242 setCookie: function (name, value, days) { 243 var expires = ""; 244 if (days) { 245 var date = new Date(); 246 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 247 expires = "; expires=" + date.toUTCString(); 248 } 249 document.cookie = name + "=" + (value || "") + expires + "; path=/"; 250 }, 251 252 getCookie: function (name) { 253 var dc = document.cookie; 254 var prefix = name + "="; 255 var begin = dc.indexOf("; " + prefix); 256 if (begin == -1) { 257 begin = dc.indexOf(prefix); 258 if (begin != 0) return null; 259 } 260 else { 261 begin += 2; 262 var end = document.cookie.indexOf(";", begin); 263 if (end == -1) { 264 end = dc.length; 265 } 266 } 267 return decodeURI(dc.substring(begin + prefix.length, end)); 268 }, 269 270 initTcf: function () { 271 const configElem = document.querySelector('#CookiebotConfiguration'); 272 if (!configElem) { 273 console.warn("No Cookiebot config found!"); 274 return; 275 } 276 277 const config = JSON.parse(configElem.textContent); 278 const tcf = config?.Frameworks.IABTCF2; 279 if (!tcf) { 280 console.warn("No IAB TCF2 config found!"); 281 return; 282 } 283 const vendors = tcf.AllowedVendors; 284 285 let purposes = tcf.AllowedPurposes; 286 const specialPurposes = tcf.AllowedSpecialPurposes; 287 const features = tcf.AllowedFeatures; 288 let specialFeatures = tcf.AllowedSpecialFeatures; 289 290 const stackedFeatures = adFreeSubscription.resolveStackedFeatures(adFreeSubscriptionTcfPurposes, tcf.AllowedPurposes, tcf.AllowedSpecialFeatures); 291 purposes = purposes.filter(e => !stackedFeatures.some(sf => sf.purposes.includes(e))); 292 specialFeatures = specialFeatures.filter(e => !stackedFeatures.some(sf => sf.specialFeatures.includes(e))); 293 294 adFreeSubscription.displayPurposes(adFreeSubscriptionTcfPurposes, stackedFeatures.map(sf => sf.id), purposes, specialPurposes, features, specialFeatures); 295 }, 296 297 resolveStackedFeatures: function (tcfPurposes, actualPurposes, actualSpecialFeatures) { 298 const stackedFeatures = []; 299 for (var i in tcfPurposes.stacks) { 300 const stackedFeature = tcfPurposes.stacks[i]; 301 const purposes = stackedFeature.purposes; 302 const specialFeatures = stackedFeature.specialFeatures; 303 304 const isStackedFeature = 305 (purposes.length === 0 || purposes.every(e => actualPurposes.includes(e))) && 306 (specialFeatures.length === 0 || specialFeatures.every(e => actualSpecialFeatures.includes(e))); 307 if (isStackedFeature) { 308 stackedFeatures.push(stackedFeature); 309 } 310 } 311 312 // Alle Stacked features rausfiltern, die schon in anderen enthalten sind 313 let filtered = []; 314 for (var i in stackedFeatures) { 315 const stackedFeature = stackedFeatures[i]; 316 if (stackedFeature.purposes.length > 0 && 317 stackedFeatures.some( 318 sf => sf.id != stackedFeature.id && 319 stackedFeature.purposes.every(p => sf.purposes.includes(p)))) { 320 continue; 321 } 322 if (stackedFeature.specialFeatures.length > 0 && 323 stackedFeatures.some( 324 sf => sf.id != stackedFeature.id && 325 stackedFeature.specialFeatures.every(p => sf.specialFeatures.includes(p)))) { 326 continue; 327 } 328 filtered.push(stackedFeature); 329 } 330 331 // Sicherstellen, dass Zwecke/Features nur in einem Stack vorkommen 332 let filteredByDuplicate = []; 333 for (var i in actualPurposes) { 334 const actualPurpose = actualPurposes[i]; 335 const stackedFeature = filtered.find(sf => sf.purposes.includes(actualPurpose)); 336 if (stackedFeature && !filteredByDuplicate.find(sf =>
336 sf.id === stackedFeature.id)) { 337 filteredByDuplicate.push(stackedFeature); 338 } 339 } 340 for (var i in actualSpecialFeatures) { 341 const actualSpecialFeature = actualSpecialFeatures[i]; 342 const stackedFeature = filtered.find(sf => sf.specialFeatures.includes(actualSpecialFeature)); 343 if (stackedFeature && !filteredByDuplicate.find(sf => sf.id === stackedFeature.id)) { 344 filteredByDuplicate.push(stackedFeature); 345 } 346 } 347 filtered = filteredByDuplicate; 348 349 // // jetzt noch alle mit gleichem namen rausfiltern 350 // let filteredByName = []; 351 // for (var i in filtered) { 352 // const stackedFeature = filtered[i]; 353 // if (!filteredByName.map(sf => sf.name.trim()).includes(stackedFeature.name.trim())) { 354 // filteredByName.push(stackedFeature); 355 // } 356 // } 357 358 return filtered; 359 }, 360 361 displayPurposes: function (tcfPurposes, stackedFeatures, actualPurposes, actualSpecialPurposes, actualFeatures, actualSpecialFeatures) { 362 const accordion = document.querySelector('#ad-free-subscription-purposes'); 363 if (!accordion) { 364 console.warn('No accordion for displayling purposes found.'); 365 return; 366 } 367 368 // remove old entries 369 const existingItems = document.querySelectorAll('.ad-free-sub-purpose-item:not(.ad-free-sub-purpose-item-necessary)'); 370 existingItems.forEach((e) => { 371 accordion.removeChild(e); 372 }); 373 374 adFreeSubscription.displayPurposesList(accordion, tcfPurposes.stacks, stackedFeatures); 375 adFreeSubscription.displayPurposesList(accordion, tcfPurposes.purposes, actualPurposes); 376 adFreeSubscription.displayPurposesList(accordion, tcfPurposes.specialPurposes, actualSpecialPurposes); 377 adFreeSubscription.displayPurposesList(accordion, tcfPurposes.features, actualFeatures); 378 adFreeSubscription.displayPurposesList(accordion, tcfPurposes.specialFeatures, actualSpecialFeatures); 379 }, 380 381 displayPurposesList: function (accordion, purposesList, currentPurposes) { 382 if (!purposesList) { 383 console.log("No purposes list given."); 384 return; 385 } 386 if (currentPurposes && currentPurposes.length > 0) { 387 for (var i = 0; i < currentPurposes.length; i++) { 388 389 const purposeId = currentPurposes[i]; 390 const purpose = purposesList[purposeId]; 391 if (!purpose) { 392 console.log("No purpose with id '" + purposeId + "' found.") 393 continue; 394 } 395 const name = purpose.name; 396 const description = purpose.description; 397 398 const item = document.createElement('div'); 399 item.classList.add('ad-free-sub-accordion-item'); 400 item.classList.add('ad-free-sub-purpose-item'); 401 402 const button = document.createElement('button'); 403 button.classList.add('ad-free-sub-accordion-item-button'); 404 button.textContent = name; 405 item.appendChild(button); 406 407 const content = document.createElement('div'); 408 content.classList.add('ad-free-sub-accordion-item-content'); 409 content.style.display = 'none'; 410 const contentParagraph = document.createElement('p'); 411 contentParagraph.textContent = description; 412 content.appendChild(contentParagraph); 413 item.appendChild(content); 414 415 accordion.appendChild(item); 416 } 417 } 418 } 419 }; 420 421 if (typeof (Cookiebot) !== 'undefined') { 422 init(); 423 } else { 424 document.addEventListener('DOMContentLoaded', init); 425 } 426 427 function init() { 428 console.log("Ad-Free Subscription JS is loaded and ready."); 429 430 if (typeof (Cookiebot) === 'undefined') { 431 console.error("Cookiebot is not defined!"); 432 const error = new Error("Cookiebot is not defined!"); 433 adFreeSubscription.sendError(error); 434 return; 435 } 436 437 const adFree = typeof (adFreeSubscriptionConfig) !== 'undefined' && adFreeSubscriptionConfig.isAdFreeSubscriber; 438 if (adFree) { 439 console.log("User has a subscription."); 440 if (!Cookiebot.declined) { 441 Cookiebot.submitCustomConsent(false, false, false); 442 } 443 } else if (!adFreeSubscription.isAcceptAll()) { 444 console.log("User has no subscription and hasn't given full consent."); 445 adFreeSubscription.getClientId(); 446 447 // If the Cookiebot dialog has not already been initialized, we have to do so before showing the ad-free-banner 448 if (typeof CookiebotDialog !== 'object' || !CookiebotDialog.visible) { 449 window.addEventListener('CookiebotOnDialogInit', adFreeSubscription.onCookiebotReady);
450 // If Cookiebot already has a response (accept/decline) from the user, it won't show up 451 // Therefore we have to force Cookiebot to show up again 452 if (Cookiebot.hasResponse) { 453 console.log("Cookiebot already has a response, showing up Cookiebot ..."); 454 Cookiebot.show(); 455 } 456 // otherwise, just show the banner 457 } else { 458 adFreeSubscription.showBanner(); 459 } 460 } else { 461 console.log("User has no subscription, but has given full consent."); 462 } 463 } 464})();
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.