PageSourceSearch

https://www.swimaholic.bg/scripts/consent-manager.js?v=020fe5a6e0a92e14

js swimaholic.bg collected 2026-10-06 18:43:27 UTC 31,938 bytes, 1,171 lines download raw bytes

1(function (window, document) {
2	'use strict';
3
4	var script = document.currentScript;
5	var COOKIE_SCHEMA_VERSION = 2;
6	var DEFAULT_COOKIE_NAME = 'swimaholic_consent';
7	var DEFAULT_POLICY_REVISION = '1';
8	var DEFAULT_MAX_AGE_DAYS = 180;
9	var MAX_CLOCK_SKEW_MS = 5 * 60 * 1000;
10	var CONSENT_ACTIVATED_EVENT = 'swimaholic:consent-activated';
11	var bannerId = 'swim-consent-banner';
12	var preferencesId = 'swim-consent-preferences';
13	var analyticsCheckboxId = 'consent-analytics';
14	var targetingCheckboxId = 'consent-targeting';
15	var activationQueue = [];
16	var consentListeners = [];
17	var activationRunning = false;
18	var preferencesOpen = false;
19	var pendingOpen = false;
20	var previousFocus = null;
21	var observer = null;
22	var pendingConsentEvent = null;
23	var lastAppliedGoogleConsent = null;
24
25	function scriptAttribute(name, fallbackName) {
26		if (!script || !script.getAttribute) {
27			return '';
28		}
29
30		return script.getAttribute(name) || (fallbackName ? script.getAttribute(fallbackName) : '') || '';
31	}
32
33	function validCookieName(value) {
34		return /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(value);
35	}
36
37	function maxAgeDays(value) {
38		var days = parseInt(value, 10);
39
40		if (!isFinite(days) || days < 1 || days > 730) {
41			return DEFAULT_MAX_AGE_DAYS;
42		}
43
44		return days;
45	}
46
47	var configuredCookieName = scriptAttribute('data-consent-cookie-name', 'data-consent-cookie');
48	var cookieName = validCookieName(configuredCookieName) ? configuredCookieName : DEFAULT_COOKIE_NAME;
49	var policyRevision = scriptAttribute('data-consent-policy-revision', 'data-consent-revision') || DEFAULT_POLICY_REVISION;
50	var configuredMaxAge = scriptAttribute('data-consent-max-age-days', 'data-consent-days');
51	var statisticsEndpoint = scriptAttribute('data-consent-statistics-endpoint');
52	var vendorInventoryFingerprint = scriptAttribute('data-consent-vendor-inventory-fingerprint');
53	var vendorInventoryComplete = scriptAttribute('data-consent-vendor-inventory-complete') === 'true' &&
54		/^[0-9a-f]{64}$/.test(vendorInventoryFingerprint);
55	var gtmConsentBridgeEnabled = scriptAttribute('data-consent-gtm-bridge-enabled') === 'true';
56	var cookieMaxAgeDays = maxAgeDays(configuredMaxAge);
57	var cookieMaxAgeSeconds = cookieMaxAgeDays * 24 * 60 * 60;
58	var cookieMaxAgeMs = cookieMaxAgeSeconds * 1000;
59
60	function undecidedState() {
61		return {
62			v: COOKIE_SCHEMA_VERSION,
63			revision: policyRevision,
64			inventory: vendorInventoryFingerprint,
65			decisionId: null,
66			timestamp: null,
67			analytics: false,
68			targeting: false,
69			decided: false
70		};
71	}
72
73	function trim(value) {
74		return String(value || '').replace(/^\s+|\s+$/g, '');
75	}
76
77	function readCookie(name) {
78		var cookies = document.cookie ? document.cookie.split(';') : [];
79		var prefix = name + '=';
80		var i;
81		var item;
82
83		for (i = 0; i < cookies.length; i += 1) {
84			item = trim(cookies[i]);
85			if (item.indexOf(prefix) === 0) {
86				return item.substring(prefix.length);
87			}
88		}
89
90		return null;
91	}
92
93	function isStrictStoredState(value) {
94		var allowedKeys = {
95			v: true,
96			revision: true,
97			inventory: true,
98			decisionId: true,
99			timestamp: true,
100			analytics: true,
101			targeting: true
102		};
103		var keys;
104		var now = new Date().getTime();
105		var i;
106
107		if (!value || Object.prototype.toString.call(value) !== '[object Object]') {
108			return false;
109		}
110
111		keys = Object.keys(value);
112		if (keys.length !== 7) {
113			return false;
114		}
115
116		for (i = 0; i < keys.length; i += 1) {
117			if (!allowedKeys[keys[i]]) {
118				return false;
119			}
120		}
121
122		if (value.v !== COOKIE_SCHEMA_VERSION
123			|| value.revision !== policyRevision
124			|| value.inventory !== vendorInventoryFingerprint
125		) {
126			return false;
127		}
128
129		if (typeof value.decisionId !== 'string' || !/^[0-9a-f]{32}$/.test(value.decisionId)) {
130			return false;
131		}
132
133		if (typeof value.timestamp !== 'number' || !isFinite(value.timestamp) || Math.floor(value.timestamp) !== value.timestamp) {
134			return false;
135		}
136
137		if (value.timestamp <= 0 || value.timestamp > now + MAX_CLOCK_SKEW_MS || now - value.timestamp > cookieMaxAgeMs) {
138			return false;
139		}
140
141		return typeof value.analytics === 'boolean' && typeof value.targeting === 'boolean';
142	}
143
144	function createDecisionId() {
145		var bytes;
146		var result = '';
147		var i;
148
149		if (window.crypto && typeof window.crypto.getRandomValues === 'function') {
150			bytes = new Uint8Array(16);
151			window.crypto.getRandomValues(bytes);
152			for (i = 0; i < bytes.length; i += 1) {
153				result += ('0' + bytes[i].toString(16)).slice(-2);
154			}
155			return result;
156		}
157
158		// The identifier is only a correlation value, never a secret or token.
159		while (result.length < 32) {
160			result += Math.floor(Math.random() * 0x100000000).toString(16);
161		}
162		return (result + new Date().getTime().toString(16)).slice(0, 32);
163	}
164
165	function expireHostCookie(name) {
166		document.cookie = name + '=; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/; SameSite=Lax';
167	}
168
169	function restoreState() {
170		var encoded = readCookie(cookieName);
171		var parsed;
172
173		if (!vendorInventoryComplete) {
174			expireHostCookie(cookieName);
175			return undecidedState();
176		}
177
178		if (encoded === null) {
179			return undecidedState();
180		}
181
182		try {
183			parsed = JSON.parse(decodeURIComponent(encoded));
184		} catch (error) {
185			expireHostCookie(cookieName);
186			return undecidedState();
187		}
188
189		if (!isStrictStoredState(parsed)) {
190			expireHostCookie(cookieName);
191			return undecidedState();
192		}
193
194		parsed.decided = true;
195		return parsed;
196	}
197
198	var consentState = restoreState();
199
200	window.dataLayer = window.dataLayer && typeof window.dataLayer.push === 'function' ? window.dataLayer : [];
201	window.gtag = typeof window.gtag === 'function' ? window.gtag : function () {
202		window.dataLayer.push(arguments);
203	};
204
205	function googleConsentPayload(state) {
206		return {
207			analytics_storage: state.analytics ? 'granted' : 'denied',
208			ad_storage: state.targeting ? 'granted' : 'denied',
209			ad_user_data: state.targeting ? 'granted' : 'denied',
210			ad_personalization: state.targeting ? 'granted' : 'denied',
211			functionality_storage: 'denied',
212			security_storage: 'granted',
213			personalization_storage: 'denied'
214		};
215	}
216
217	function copyGoogleConsentPayload(payload) {
218		if (!payload) {
219			return null;
220		}
221
222		// Only fixed Consent Mode enums are exposed. This deliberately excludes
223		// decision identifiers, timestamps and any callback-provided values.
224		return {
225			analytics_storage: payload.analytics_storage === 'granted' ? 'granted' : 'denied',
226			ad_storage: payload.ad_storage === 'granted' ? 'granted' : 'denied',
227			ad_user_data: payload.ad_user_data === 'granted' ? 'granted' : 'denied',
228			ad_personalization: payload.ad_personalization === 'granted' ? 'granted' : 'denied',
229			functionality_storage: payload.functionality_storage === 'granted' ? 'granted' : 'denied',
230			security_storage: payload.security_storage === 'granted' ? 'granted' : 'denied',
231			personalization_storage: payload.personalization_storage === 'granted' ? 'granted' : 'denied'
232		};
233	}
234
235	function publicBridgeState() {
236		return {
237			enabled: gtmConsentBridgeEnabled,
238			listenerRegistered: consentListeners.length === 1,
239			pendingEvent: pendingConsentEvent !== null,
240			lastApplied: copyGoogleConsentPayload(lastAppliedGoogleConsent)
241		};
242	}
243
244	function applyGoogleConsent(state) {
245		// When audited GTM is active, its Consent Initialization custom template
246		// owns updateConsentState() through the synchronous listener below.
247		// Page-level gtag updates are deliberately not used as a substitute because
248		// GTM can process them after the next event.
249		if (gtmConsentBridgeEnabled) {
250			return notifyConsentListeners(state);
251		}
252		window.gtag('consent', 'update', googleConsentPayload(state));
253		return true;
254	}
255
256	function notifyConsentListeners(state) {
257		var payload = googleConsentPayload(state);
258
259		if (consentListeners.length !== 1) {
260			return false;
261		}
262		try {
263			consentListeners[0](payload);
264			lastAppliedGoogleConsent = copyGoogleConsentPayload(payload);
265			return true;
266		} catch (error) {
267			// Suppress the following measurement event when GTM could not apply the
268			// new state. This keeps grants and especially withdrawals fail-closed.
269			return false;
270		}
271	}
272
273	function addConsentListener(callback) {
274		if (typeof callback !== 'function' || consentListeners.length !== 0) {
275			return false;
276		}
277		consentListeners.push(callback);
278		if (consentState.decided) {
279			try {
280				var payload = googleConsentPayload(consentState);
281				callback(payload);
282				lastAppliedGoogleConsent = copyGoogleConsentPayload(payload);
283				if (pendingConsentEvent) {
284					var pending = pendingConsentEvent;
285					pendingConsentEvent = null;
286					emitPendingConsentEvent(pending);
287				}
288			} catch (error) {
289				consentListeners.pop();
290				return false;
291			}
292		}
293		return true;
294	}
295
296	function emitConsentEvents(state, source, previous) {
297		var retainedTransitions = arguments.length > 3 && arguments[3] ? arguments[3] : {};
298		var analyticsGrantSource = state.analytics
299			? (retainedTransitions.analytics || ((!previous || !previous.analytics) ? source : null))
300			: null;
301		var targetingGrantSource = state.targeting
302			? (retainedTransitions.targeting || ((!previous || !previous.targeting) ? source : null))
303			: null;
304		window.dataLayer.push({
305			event: 'swimaholic_consent_update',
306			consent_source: source,
307			consent_revision: policyRevision,
308			consent_analytics: state.analytics ? 'granted' : 'denied',
309			consent_targeting: state.targeting ? 'granted' : 'denied'
310		});
311
312		if (analyticsGrantSource) {
313			window.dataLayer.push({
314				event: 'CookieScriptCategory-performance',
315				consent_source: analyticsGrantSource
316			});
317		}
318
319		if (targetingGrantSource) {
320			window.dataLayer.push({
321				event: 'CookieScriptCategory-targeting',
322				consent_source: targetingGrantSource
323			});
324		}
325	}
326
327	function consentEventFor(state, source, previous, retained) {
328		retained = retained || {};
329		var retainedTransitions = retained.transitions || {};
330		return {
331			state: state,
332			source: source,
333			previous: previous,
334			transitions: {
335				analytics: state.analytics
336					? (retainedTransitions.analytics || ((!previous || !previous.analytics) ? source : null))
337					: null,
338				targeting: state.targeting
339					? (retainedTransitions.targeting || ((!previous || !previous.targeting) ? source : null))
340					: null
341			}
342		};
343	}
344
345	function emitPendingConsentEvent(pending) {
346		emitConsentEvents(pending.state, pending.source, pending.previous, pending.transitions);
347	}
348
349	if (consentState.decided) {
350		var restoredConsentEvent = consentEventFor(consentState, 'restore', null, null);
351		if (applyGoogleConsent(consentState)) {
352			emitPendingConsentEvent(restoredConsentEvent);
353		} else {
354			pendingConsentEvent = restoredConsentEvent;
355		}
356	}
357
358	function publicState() {
359		return {
360			v: consentState.v,
361			revision: consentState.revision,
362			inventory: consentState.inventory,
363			decisionId: consentState.decisionId,
364			timestamp: consentState.timestamp,
365			analytics: consentState.analytics,
366			targeting: consentState.targeting,
367			decided: consentState.decided
368		};
369	}
370
371	function hasConsent(category) {
372		category = String(category || '').toLowerCase();
373
374		if (category === 'necessary' || category === 'essential' || category === 'security') {
375			return true;
376		}
377
378		// There is no functionality preference in this CMP revision. It must
379		// remain fail-closed until one is exposed to the visitor and persisted.
380		if (category === 'functionality') {
381			return false;
382		}
383
384		if (!consentState.decided) {
385			return false;
386		}
387
388		if (category === 'analytics' || category === 'performance') {
389			return consentState.analytics;
390		}
391
392		if (category === 'targeting' || category === 'marketing' || category === 'advertising') {
393			return consentState.targeting;
394		}
395
396		return false;
397	}
398
399	function normalizeScriptCategory(node) {
400		var category = node.getAttribute('data-consent-category') ||
401			node.getAttribute('data-measuring-script-category') ||
402			node.getAttribute('data-cookiecategory') || '';
403
404		category = String(category).toLowerCase();
405		if (category === 'analytics' || category === 'performance') {
406			return 'analytics';
407		}
408
409		if (category === 'targeting' || category === 'marketing' || category === 'advertising') {
410			return 'targeting';
411		}
412
413		return '';
414	}
415
416	function isInertConsentScript(node) {
417		var category;
418
419		if (!node || String(node.tagName || '').toLowerCase() !== 'script') {
420			return false;
421		}
422
423		category = normalizeScriptCategory(node);
424		return category !== '' && String(node.getAttribute('type') || '').toLowerCase() === 'text/plain';
425	}
426
427	function matchesSelector(node, selector) {
428		var matches = node && (node.matches || node.matchesSelector || node.msMatchesSelector || node.webkitMatchesSelector);
429
430		return matches ? matches.call(node, selector) : false;
431	}
432
433	function enqueueScript(node) {
434		var category;
435
436		if (!isInertConsentScript(node) || node.getAttribute('data-consent-activated') === 'true' || node.getAttribute('data-consent-queued') === 'true') {
437			return;
438		}
439
440		category = normalizeScriptCategory(node);
441		if (!hasConsent(category)) {
442			return;
443		}
444
445		node.setAttribute('data-consent-queued', 'true');
446		activationQueue.push(node);
447	}
448
449	function enqueueScripts(root) {
450		var selector = 'script[type="text/plain"][data-consent-category], ' +
451			'script[type="text/plain"][data-measuring-script-category], ' +
452			'script[type="text/plain"][data-cookiecategory]';
453		var nodes;
454		var i;
455
456		if (!root) {
457			return;
458		}
459
460		if (matchesSelector(root, selector)) {
461			enqueueScript(root);
462		}
463
464		if (!root.querySelectorAll) {
465			return;
466		}
467
468		nodes = root.querySelectorAll(selector);
469		for (i = 0; i < nodes.length; i += 1) {
470			enqueueScript(nodes[i]);
471		}
472	}
473
474	function copyScriptAttributes(source, target) {
475		var ignored = {
476			type: true,
477			async: true,
478			defer: true,
479			'data-consent-category': true,
480			'data-measuring-script-category': true,
481			'data-cookiecategory': true,
482			'data-cookiescript': true,
483			'data-consent-activated': true,
484			'data-consent-queued': true
485		};
486		var attributes = source.attributes || [];
487		var i;
488		var attribute;
489
490		for (i = 0; i < attributes.length; i += 1) {
491			attribute = attributes[i];
492			if (!ignored[String(attribute.name).toLowerCase()]) {
493				target.setAttribute(attribute.name, attribute.value);
494			}
495		}
496	}
497
498	function processActivationQueue() {
499		var source;
500		var category;
501		var active;
502		var nonBlocking;
503		var finish;
504
505		if (activationRunning || activationQueue.length === 0) {
506			return;
507		}
508
509		source = activationQueue.shift();
510		category = normalizeScriptCategory(source);
511		if (!source.parentNode || !hasConsent(category)) {
512			source.removeAttribute('data-consent-queued');
513			processActivationQueue();
514			return;
515		}
516
517		activationRunning = true;
518		source.setAttribute('data-consent-activated', 'true');
519		source.removeAttribute('data-consent-queued');
520		active = document.createElement('script');
521		copyScriptAttributes(source, active);
522		nonBlocking = source.hasAttribute('async') || source.hasAttribute('defer');
523		if (source.hasAttribute('async')) {
524			active.async = true;
525		} else {
526			active.async = false;
527		}
528
529		if (source.hasAttribute('defer')) {
530			active.defer = true;
531		}
532
533		finish = function () {
534			active.onload = null;
535			active.onerror = null;
536			activationRunning = false;
537			processActivationQueue();
538		};
539
540		if (source.getAttribute('src')) {
541			if (!nonBlocking) {
542				active.onload = finish;
543				active.onerror = finish;
544			}
545			source.parentNode.insertBefore(active, source);
546			if (nonBlocking) {
547				finish();
548			}
549			return;
550		}
551
552		active.text = source.text || source.textContent || source.innerHTML || '';
553		source.parentNode.insertBefore(active, source);
554		finish();
555	}
556
557	function activateAllowedScripts(root) {
558		enqueueScripts(root || document);
559		processActivationQueue();
560	}
561
562	function observeInertScripts() {
563		var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
564
565		if (!MutationObserver || !document.documentElement || observer) {
566			return;
567		}
568
569		observer = new MutationObserver(function (mutations) {
570			var i;
571			var j;
572
573			for (i = 0; i < mutations.length; i += 1) {
574				for (j = 0; j < mutations[i].addedNodes.length; j += 1) {
575					enqueueScripts(mutations[i].addedNodes[j]);
576				}
577			}
578
579			processActivationQueue();
580		});
581
582		observer.observe(document.documentElement, {childList: true, subtree: true});
583	}
584
585	function secureCookieSuffix() {
586		return window.location && window.location.protocol === 'https:' ? '; Secure' : '';
587	}
588
589	function persistState(state) {
590		var stored = {
591			v: COOKIE_SCHEMA_VERSION,
592			revision: policyRevision,
593			inventory: vendorInventoryFingerprint,
594			decisionId: state.decisionId,
595			timestamp: state.timestamp,
596			analytics: state.analytics,
597			targeting: state.targeting
598		};
599		var expires = new Date(state.timestamp + cookieMaxAgeMs).toUTCString();
600
601		document.cookie = cookieName + '=' + encodeURIComponent(JSON.stringify(stored)) +
602			'; Max-Age=' + cookieMaxAgeSeconds + '; Expires=' + expires + '; Path=/; SameSite=Lax' + secureCookieSuffix();
603	}
604
605	function normalizeDecisionAction(action, state) {
606		action = String(action || 'custom').toLowerCase();
607		if (action === 'accept_all' && state.analytics && state.targeting) {
608			return action;
609		}
610		if (action === 'reject_all' && !state.analytics && !state.targeting) {
611			return action;
612		}
613		return 'custom';
614	}
615
616	function reportDecision(state, phase, action) {
617		var body;
618		var request;
619
620		if (!statisticsEndpoint || !state || !state.decided) {
621			return;
622		}
623
624		body = 'phase=' + encodeURIComponent(phase) + '&action=' + encodeURIComponent(action);
625		try {
626			if (window.navigator && typeof window.navigator.sendBeacon === 'function' &&
627				window.navigator.sendBeacon(statisticsEndpoint, body)
628			) {
629				return;
630			}
631		} catch (beaconError) {
632			// Reporting is best-effort and must never affect the consent choice.
633		}
634
635		if (typeof window.fetch === 'function') {
636			try {
637				window.fetch(statisticsEndpoint, {
638					method: 'POST',
639					headers: {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'},
640					body: body,
641					credentials: 'same-origin',
642					keepalive: true
643				})['catch'](function () {});
644				return;
645			} catch (fetchError) {
646				// Older browsers can expose fetch without supporting keepalive.
647			}
648		}
649
650		if (typeof window.XMLHttpRequest === 'function') {
651			try {
652				request = new window.XMLHttpRequest();
653				request.open('POST', statisticsEndpoint, true);
654				request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8');
655				request.send(body);
656			} catch (requestError) {
657				// A statistics outage must stay invisible to the visitor.
658			}
659		}
660	}
661
662	function expireCookieEverywhere(name) {
663		var hostname = window.location && window.location.hostname ? String(window.location.hostname) : '';
664		var labels = hostname.split('.');
665		var base = name + '=; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/; SameSite=Lax' + secureCookieSuffix();
666		var i;
667
668		document.cookie = base;
669		if (!hostname || /^\d{1,3}(\.\d{1,3}){3}$/.test(hostname) || hostname.indexOf(':') !== -1) {
670			return;
671		}
672
673		for (i = 0; i <= labels.length - 2; i += 1) {
674			document.cookie = base + '; Domain=.' + labels.slice(i).join('.');
675		}
676	}
677
678	function cookieNames() {
679		var cookies = document.cookie ? document.cookie.split(';') : [];
680		var result = [];
681		var i;
682		var separator;
683		var name;
684
685		for (i = 0; i < cookies.length; i += 1) {
686			separator = cookies[i].indexOf('=');
687			name = trim(separator === -1 ? cookies[i] : cookies[i].substring(0, separator));
688			if (name) {
689				result.push(name);
690			}
691		}
692
693		return result;
694	}
695
696	function clearCookies(patterns) {
697		var names = cookieNames();
698		var i;
699		var j;
700
701		for (i = 0; i < names.length; i += 1) {
702			for (j = 0; j < patterns.length; j += 1) {
703				if (patterns[j].test(names[i])) {
704					expireCookieEverywhere(names[i]);
705					break;
706				}
707			}
708		}
709	}
710
711	function removeStorageItem(storageName, key) {
712		try {
713			if (window[storageName]) {
714				window[storageName].removeItem(key);
715			}
716		} catch (error) {
717			// Storage can be unavailable in private mode or when the browser blocks it.
718		}
719	}
720
721	function removeStorageKeysMatching(storageName, patterns) {
722		var storage;
723		var keys = [];
724		var i;
725		var j;
726		var key;
727
728		try {
729			storage = window[storageName];
730			if (!storage || typeof storage.key !== 'function') {
731				return;
732			}
733
734			for (i = 0; i < storage.length; i += 1) {
735				key = storage.key(i);
736				for (j = 0; key !== null && j < patterns.length; j += 1) {
737					if (patterns[j].test(key)) {
738						keys.push(key);
739						break;
740					}
741				}
742			}
743
744			for (i = 0; i < keys.length; i += 1) {
745				storage.removeItem(keys[i]);
746			}
747		} catch (error) {
748			// Storage enumeration can be blocked independently of cookie access.
749		}
750	}
751
752	function clearRevokedData(revokedAnalytics, revokedTargeting) {
753		if (revokedAnalytics) {
754			clearCookies([/^_ga(?:_|$)/, /^_gid$/, /^_gat(?:_|$)/]);
755		}
756
757		if (revokedTargeting) {
758			clearCookies([
759				/^_gcl_/,
760				/^_fbp$/,
761				/^_fbc$/,
762				/^_ttp$/,
763				/^_tt_enable_cookie$/,
764				/^ttcsid(?:_|$)/,
765				/^_sp_(?:id|ses)(?:\.|$)/,
766				/^gp_(?:e|s)$/,
767				/^__oppref$/,
768				/^__obref$/
769			]);
770			removeStorageItem('localStorage', 'swimaholic.ecomail.sportex.basket.v1');
771			removeStorageItem('sessionStorage', 'swimaholic.ecomail.sportex.basket.v1');
772			removeStorageItem('localStorage', 'swimaholic_ecomail_popup_59_hidden_until');
773			removeStorageKeysMatching('localStorage', [/^snowplowOutQueue_(?:[A-Za-z0-9]+_)*cf_/]);
774			removeStorageKeysMatching('sessionStorage', [/^snowplowOutQueue_(?:[A-Za-z0-9]+_)*cf_/]);
775		}
776	}
777
778	function clearUnsupportedOptionalData() {
779		clearCookies([
780			/^recent_products(?:\[\d+\]|%5b\d+%5d)$/i,
781			/^news-read$/,
782			/^refId$/i,
783			/^last_basket$/,
784			/^hideBasket$/
785		]);
786	}
787
788	// Fresh, expired-policy and malformed decisions all restore as denied. Purge
789	// known vendor state immediately so an old identifier cannot outlive the
790	// decision that previously allowed it.
791	clearRevokedData(!consentState.analytics, !consentState.targeting);
792	clearUnsupportedOptionalData();
793
794	function element(id) {
795		return document.getElementById(id);
796	}
797
798	function setHidden(node, hidden) {
799		if (!node) {
800			return;
801		}
802
803		if (hidden) {
804			node.setAttribute('hidden', 'hidden');
805			node.setAttribute('aria-hidden', 'true');
806		} else {
807			node.removeAttribute('hidden');
808			node.setAttribute('aria-hidden', 'false');
809		}
810	}
811
812	function setModalBodyState(open) {
813		var body = document.body;
814
815		if (!body) {
816			return;
817		}
818
819		if (body.classList) {
820			body.classList[open ? 'add' : 'remove']('swim-consent-modal-open');
821			return;
822		}
823
824		if (open && body.className.indexOf('swim-consent-modal-open') === -1) {
825			body.className += ' swim-consent-modal-open';
826		} else if (!open) {
827			body.className = trim(body.className.replace(/(?:^|\s)swim-consent-modal-open(?:\s|$)/g, ' '));
828		}
829	}
830
831	function syncPreferenceInputs() {
832		var analytics = element(analyticsCheckboxId);
833		var targeting = element(targetingCheckboxId);
834
835		if (analytics) {
836			analytics.checked = consentState.decided && consentState.analytics;
837		}
838
839		if (targeting) {
840			targeting.checked = consentState.decided && consentState.targeting;
841		}
842	}
843
844	function focusableElements(container) {
845		var nodes;
846		var result = [];
847		var i;
848
849		if (!container || !container.querySelectorAll) {
850			return result;
851		}
852
853		nodes = container.querySelectorAll('a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])');
854		for (i = 0; i < nodes.length; i += 1) {
855			if (nodes[i].getAttribute('aria-hidden') !== 'true' && !nodes[i].hasAttribute('hidden')) {
856				result.push(nodes[i]);
857			}
858		}
859
860		return result;
861	}
862
863	function focusPreferences() {
864		var preferences = element(preferencesId);
865		var autofocus = preferences && preferences.querySelector('[data-consent-initial-focus]');
866		var focusable = focusableElements(preferences);
867		var target = autofocus || focusable[0];
868
869		if (target && target.focus) {
870			target.focus();
871		}
872	}
873
874	function openPreferences(trigger) {
875		var preferences = element(preferencesId);
876		var dialog;
877
878		if (!preferences || !document.body) {
879			pendingOpen = true;
880			return;
881		}
882
883		pendingOpen = false;
884		previousFocus = trigger && trigger.focus ? trigger : document.activeElement;
885		syncPreferenceInputs();
886		setHidden(element(bannerId), true);
887		setHidden(preferences, false);
888		dialog = preferences.querySelector('[role="dialog"]');
889		if (!dialog) {
890			preferences.setAttribute('role', 'dialog');
891			preferences.setAttribute('aria-modal', 'true');
892		}
893		preferencesOpen = true;
894		setModalBodyState(true);
895		window.setTimeout(focusPreferences, 0);
896	}
897
898	function closePreferences() {
899		var focusTarget = previousFocus;
900
901		setHidden(element(preferencesId), true);
902		preferencesOpen = false;
903		setModalBodyState(false);
904		if (!consentState.decided) {
905			setHidden(element(bannerId), false);
906		}
907
908		previousFocus = null;
909		if (focusTarget && document.documentElement.contains(focusTarget) && focusTarget.focus) {
910			focusTarget.focus();
911		}
912	}
913
914	function updateUiAfterDecision() {
915		var focusTarget = preferencesOpen ? previousFocus : null;
916
917		setHidden(element(bannerId), true);
918		setHidden(element(preferencesId), true);
919		preferencesOpen = false;
920		setModalBodyState(false);
921		previousFocus = null;
922		if (focusTarget && document.documentElement.contains(focusTarget) && focusTarget.focus) {
923			focusTarget.focus();
924		}
925	}
926
927	function dismissBanner() {
928		setHidden(element(bannerId), true);
929	}
930
931	function dispatchConsentActivatedEvent() {
932		if (!document.createEvent || !document.dispatchEvent) {
933			return;
934		}
935
936		// This wake-up signal deliberately carries no consent values, identity or
937		// other detail. Consumers must read only their own allowed category state.
938		var event = document.createEvent('Event');
939		event.initEvent(CONSENT_ACTIVATED_EVENT, false, false);
940		document.dispatchEvent(event);
941	}
942
943	function saveConsent(choice, action) {
944		var previous = consentState;
945		var phase = previous.decided ? 'change' : 'initial';
946		// A DB/vendor inventory error is a real runtime release gate. Never
947		// activate optional integrations from a stale decision while the current
948		// page cannot prove every active recipient.
949		if (!vendorInventoryComplete) {
950			choice = {analytics: false, targeting: false};
951		}
952		var next = {
953			v: COOKIE_SCHEMA_VERSION,
954			revision: policyRevision,
955			inventory: vendorInventoryFingerprint,
956			decisionId: createDecisionId(),
957			timestamp: new Date().getTime(),
958			analytics: choice && choice.analytics === true,
959			targeting: choice && choice.targeting === true,
960			decided: true
961		};
962		var revokedAnalytics = previous.decided && previous.analytics && !next.analytics;
963		var revokedTargeting = previous.decided && previous.targeting && !next.targeting;
964
965		consentState = next;
966		persistState(next);
967		action = normalizeDecisionAction(action, next);
968		if (vendorInventoryComplete) {
969			reportDecision(next, phase, action);
970		}
971		var retainedConsentEvent = pendingConsentEvent;
972		var currentConsentEvent = consentEventFor(
973			next,
974			previous.decided ? 'change' : 'choice',
975			previous.decided ? previous : null,
976			retainedConsentEvent
977		);
978		var googleConsentApplied = applyGoogleConsent(next);
979		if (googleConsentApplied) {
980			pendingConsentEvent = null;
981			emitPendingConsentEvent(currentConsentEvent);
982		} else {
983			pendingConsentEvent = currentConsentEvent;
984		}
985		expireCookieEverywhere('CookieScriptConsent');
986		clearUnsupportedOptionalData();
987		updateUiAfterDecision();
988		clearRevokedData(!next.analytics, !next.targeting);
989
990		if (revokedAnalytics || revokedTargeting) {
991			window.setTimeout(function () {
992				window.location.reload();
993			}, googleConsentApplied ? 100 : 0);
994			return publicState();
995		}
996
997		activateAllowedScripts(document);
998		dispatchConsentActivatedEvent();
999		return publicState();
1000	}
1001
1002	function readPreferencesAndSave() {
1003		var analytics = element(analyticsCheckboxId);
1004		var targeting = element(targetingCheckboxId);
1005
1006		return saveConsent({
1007			analytics: !!(analytics && analytics.checked),
1008			targeting: !!(targeting && targeting.checked)
1009		}, 'custom');
1010	}
1011
1012	function actionElement(target) {
1013		while (target && target !== document) {
1014			if (target.getAttribute && (target.getAttribute('data-consent-action') || target.hasAttribute('data-consent-open'))) {
1015				return target;
1016			}
1017			target = target.parentNode;
1018		}
1019
1020		return null;
1021	}
1022
1023	function handleClick(event) {
1024		var target = actionElement(event.target || event.srcElement);
1025		var action;
1026
1027		if (!target) {
1028			return;
1029		}
1030
1031		action = target.getAttribute('data-consent-action') || (target.hasAttribute('data-consent-open') ? 'open' : '');
1032		if (action === 'settings') {
1033			action = 'open';
1034		}
1035
1036		if (action !== 'accept-all' && action !== 'reject-all' && action !== 'open' && action !== 'save' && action !== 'close' && action !== 'dismiss') {
1037			return;
1038		}
1039
1040		if (event.preventDefault) {
1041			event.preventDefault();
1042		} else {
1043			event.returnValue = false;
1044		}
1045
1046		if (action === 'accept-all') {
1047			saveConsent({analytics: true, targeting: true}, 'accept_all');
1048		} else if (action === 'reject-all') {
1049			saveConsent({analytics: false, targeting: false}, 'reject_all');
1050		} else if (action === 'open') {
1051			openPreferences(target);
1052		} else if (action === 'save') {
1053			readPreferencesAndSave();
1054		} else if (action === 'dismiss') {
1055			dismissBanner();
1056		} else {
1057			closePreferences();
1058		}
1059	}
1060
1061	function handleSubmit(event) {
1062		var form = event.target || event.srcElement;
1063		var preferences = element(preferencesId);
1064
1065		if (!preferences || !form || (form !== preferences && !preferences.contains(form))) {
1066			return;
1067		}
1068
1069		if (event.preventDefault) {
1070			event.preventDefault();
1071		} else {
1072			event.returnValue = false;
1073		}
1074
1075		readPreferencesAndSave();
1076	}
1077
1078	function handleModalKeyboard(event) {
1079		var key = event.key || event.keyCode;
1080		var preferences;
1081		var focusable;
1082		var first;
1083		var last;
1084
1085		if (!preferencesOpen) {
1086			return;
1087		}
1088
1089		if (key === 'Escape' || key === 'Esc' || key === 27) {
1090			if (event.preventDefault) {
1091				event.preventDefault();
1092			}
1093			closePreferences();
1094			return;
1095		}
1096
1097		if (key !== 'Tab' && key !== 9) {
1098			return;
1099		}
1100
1101		preferences = element(preferencesId);
1102		focusable = focusableElements(preferences);
1103		if (focusable.length === 0) {
1104			if (event.preventDefault) {
1105				event.preventDefault();
1106			}
1107			return;
1108		}
1109
1110		first = focusable[0];
1111		last = focusable[focusable.length - 1];
1112		if (event.shiftKey && document.activeElement === first) {
1113			event.preventDefault();
1114			last.focus();
1115		} else if (!event.shiftKey && document.activeElement === last) {
1116			event.preventDefault();
1117			first.focus();
1118		}
1119	}
1120
1121	function initializeUi() {
1122		var banner = element(bannerId);
1123		var preferences = element(preferencesId);
1124		var dialog = preferences && preferences.querySelector('[role="dialog"]');
1125
1126		if (banner) {
1127			banner.setAttribute('role', 'dialog');
1128			banner.setAttribute('aria-modal', 'false');
1129		}
1130
1131		if (preferences && !dialog) {
1132			preferences.setAttribute('role', 'dialog');
1133			preferences.setAttribute('aria-modal', 'true');
1134		}
1135
1136		setHidden(preferences, true);
1137		setHidden(banner, consentState.decided);
1138		syncPreferenceInputs();
1139
1140		if (pendingOpen) {
1141			openPreferences();
1142		}
1143
1144		activateAllowedScripts(document);
1145	}
1146
1147	window.SwimaholicConsent = {
1148		has: hasConsent,
1149		state: publicState,
1150		googleState: function () {
1151			return googleConsentPayload(consentState);
1152		},
1153		bridgeState: publicBridgeState,
1154		addListener: addConsentListener,
1155		open: function () {
1156			openPreferences();
1157		},
1158		save: saveConsent
1159	};
1160
1161	document.addEventListener('click', handleClick, false);
1162	document.addEventListener('submit', handleSubmit, false);
1163	document.addEventListener('keydown', handleModalKeyboard, false);
1164	observeInertScripts();
1165
1166	if (document.readyState === 'loading') {
1167		document.addEventListener('DOMContentLoaded', initializeUi, false);
1168	} else {
1169		initializeUi();
1170	}
1171}(window, document));

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.