PageSourceSearch

https://colossal.com/wp-content/themes/colossal/consent/consent.js?ver=1.0

js colossal.com collected 2026-10-01 10:43:28 UTC 7,139 bytes, 232 lines download raw bytes

1(function () {
2	'use strict';
3
4	// Bump this after any policy change to force every visitor to be re-prompted.
5	var CONSENT_VERSION = 1;
6
7	var config = window.ColossalConsentConfig || {};
8	var COOKIE_NAME = config.cookieName || 'site_consent';
9	var COOKIE_DAYS = config.cookieDays || 365;
10	var CATEGORIES = config.categories || ['necessary', 'analytics', 'marketing', 'preferences'];
11
12	// Domains that must never load a <script src="..."> unless their category is granted.
13	// Safety net only — the primary mechanism is the consent:<category> comment blocks below.
14	var TRACKER_DOMAIN_CATEGORY = {
15		'googletagmanager.com': 'analytics',
16		'google-analytics.com': 'analytics',
17		'connect.facebook.net': 'marketing',
18		'facebook.net': 'marketing'
19	};
20
21	function setCookie(name, value, days) {
22		var expires = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toUTCString();
23		document.cookie = name + '=' + encodeURIComponent(value) + '; expires=' + expires + '; path=/; SameSite=Lax';
24	}
25
26	function getCookie(name) {
27		var escaped = name.replace(/([.$?*|{}()\[\]\\\/+^])/g, '\\$1');
28		var match = document.cookie.match(new RegExp('(?:^|; )' + escaped + '=([^;]*)'));
29		return match ? decodeURIComponent(match[1]) : null;
30	}
31
32	function readConsent() {
33		var raw = getCookie(COOKIE_NAME);
34		if (!raw) return null;
35		var parsed;
36		try {
37			parsed = JSON.parse(raw);
38		} catch (e) {
39			return null;
40		}
41		if (!parsed || parsed.version !== CONSENT_VERSION) return null;
42		return parsed;
43	}
44
45	function writeConsent(categories) {
46		var consent = {
47			necessary: true,
48			analytics: !!categories.analytics,
49			marketing: !!categories.marketing,
50			preferences: !!categories.preferences,
51			version: CONSENT_VERSION,
52			timestamp: Date.now()
53		};
54		setCookie(COOKIE_NAME, JSON.stringify(consent), COOKIE_DAYS);
55		return consent;
56	}
57
58	function rebuildScripts(container) {
59		var scripts = container.querySelectorAll('script');
60		scripts.forEach(function (oldScript) {
61			var newScript = document.createElement('script');
62			for (var i = 0; i < oldScript.attributes.length; i++) {
63				var attr = oldScript.attributes[i];
64				newScript.setAttribute(attr.name, attr.value);
65			}
66			newScript.textContent = oldScript.textContent;
67			newScript.setAttribute('data-consent-injected', 'true');
68			oldScript.parentNode.replaceChild(newScript, oldScript);
69		});
70	}
71
72	function findConsentComments() {
73		var walker = document.createTreeWalker(document.documentElement, NodeFilter.SHOW_COMMENT, null);
74		var comments = [];
75		var node;
76		while ((node = walker.nextNode())) {
77			comments.push(node);
78		}
79		return comments;
80	}
81
82	function activateConsentedTrackers(consent) {
83		findConsentComments().forEach(function (comment) {
84			var match = /^\s*consent:([a-z0-9_-]+)/i.exec(comment.nodeValue);
85			if (!match) return;
86
87			var category = match[1].toLowerCase();
88			if (CATEGORIES.indexOf(category) === -1 || !consent[category]) return;
89
90			var html = comment.nodeValue.slice(match[0].length);
91			var container = document.createElement('div');
92			container.innerHTML = html;
93			rebuildScripts(container);
94
95			var parent = comment.parentNode;
96			if (!parent) return;
97
98			Array.prototype.slice.call(container.childNodes).forEach(function (node) {
99				parent.insertBefore(node, comment);
100			});
101			parent.removeChild(comment);
102		});
103	}
104
105	function watchForRogueTrackers(consent) {
106		if (!('MutationObserver' in window)) return;
107
108		var blockedDomains = Object.keys(TRACKER_DOMAIN_CATEGORY);
109
110		var checkNode = function (node) {
111			if (!node || node.nodeType !== 1 || node.tagName !== 'SCRIPT') return;
112			if (node.getAttribute('data-consent-injected') === 'true') return;
113
114			var src = node.getAttribute('src') || '';
115			for (var i = 0; i < blockedDomains.length; i++) {
116				var domain = blockedDomains[i];
117				if (src.indexOf(domain) === -1) continue;
118
119				var category = TRACKER_DOMAIN_CATEGORY[domain];
120				if (!consent[category]) {
121					node.parentNode && node.parentNode.removeChild(node);
122				}
123				return;
124			}
125		};
126
127		var observer = new MutationObserver(function (mutations) {
128			mutations.forEach(function (mutation) {
129				mutation.addedNodes.forEach(checkNode);
130			});
131		});
132
133		observer.observe(document.head, { childList: true, subtree: true });
134		observer.observe(document.body, { childList: true, subtree: true });
135	}
136
137	function initBanner(onAction) {
138		var banner = document.getElementById('consent-banner');
139		if (!banner) return null;
140
141		var panel = document.getElementById('consent-panel');
142		var customizeBtn = banner.querySelector('[data-consent-action="customize"]');
143
144		function show() {
145			banner.hidden = false;
146			requestAnimationFrame(function () {
147				requestAnimationFrame(function () {
148					banner.classList.add('is-visible');
149				});
150			});
151		}
152
153		function hide() {
154			var reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
155			banner.classList.remove('is-visible');
156
157			if (reduceMotion) {
158				banner.hidden = true;
159				return;
160			}
161
162			var onEnd = function (event) {
163				if (event.target !== banner || event.propertyName !== 'transform') return;
164				banner.removeEventListener('transitionend', onEnd);
165				banner.hidden = true;
166			};
167			banner.addEventListener('transitionend', onEnd);
168		}
169
170		function categoriesFromToggles() {
171			var categories = {};
172			banner.querySelectorAll('[data-consent-category]').forEach(function (input) {
173				categories[input.getAttribute('data-consent-category')] = input.checked;
174			});
175			return categories;
176		}
177
178		customizeBtn && customizeBtn.addEventListener('click', function () {
179			var expanded = customizeBtn.getAttribute('aria-expanded') === 'true';
180			customizeBtn.setAttribute('aria-expanded', String(!expanded));
181			panel.hidden = expanded;
182		});
183
184		banner.querySelector('[data-consent-action="accept-all"]').addEventListener('click', function () {
185			onAction({ analytics: true, marketing: true, preferences: true });
186			hide();
187		});
188
189		banner.querySelector('[data-consent-action="reject"]').addEventListener('click', function () {
190			onAction({ analytics: false, marketing: false, preferences: false });
191			hide();
192		});
193
194		banner.querySelector('[data-consent-action="save"]').addEventListener('click', function () {
195			onAction(categoriesFromToggles());
196			hide();
197		});
198
199		return { show: show, hide: hide };
200	}
201
202	function init() {
203		var existing = readConsent();
204
205		if (existing) {
206			activateConsentedTrackers(existing);
207			watchForRogueTrackers(existing);
208			return;
209		}
210
211		// Mutated in place (never reassigned) so the MutationObserver closure
212		// below always sees the latest grants after a banner action.
213		var consentState = { necessary: true, analytics: false, marketing: false, preferences: false };
214		watchForRogueTrackers(consentState);
215
216		var banner = initBanner(function (categories) {
217			var updated = writeConsent(categories);
218			Object.keys(updated).forEach(function (key) {
219				consentState[key] = updated[key];
220			});
221			activateConsentedTrackers(consentState);
222		});
223
224		if (banner) banner.show();
225	}
226
227	if (document.readyState === 'loading') {
228		document.addEventListener('DOMContentLoaded', init);
229	} else {
230		init();
231	}
232})();

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.