. Holds anything in the catalog * that gets injected after the server rewrite (GTM, pixels, lazy embeds, SPA page changes), keeps * the consent state, and releases held elements the moment their category is allowed. * Matching mirrors VSO_Consent::match_url() exactly. ES5, no dependencies. */ (function (w, d) { 'use strict'; var C = w.VSO_CONSENT_CFG; if (!C || w.VSOConsent) { return; }var CATS = ['functional', 'analytics', 'marketing', 'media', 'fonts']; var COOKIE = 'vso_consent'; var debug = /[?&]vso_consent_debug=1\b/.test(location.search); function log() { if (debug && w.console) { console.log.apply(console, ['[vso-consent]'].concat([].slice.call(arguments))); } }// ---- matching ------------------------------------------------------------------------------ function split(u) { u = String(u || '').trim().toLowerCase(); if (!u || /^(data|blob|about|javascript):/.test(u)) { return null; } var m = u.match(/^(?:[a-z][a-z0-9+.\-]*:)?\/\/([^\/?#]*)([\s\S]*)$/); if (m) { return { host: m[1].replace(/^.*@|:\d+$/g, ''), rest: m[2] }; } if (/^[a-z][a-z0-9+.\-]*:/.test(u)) { return null; } // mailto:, tel: return { host: C.host, rest: u }; } function match(url) { var p = split(url), i, j, e, s, pat, hay, ex = false; if (!p) { return null; } for (i = 0; i < C.ex.length; i++) { e = C.ex[i]; if (p.host === e || p.host.slice(-e.length - 1) === '.' + e) { ex = true; break; } } hay = '.' + p.host + p.rest; for (i = 0; i < C.svc.length; i++) { s = C.svc[i]; // An exempt host only escapes the entries not marked 'own' (our trackers on our hosts). if (ex && !s.o) { continue; } for (j = 0; j < s.m.length; j++) { pat = s.m[j]; if (pat.charAt(0) === '/' ? p.rest.indexOf(pat) > -1 : hay.indexOf('.' + pat) > -1) { return s; } } } return null; } function matchInline(code) { var i, j, s, low; if (!code) { return null; } low = code.toLowerCase(); for (i = 0; i < C.svc.length; i++) { s = C.svc[i]; for (j = 0; j < s.i.length; j++) { if (s.i[j] && low.indexOf(s.i[j].toLowerCase()) > -1) { return s; } } } return null; } function svcById(id) { for (var i = 0; i < C.svc.length; i++) { if (C.svc[i].id === id) { return C.svc[i]; } } return null; }// ---- cookies ------------------------------------------------------------------------------- function readCookie(n) { var parts = d.cookie ? d.cookie.split('; ') : [], i, k; for (i = 0; i < parts.length; i++) { k = parts[i].indexOf('='); if (parts[i].slice(0, k) === n) { try { return decodeURIComponent(parts[i].slice(k + 1)); } catch (e) { return parts[i].slice(k + 1); } } } return null; } function writeCookie(n, v, days) { d.cookie = n + '=' + encodeURIComponent(v) + ';path=/;max-age=' + (days * 86400) + ';SameSite=Lax' + (location.protocol === 'https:' ? ';Secure' : ''); } /** Deletes a cookie on this host and on every parent domain (GA sets them on the eTLD+1). */ function eraseCookie(n) { var parts = location.hostname.split('.'), i; d.cookie = n + '=;path=/;max-age=0'; for (i = 0; i < parts.length - 1; i++) { d.cookie = n + '=;path=/;max-age=0;domain=.' + parts.slice(i).join('.'); } } /** Mirror of VSO_Consent::cookie_match(): prefix patterns ("_ga", "pys_", "_ce.") match by * prefix, the rest exactly, so Meta's "fr" never erases "frontend_lang". */ function cookieMatch(n, p) { if (!p) { return false; } if (n === p) { return true; } var last = p.charAt(p.length - 1); return (p.charAt(0) === '_' || last === '_' || last === '.') && n.indexOf(p) === 0; } function eraseFor(cat) { var names = d.cookie ? d.cookie.split('; ') : [], i, j, k, s, n; for (i = 0; i < C.svc.length; i++) { s = C.svc[i]; if (s.c !== cat) { continue; } for (j = 0; j < names.length; j++) { n = names[j].split('=')[0]; for (k = 0; k < s.k.length; k++) { if (cookieMatch(n, s.k[k])) { eraseCookie(n); } } } } }// ---- region / regime ----------------------------------------------------------------------- // Timezone, not IP: the page is cached, so the server cannot know who is asking. Anything we // cannot place gets opt-in, the strictest model, which is lawful everywhere. function region() { var tz = ''; try { tz = Intl.DateTimeFormat().resolvedOptions().timeZone || ''; } catch (e) {} if (/^Europe\//.test(tz) || /^Atlantic\/(Reykjavik|Canary|Madeira|Azores|Faroe)$/.test(tz) || /^Asia\/(Nicosia|Famagusta)$/.test(tz)) { return 'eu'; } if (/^America\/(New_York|Chicago|Denver|Los_Angeles|Phoenix|Anchorage|Detroit|Boise|Juneau|Sitka|Yakutat|Nome|Adak|Metlakatla|Menominee|Indiana\/.+|Kentucky\/.+|North_Dakota\/.+)$/.test(tz) || tz === 'Pacific/Honolulu') { return 'us'; } if (/^America\/(Sao_Paulo|Bahia|Fortaleza|Recife|Belem|Manaus|Cuiaba|Campo_Grande|Porto_Velho|Boa_Vista|Rio_Branco|Maceio|Araguaina|Santarem|Noronha|Eirunepe)$/.test(tz)) { return 'br'; } if (/^America\/(Toronto|Vancouver|Montreal|Edmonton|Winnipeg|Halifax|St_Johns|Regina|Moncton|Whitehorse|Yellowknife|Iqaluit|Glace_Bay|Goose_Bay|Dawson_Creek|Swift_Current)$/.test(tz)) { return 'ca'; } return 'other'; } var REGION = region(); // US state laws (CCPA/CPRA and the rest) are opt-out; everything else here is opt-in. var REGIME = C.regions === 'regional' && REGION === 'us' ? 'optout' : 'optin'; var GPC = navigator.globalPrivacyControl === true;// ---- state --------------------------------------------------------------------------------- var state = null; // standalone: {v, t, id, c:{cat:0|1}, r, m} var once = {}; // per-element one-time releases from a placeholder var listeners = [];function loadState() { var raw = readCookie(COOKIE), s = null; if (raw) { try { s = JSON.parse(raw); } catch (e) { s = null; } } return s && s.v === C.rev && s.c && typeof s.c === 'object' ? s : null; }// ConsentMagic: its own cookies are the truth. cs_enabled_cookie_term_ = yes|no, // term ids resolved by slug from CS_Data.cookielist (printed by ConsentMagic, possibly after us). var CM_SLUGS = { functional: ['functional', 'preferences', 'necessary_functional'], analytics: ['analytics', 'statistics', 'performance'], marketing: ['marketing', 'advertising', 'targeting'], media: ['embedded_video', 'embedded-video', 'video', 'media', 'marketing'], fonts: ['googlefonts', 'google_fonts', 'fonts'] }; function cmTerm(cat) { var cd = w.CS_Data, list, slugs, i, k; if (!cd || !cd.cookielist) { return null; } list = cd.cookielist; slugs = CM_SLUGS[cat] || []; for (i = 0; i < slugs.length; i++) { for (k in list) { if (Object.prototype.hasOwnProperty.call(list, k) && list[k].slug === slugs[i]) { return list[k].term_id; } } } return null; } function cmAllowed(cat) { var cd = w.CS_Data, term, list, k, any = false; if (!cd) { return false; } var prefix = 'cs_enabled_cookie_term' + (cd.test_prefix || '') + '_'; term = cmTerm(cat); if (term !== null) { return readCookie(prefix + term) === 'yes'; } // No matching category on this install: allowed only when every optional one is. list = cd.cookielist || {}; for (k in list) { if (!Object.prototype.hasOwnProperty.call(list, k) || String(list[k].term_id) === String(cd.cs_necessary_cat_id) || String(list[k].ignore) === '1') { continue; } any = true; if (readCookie(prefix + list[k].term_id) !== 'yes') { return false; } } return any; }function allowed(cat) { if (!cat || cat === 'necessary') { return true; } if (C.mode === 'cm') { return cmAllowed(cat); } if (state) { return state.c[cat] === 1; } if (REGIME === 'optout') { return !(GPC && (cat === 'marketing')); } return false; } function snapshot() { var o = {}, i; for (i = 0; i < CATS.length; i++) { o[CATS[i]] = allowed(CATS[i]) ? 1 : 0; } return o; }// ---- Google Consent Mode v2 + WP Consent API ---------------------------------------------- w.dataLayer = w.dataLayer || []; var dl0 = w.dataLayer.length; if (typeof w.gtag !== 'function') { w.gtag = function () { w.dataLayer.push(arguments); }; } function gcm(kind) { var a = allowed('analytics'), m = allowed('marketing'); var g = function (b) { return b ? 'granted' : 'denied'; }; var o = { ad_storage: g(m), ad_user_data: g(m), ad_personalization: g(m), analytics_storage: g(a), functionality_storage: 'granted', personalization_storage: g(allowed('functional')), security_storage: 'granted' }; if (kind === 'default') { o.wait_for_update = 500; } w.gtag('consent', kind, o); if (kind !== 'default') { return; } w.gtag('set', 'ads_data_redaction', true); // A tag printed above the guard (Cloudflare's Google tag gateway injects one at the edge) queued its // config first. While gtag.js has not taken over the queue, move the default in front of it. var dl = w.dataLayer, n = dl.length - dl0; if (dl0 && n > 0 && dl.push === Array.prototype.push) { dl.unshift.apply(dl, dl.splice(dl0, n)); } } function wpConsent() { if (typeof w.wp_set_consent !== 'function') { return; } var map = { functional: ['functional', 'preferences'], analytics: ['statistics', 'statistics-anonymous'], marketing: ['marketing'] }, k, i; for (k in map) { for (i = 0; i < map[k].length; i++) { w.wp_set_consent(map[k][i], allowed(k) ? 'allow' : 'deny'); } } }// ---- holding ------------------------------------------------------------------------------- var N = {}; // native accessors, captured before anything else runs function nativeAccessor(Ctor, prop) { var desc = Ctor && Object.getOwnPropertyDescriptor(Ctor.prototype, prop); return desc && desc.set ? desc : null; } N.script = nativeAccessor(w.HTMLScriptElement, 'src'); N.iframe = nativeAccessor(w.HTMLIFrameElement, 'src'); N.img = nativeAccessor(w.HTMLImageElement, 'src'); N.link = nativeAccessor(w.HTMLLinkElement, 'href'); N.media = nativeAccessor(w.HTMLMediaElement, 'src'); N.source = nativeAccessor(w.HTMLSourceElement, 'src'); var setAttr = Element.prototype.setAttribute; var CAT_ATTR = 'data-vso-cat';function esc(s) { return String(s).replace(/[&<>"']/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; }); } function openLink(url) { var m = url.match(/(?:youtube(?:-nocookie)?\.com\/embed\/|youtu\.be\/)([\w-]{6,})/i); if (m) { return 'https://www.youtube.com/watch?v=' + m[1]; } m = url.match(/player\.vimeo\.com\/video\/(\d+)/i); if (m) { return 'https://vimeo.com/' + m[1]; } if (/\/maps\b/.test(url)) { url = url.replace(/([?&])output=embed(&|$)/, '').replace(/[?&]$/, ''); } return url.indexOf('//') === 0 ? 'https:' + url : url; } function placeholder(svc, url) { return C.ph.split('{provider}').join(esc(svc.n)).split('{link}').join(esc(openLink(url))); }function mark(el, svc, url, attr) { setAttr.call(el, CAT_ATTR, svc.c); setAttr.call(el, 'data-vso-svc', svc.id); setAttr.call(el, attr || 'data-vso-src', url); w.VSOConsent.seen[svc.id] = 1; } /** Returns true when the element was held (the caller must then NOT set the real URL). */ function guard(el, tag, url) { var svc = match(url); if (!svc || allowed(svc.c) || once[url] || once['svc:' + svc.id]) { return false; } if (tag === 'script') { mark(el, svc, url); var t = el.getAttribute('type'); if (t && t !== 'text/plain') { setAttr.call(el, 'data-vso-type', t); } setAttr.call(el, 'type', 'text/plain'); } else if (tag === 'iframe') { mark(el, svc, url); if (el.hasAttribute('sandbox')) { setAttr.call(el, 'data-vso-sandbox', el.getAttribute('sandbox')); el.removeAttribute('sandbox'); } if (svc.c === 'media') { el.srcdoc = placeholder(svc, url); } } else if (tag === 'img' || tag === 'media' || tag === 'source') { // media: MediaElement.js (WP [video]) probes a YouTube/Vimeo page URL as video.src if (svc.c === 'fonts') { return false; } mark(el, svc, url); } else if (tag === 'link') { var rel = (el.getAttribute('rel') || '').toLowerCase(); if (/\b(preconnect|dns-prefetch|prefetch|preload|modulepreload)\b/.test(rel)) { mark(el, svc, url, 'data-vso-href'); setAttr.call(el, 'rel', 'vso-held'); return true; } if (rel.indexOf('stylesheet') < 0) { return false; } mark(el, svc, url, 'data-vso-href'); setAttr.call(el, 'data-vso-rel', rel); setAttr.call(el, 'rel', 'vso-held'); } else { return false; } log('held', tag, svc.id, url); return true; }function patch(Ctor, prop, tag) { var desc = tag === 'link' ? N.link : N[tag]; if (!desc) { return; } try { Object.defineProperty(Ctor.prototype, prop, { configurable: true, enumerable: desc.enumerable, get: function () { return desc.get.call(this); }, set: function (v) { if (!guard(this, tag, String(v))) { desc.set.call(this, v); } } }); } catch (e) { log('patch failed', tag, e); } } patch(w.HTMLScriptElement, 'src', 'script'); patch(w.HTMLIFrameElement, 'src', 'iframe'); patch(w.HTMLImageElement, 'src', 'img'); patch(w.HTMLLinkElement, 'href', 'link'); patch(w.HTMLMediaElement, 'src', 'media'); patch(w.HTMLSourceElement, 'src', 'source');function kind(el) { var tag = el.tagName ? el.tagName.toLowerCase() : ''; return tag === 'video' || tag === 'audio' ? 'media' : tag; } Element.prototype.setAttribute = function (name, value) { var n = String(name).toLowerCase(), tag = kind(this); if (((n === 'src' && (tag === 'script' || tag === 'iframe' || tag === 'img' || tag === 'media' || tag === 'source')) || (n === 'href' && tag === 'link')) && guard(this, tag, String(value))) { return; } return setAttr.apply(this, arguments); };// Parser-inserted and innerHTML-inserted elements never pass through the setters above. var JS_TYPES = /^(|text\/javascript|application\/javascript|module|text\/ecmascript|application\/ecmascript)$/i; function inspect(el) { if (el.nodeType !== 1 || el.hasAttribute(CAT_ATTR) || el.hasAttribute('data-vso-guard')) { return; } var tag = el.tagName.toLowerCase(), url, svc; if (C.mode === 'cm' && (el.hasAttribute('data-cs-class') || el.hasAttribute('data-cs-src'))) { return; } if (tag === 'script') { if (!JS_TYPES.test(el.getAttribute('type') || '')) { return; } url = el.getAttribute('src'); if (url) { if (guard(el, 'script', url)) { el.removeAttribute('src'); el.addEventListener('beforescriptexecute', function (e) { e.preventDefault(); }, { once: true }); } } else if ((svc = matchInline(el.textContent)) && !allowed(svc.c)) { setAttr.call(el, CAT_ATTR, svc.c); setAttr.call(el, 'data-vso-svc', svc.id); if (el.getAttribute('type')) { setAttr.call(el, 'data-vso-type', el.getAttribute('type')); } setAttr.call(el, 'type', 'text/plain'); el.addEventListener('beforescriptexecute', function (e) { e.preventDefault(); }, { once: true }); w.VSOConsent.seen[svc.id] = 1; log('held inline', svc.id); } } else if (tag === 'iframe') { url = el.getAttribute('src'); if (url && guard(el, 'iframe', url)) { el.removeAttribute('src'); } } else if (tag === 'img') { url = el.getAttribute('src'); if (url && guard(el, 'img', url)) { el.removeAttribute('src'); el.removeAttribute('srcset'); } } else if (tag === 'link') { url = el.getAttribute('href'); if (url && guard(el, 'link', url)) { el.removeAttribute('href'); } } else if (tag === 'video' || tag === 'audio' || tag === 'source') { url = el.getAttribute('src'); // : the browser never fetches it, and MediaElement.js reads // the URL from it to build its YouTube player (released with player_api on consent) if (tag === 'source' && el.getAttribute('type') && !d.createElement('video').canPlayType(el.getAttribute('type'))) { return; } if (url && guard(el, kind(el), url)) { el.removeAttribute('src'); } } } function scan(root) { inspect(root); if (root.querySelectorAll) { var list = root.querySelectorAll('script,iframe,img[src],link[href],video[src],audio[src],source[src]'), i; for (i = 0; i < list.length; i++) { inspect(list[i]); } } } var mo = new MutationObserver(function (muts) { for (var i = 0; i < muts.length; i++) { for (var j = 0; j < muts[i].addedNodes.length; j++) { if (muts[i].addedNodes[j].nodeType === 1) { scan(muts[i].addedNodes[j]); } } } elementorVideos(); }); mo.observe(d.documentElement, { childList: true, subtree: true });// innerHTML & co.: an iframe or img inserted this way starts loading as it is attached, a // microtask before the observer above sees it. Neutralise the markup first in an inert //