1import 'https://cdn.jsdelivr.net/gh/orestbida/[email protected]/dist/cookieconsent.umd.js'; 2 3// Which services this instance actually runs, emitted server-side by ThirdPartyScripts 4// .CookieConsentScript() ahead of this module (e.g. APSA has no Google Ads, so it gets no 5// advertising category). Fall back to everything-on if the flags are missing: over-disclosing 6// is the safe failure direction for consent copy. 7var services = window.ecprConsent || { companyName: "", cookieName: "", cookieDomain: "", googleAnalytics: true, googleAds: true, microsoftClarity: true }; 8 9// One consent cookie per deployment. Production sets cookieDomain to the registrable domain 10// (ecpr.eu) so a single cookie covers both the main site and events.ecpr.eu - without that, each 11// host writes its own copy, the older one shadows the newer on reads (document.cookie hides 12// domains), and the banner re-appears on every load. Test sites get a host-suffixed cookieName 13// (TestMode) so they neither read nor write production's cookie: consent never leaks between 14// environments. Blank domain = host-only (local dev, test sites). 15var cookieOptions = { name: services.cookieName || "site_consent" }; 16if (services.cookieDomain) { 17 cookieOptions.domain = services.cookieDomain; 18} 19 20// Cookies each consent category is responsible for. GA/Clarity are analytics; the Google Ads 21// conversion linker (_gcl_*) is advertising. (Tawk.to live chat is click-to-load - user-requested, 22// so outside consent categories entirely.) 23var CATEGORY_COOKIE_PATTERNS = { 24 analytics: [/^_ga/, /^_clck/, /^_clsk/], 25 advertising: [/^_gcl/] 26}; 27 28// GA writes _ga / _ga_* to the registrable domain (e.g. .ecpr.eu), which CookieConsent's autoClear 29// (scoped to location.hostname) can't delete. Expire the category's cookies across the current host and 30// every parent domain so withdrawal actually removes them on production, not just on localhost. 31function clearCategoryCookies(category) { 32 var patterns = CATEGORY_COOKIE_PATTERNS[category] || []; 33 var hostname = window.location.hostname; 34 var hostParts = hostname.split("."); 35 // "" = host-only (no domain attribute); the bare hostname covers cookies written with an explicit 36 // domain=<host> (e.g. localhost); the dotted suffixes cover the registrable domain GA uses (.ecpr.eu). 37 var domains = ["", hostname]; 38 for (var i = 0; i < hostParts.length - 1; i++) { 39 domains.push("." + hostParts.slice(i).join(".")); 40 } 41 document.cookie.split(";").forEach(function (entry) { 42 var name = entry.split("=")[0].trim(); 43 if (!patterns.some(function (p) { return p.test(name); })) return; 44 domains.forEach(function (domain) { 45 document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/" + 46 (domain ? ";domain=" + domain : ""); 47 }); 48 }); 49} 50 51// Google Consent Mode: the layouts queue a 'default: denied' before gtag.js loads; this sends the 52// matching 'update' whenever consent is granted or withdrawn. Analytics and advertising consent are 53// independent: GA storage follows the analytics category, the ad_* signals follow advertising (which, 54// when the instance runs no ads, simply never exists and so stays denied). On pages without GA the 55// gtag global doesn't exist, hence the guard. 56function updateGtagConsent() { 57 if (typeof window.gtag !== "function") return; 58 var analytics = CookieConsent.acceptedCategory("analytics") ? "granted" : "denied"; 59 var advertising = CookieConsent.acceptedCategory("advertising") ? "granted" : "denied"; 60 window.gtag("consent", "update", { 61 analytics_storage: analytics, 62 ad_storage: advertising, 63 ad_user_data: advertising, 64 ad_personalization: advertising 65 }); 66} 67 68// --- Copy and structure assembled from the instance's services --------------------------------- 69 70var analyticsServiceNames = []; 71if (services.googleAnalytics) { analyticsServiceNames.push("Google Analytics"); } 72if (services.microsoftClarity) { analyticsServiceNames.push("Microsoft Clarity"); } 73 74var overviewDescription = "We use necessary cookies to make our site work." 75 + (analyticsServiceNames.length 76 ? " We'd also like to set analytics cookies that help us make improvements by measuring how you use the site" 77 + (services.googleAds ? ", and advertising cookies to measure the effectiveness of our advertising" : "") 78 + "." 79 : (services.googleAds ? " We'd also like to set advertising cookies to measure the effectiveness of our advertising." : "")) 80 + ((analyticsServiceNames.length || services.googleAds) ? " These will be set only if you accept." : ""); 81 82var categories = { 83 necessary: { 84 readOnly: true 85 }, 86 functionality: {}, 87 analytics: { 88 autoClear: { 89 cookies: [ 90 { name: /^_ga/ }, 91 { name: /^_clck/ }, 92 { name: /^_clsk/ } 93 ] 94 } 95 } 96}; 97 98var sections = [ 99 { 100 title: "Our use of cookies", 101 description: overviewDescription 102 }, 103 { 104 title: "Strictly Necessary Cookies <span class=\"pm__badge\">Always Enabled</span>", 105 description: "Necessary cookies enable core functionality such as security, network management, and accessibility. You may disable these by changing your browser settings, but this may affect how the website functions.", 106 linkedCategory: "necessary" 107 } 108]; 109 110if (analyticsServiceNames.length) { 111 sections.push({
112 title: "Analytics Cookies", 113 description: "We'd like to set " + analyticsServiceNames.join(" and ") + " cookies to help us improve our website by collecting and reporting information on how you use it. The cookies collect information in a way that does not directly identify anyone. For more information on how these cookies work please see our <a href='/Privacy'>Privacy Notice</a>.", 114 linkedCategory: "analytics" 115 }); 116} 117 118if (services.googleAds) { 119 categories.advertising = { 120 autoClear: { 121 cookies: [ 122 { name: /^_gcl/ } 123 ] 124 } 125 }; 126 sections.push({ 127 title: "Advertising Cookies", 128 description: "We'd like to set Google Ads cookies to measure the effectiveness of our advertising. Google may also use these cookies to personalise the ads you see on other websites. For more information on how these cookies work please see our <a href='/Privacy'>Privacy Notice</a>.", 129 linkedCategory: "advertising" 130 }); 131} 132 133CookieConsent.run({ 134 cookie: cookieOptions, 135 // Bumped when the consent scope changes (v1: advertising split out of analytics) so returning 136 // visitors are re-prompted and re-consent under the current category descriptions. 137 revision: 1, 138 // Accept-time sync. Without this, the only consent update gtag ever sees is the 'denied' pushed by 139 // the load-time sync below, and the user's acceptance does nothing until the next page load. 140 onFirstConsent: function () { 141 updateGtagConsent(); 142 }, 143 // Reactivating a blocked script (text/plain -> text/javascript) loads it, but withdrawal can't 144 // unload an already-running script (GA, Clarity keep sending hits until the page is gone). Reload 145 // on revocation so the scripts return to their blocked state and autoClear takes effect. 146 // The consent 'update: denied' must go first: it stops live gtag writing cookies, closing the race 147 // where GA re-set _ga_* between the delete and the reload. 148 onChange: function ({ changedCategories, cookie }) { 149 updateGtagConsent(); 150 var revoked = ["analytics", "advertising"].filter(function (category) { 151 return changedCategories.includes(category) && !cookie.categories.includes(category); 152 }); 153 if (revoked.length) { 154 revoked.forEach(clearCategoryCookies); 155 window.location.reload(); 156 } 157 }, 158 guiOptions: { 159 consentModal: { 160 layout: "bar", 161 position: "bottom", 162 equalWeightButtons: true, 163 flipButtons: true 164 }, 165 preferencesModal: { 166 layout: "bar", 167 position: "right", 168 equalWeightButtons: false, 169 flipButtons: true 170 } 171 }, 172 categories: categories, 173 language: { 174 default: "en", 175 autoDetect: "browser", 176 translations: { 177 en: { 178 consentModal: { 179 title: "Our use of cookies", 180 description: overviewDescription, 181 acceptAllBtn: "Accept all", 182 acceptNecessaryBtn: "Reject all", 183 showPreferencesBtn: "Manage preferences", 184 footer: "<a href='/Privacy'>Privacy Notice</a>" 185 + (services.companyName ? "\n<a href='/'>" + services.companyName + "</a>" : "") 186 }, 187 preferencesModal: { 188 title: "Consent Preferences Center", 189 acceptAllBtn: "Accept all", 190 acceptNecessaryBtn: "Reject all", 191 savePreferencesBtn: "Save preferences", 192 closeIconLabel: "Close modal", 193 serviceCounterLabel: "Service|Services", 194 sections: sections 195 } 196 } 197 } 198 } 199}).then(function () { 200 // Sync gtag with the stored choice on every page load. Deliberately after run() resolves rather 201 // than in an onConsent callback: the callback proved unreliable on reloads with existing consent, 202 // leaving gtag stuck on the 'denied' defaults (hits sent with gcs=G100, no _ga cookies). 203 updateGtagConsent(); 204});
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.