@ecomconsult/consentkit 0.3.4 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +260 -16
- package/npm/core.cjs +1 -1
- package/npm/core.mjs +4 -1
- package/npm/index.cjs +5 -2
- package/npm/index.d.ts +68 -0
- package/npm/index.mjs +14 -2
- package/npm/internal-stub.mjs +7 -1
- package/package.json +1 -1
- package/src/ck-core.js +488 -29
- package/src/ck-debug-loader.js +134 -0
- package/src/ck-debug.js +862 -0
- package/src/ck-saas.js +36 -1
- package/src/ck-ui-branding.js +511 -0
- package/src/ck-ui.js +43 -416
package/src/ck-debug.js
ADDED
|
@@ -0,0 +1,862 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* ConsentKit debug panel (opt-in) — shows what the banner actually did on this
|
|
3
|
+
* page. Off by default: without activation this file creates no DOM, installs
|
|
4
|
+
* no observers and touches nothing.
|
|
5
|
+
*
|
|
6
|
+
* Activation (this browser only, nothing is sent anywhere):
|
|
7
|
+
* ?ck_debug=1 or #ck_debug — turns it on and remembers it (localStorage)
|
|
8
|
+
* ?ck_debug=0 — turns it off and forgets it
|
|
9
|
+
* localStorage.ck_debug = '1' — same as the query parameter
|
|
10
|
+
*
|
|
11
|
+
* Load order: ck-core.js -> ck-locales.js -> ck-ui.js -> [ck-saas.js] -> ck-debug.js
|
|
12
|
+
*
|
|
13
|
+
* Copyright (c) 2026 E-COM CONSULT PLUS. MIT License — see LICENSE.
|
|
14
|
+
*/
|
|
15
|
+
(function (global) {
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
if (!global || typeof global !== 'object') { return; }
|
|
19
|
+
|
|
20
|
+
var LS_KEY = 'ck_debug';
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Activation (pure, testable: no DOM, no storage)
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Returns { active, persist } where persist is 'on' | 'off' | null:
|
|
26
|
+
// the query/hash form is sticky so the panel survives navigation, the bare
|
|
27
|
+
// localStorage form changes nothing.
|
|
28
|
+
function isOffValue(v) {
|
|
29
|
+
return v === '0' || v === 'false' || v === 'no' || v === '';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseActivation(search, hash, stored) {
|
|
33
|
+
var on = null;
|
|
34
|
+
|
|
35
|
+
var q = String(search || '');
|
|
36
|
+
if (q.charAt(0) === '?') { q = q.slice(1); }
|
|
37
|
+
var pairs = q ? q.split('&') : [];
|
|
38
|
+
for (var i = 0; i < pairs.length; i++) {
|
|
39
|
+
var eq = pairs[i].indexOf('=');
|
|
40
|
+
var k = eq === -1 ? pairs[i] : pairs[i].slice(0, eq);
|
|
41
|
+
if (k !== LS_KEY) { continue; }
|
|
42
|
+
// ?ck_debug (no "="), ?ck_debug=1, =true, =yes -> on
|
|
43
|
+
// ?ck_debug=0, =false, =no, =(empty) -> off. The empty value is treated
|
|
44
|
+
// as off deliberately: a form or a link builder that drops the value is
|
|
45
|
+
// far more likely to mean "not set" than "switch the panel on".
|
|
46
|
+
on = eq === -1 ? true : !isOffValue(pairs[i].slice(eq + 1));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (on === null) {
|
|
50
|
+
var h = String(hash || '');
|
|
51
|
+
if (h.charAt(0) === '#') { h = h.slice(1); }
|
|
52
|
+
// #ck_debug or #ck_debug=1 (a plain fragment id, not a query)
|
|
53
|
+
if (h === LS_KEY) { on = true; }
|
|
54
|
+
else if (h.indexOf(LS_KEY + '=') === 0) { on = !isOffValue(h.slice(LS_KEY.length + 1)); }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (on === null) {
|
|
58
|
+
return { active: String(stored || '') === '1', persist: null };
|
|
59
|
+
}
|
|
60
|
+
return { active: on, persist: on ? 'on' : 'off' };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Report (pure, testable: takes plain data, returns plain data)
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// No PII by construction: cookie names only (never values), host + path only
|
|
67
|
+
// (never query strings — tracker URLs carry ids there).
|
|
68
|
+
function stripUrl(url) {
|
|
69
|
+
var s = String(url || '');
|
|
70
|
+
var host = '';
|
|
71
|
+
var path = '';
|
|
72
|
+
var m = /^(?:[a-z]+:)?\/\/([^/?#]+)([^?#]*)/i.exec(s);
|
|
73
|
+
if (m) {
|
|
74
|
+
host = m[1].toLowerCase().replace(/:\d+$/, '');
|
|
75
|
+
path = m[2] || '';
|
|
76
|
+
} else {
|
|
77
|
+
path = s.split('?')[0].split('#')[0];
|
|
78
|
+
}
|
|
79
|
+
return { host: host, path: path };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// entries: PerformanceResourceTiming-like [{ name, startTime, initiatorType }]
|
|
83
|
+
// consentAtMs: performance-clock ms of the decision, or null when undecided
|
|
84
|
+
// classify: url -> category | null (ConsentKit._categoryForUrl)
|
|
85
|
+
function buildRequests(entries, consentAtMs, classify) {
|
|
86
|
+
var out = [];
|
|
87
|
+
var list = entries || [];
|
|
88
|
+
var seen = {};
|
|
89
|
+
for (var i = 0; i < list.length; i++) {
|
|
90
|
+
var e = list[i];
|
|
91
|
+
if (!e || !e.name) { continue; }
|
|
92
|
+
var cat = null;
|
|
93
|
+
try { cat = classify ? classify(e.name) : null; } catch (err) { cat = null; }
|
|
94
|
+
if (!cat) { continue; } // only known trackers
|
|
95
|
+
var p = stripUrl(e.name);
|
|
96
|
+
var t = Number(e.startTime) || 0;
|
|
97
|
+
var when = consentAtMs === null || consentAtMs === undefined
|
|
98
|
+
? 'before' : (t >= consentAtMs ? 'after' : 'before');
|
|
99
|
+
var key = p.host + '|' + p.path + '|' + when;
|
|
100
|
+
if (seen[key]) { seen[key].count++; continue; }
|
|
101
|
+
var rec = {
|
|
102
|
+
host: p.host, path: p.path, category: cat, when: when,
|
|
103
|
+
at: Math.round(t), kind: String((e && e.initiatorType) || ''), count: 1
|
|
104
|
+
};
|
|
105
|
+
seen[key] = rec;
|
|
106
|
+
out.push(rec);
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildReport(input) {
|
|
112
|
+
var d = input || {};
|
|
113
|
+
var st = d.state || {};
|
|
114
|
+
var cats = st.categories || {};
|
|
115
|
+
return {
|
|
116
|
+
generatedAt: d.now || null,
|
|
117
|
+
client: {
|
|
118
|
+
version: d.version || null,
|
|
119
|
+
source: d.siteId ? 'saas' : 'inline',
|
|
120
|
+
siteId: d.siteId || null,
|
|
121
|
+
policyVersion: st.policyVersion || (d.config && d.config.policyVersion) || null,
|
|
122
|
+
etag: d.etag || null
|
|
123
|
+
},
|
|
124
|
+
consent: {
|
|
125
|
+
status: !st.decided ? 'none'
|
|
126
|
+
: (cats.functional && cats.analytics && cats.marketing) ? 'accepted'
|
|
127
|
+
: (!cats.functional && !cats.analytics && !cats.marketing) ? 'rejected'
|
|
128
|
+
: 'partial',
|
|
129
|
+
categories: {
|
|
130
|
+
necessary: true,
|
|
131
|
+
functional: !!cats.functional,
|
|
132
|
+
analytics: !!cats.analytics,
|
|
133
|
+
marketing: !!cats.marketing
|
|
134
|
+
},
|
|
135
|
+
decidedAt: st.ts || null,
|
|
136
|
+
method: st.method || null,
|
|
137
|
+
ttlDays: d.ttlDays == null ? null : Number(d.ttlDays)
|
|
138
|
+
},
|
|
139
|
+
blocked: (d.blocked || []).map(function (b) {
|
|
140
|
+
// `strict` marks an entry the engine held back only because strict mode
|
|
141
|
+
// is on and the host is an unknown third party — not because the
|
|
142
|
+
// tracker database recognised it.
|
|
143
|
+
return {
|
|
144
|
+
host: b.host, path: b.path, kind: b.kind,
|
|
145
|
+
category: b.category, origin: b.origin, strict: b.strict === true,
|
|
146
|
+
// false when the visitor granted the category but the element never
|
|
147
|
+
// loaded — usually a script that was created and never appended.
|
|
148
|
+
revived: b.revived !== false
|
|
149
|
+
};
|
|
150
|
+
}),
|
|
151
|
+
requests: buildRequests(d.entries, d.consentAtMs, d.classify),
|
|
152
|
+
consentMode: (d.consentMode || []).slice(),
|
|
153
|
+
// Names only — a consent debug panel must never leak cookie contents.
|
|
154
|
+
cookieNames: (d.cookieNames || []).slice(),
|
|
155
|
+
note: NOTE_EN
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
var NOTE_RU = 'Запросы, ушедшие до загрузки ConsentKit (обычный <script src> ' +
|
|
160
|
+
'в разметке), видны в этом списке, но заблокировать их клиент не может — ' +
|
|
161
|
+
'такие теги размечают вручную.';
|
|
162
|
+
var NOTE_EN = 'Requests that left before ConsentKit loaded (a plain <script src> ' +
|
|
163
|
+
'written into the HTML) show up here but cannot be blocked — mark such tags up manually.';
|
|
164
|
+
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
// Panel language (pure; the JSON report stays language-neutral either way)
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
// The panel is read by whoever is debugging the site, so it follows the same
|
|
169
|
+
// language the banner resolved for this visitor rather than a build flag.
|
|
170
|
+
// Only ru and en exist: this is an internal diagnostic surface, and a
|
|
171
|
+
// half-translated one is worse than an English one.
|
|
172
|
+
var STRINGS = {
|
|
173
|
+
ru: {
|
|
174
|
+
regionLabel: 'ConsentKit — режим отладки',
|
|
175
|
+
collapse: 'Свернуть',
|
|
176
|
+
expand: 'Развернуть',
|
|
177
|
+
closeLabel: 'Закрыть и выключить режим отладки',
|
|
178
|
+
secClient: 'Клиент',
|
|
179
|
+
version: 'версия',
|
|
180
|
+
source: 'источник',
|
|
181
|
+
srcSaas: 'SaaS',
|
|
182
|
+
srcInline: 'инлайн',
|
|
183
|
+
secConsent: 'Согласие',
|
|
184
|
+
decidedAt: 'решение',
|
|
185
|
+
method: 'способ',
|
|
186
|
+
ttl: 'срок cookie',
|
|
187
|
+
days: ' дн.',
|
|
188
|
+
status: { none: 'нет решения', accepted: 'принято', rejected: 'отклонено', partial: 'частично' },
|
|
189
|
+
secBlocked: 'Заблокировано до согласия',
|
|
190
|
+
noBlocked: 'ничего не перехвачено',
|
|
191
|
+
markup: ' (разметка)',
|
|
192
|
+
strict: 'strict',
|
|
193
|
+
notRevived: ' — не ожил после согласия',
|
|
194
|
+
secRequests: 'Запросы к трекерам',
|
|
195
|
+
noRequests: 'запросов к известным трекерам не было',
|
|
196
|
+
after: 'после согласия',
|
|
197
|
+
before: 'до согласия',
|
|
198
|
+
ms: ' мс',
|
|
199
|
+
note: NOTE_RU,
|
|
200
|
+
secConsentMode: 'Consent Mode / dataLayer',
|
|
201
|
+
noEvents: 'событий не было',
|
|
202
|
+
secActions: 'Действия',
|
|
203
|
+
reset: 'Сбросить согласие',
|
|
204
|
+
showPrefs: 'Показать настройки',
|
|
205
|
+
copy: 'Скопировать отчёт',
|
|
206
|
+
copied: 'Скопировано',
|
|
207
|
+
copyFailed: 'Не вышло',
|
|
208
|
+
footer: 'Панель видна только в этом браузере. Выключить: добавьте ?ck_debug=0 к адресу.'
|
|
209
|
+
},
|
|
210
|
+
en: {
|
|
211
|
+
regionLabel: 'ConsentKit — debug mode',
|
|
212
|
+
collapse: 'Collapse',
|
|
213
|
+
expand: 'Expand',
|
|
214
|
+
closeLabel: 'Close and turn debug mode off',
|
|
215
|
+
secClient: 'Client',
|
|
216
|
+
version: 'version',
|
|
217
|
+
source: 'source',
|
|
218
|
+
srcSaas: 'SaaS',
|
|
219
|
+
srcInline: 'inline',
|
|
220
|
+
secConsent: 'Consent',
|
|
221
|
+
decidedAt: 'decided',
|
|
222
|
+
method: 'method',
|
|
223
|
+
ttl: 'cookie lifetime',
|
|
224
|
+
days: ' days',
|
|
225
|
+
status: { none: 'no decision', accepted: 'accepted', rejected: 'rejected', partial: 'partial' },
|
|
226
|
+
secBlocked: 'Blocked until consent',
|
|
227
|
+
noBlocked: 'nothing intercepted',
|
|
228
|
+
markup: ' (markup)',
|
|
229
|
+
strict: 'strict',
|
|
230
|
+
notRevived: ' — did not come back after consent',
|
|
231
|
+
secRequests: 'Tracker requests',
|
|
232
|
+
noRequests: 'no requests to known trackers',
|
|
233
|
+
after: 'after consent',
|
|
234
|
+
before: 'before consent',
|
|
235
|
+
ms: ' ms',
|
|
236
|
+
note: NOTE_EN,
|
|
237
|
+
secConsentMode: 'Consent Mode / dataLayer',
|
|
238
|
+
noEvents: 'no events',
|
|
239
|
+
secActions: 'Actions',
|
|
240
|
+
reset: 'Reset consent',
|
|
241
|
+
showPrefs: 'Show preferences',
|
|
242
|
+
copy: 'Copy report',
|
|
243
|
+
copied: 'Copied',
|
|
244
|
+
copyFailed: 'Failed',
|
|
245
|
+
footer: 'This panel is visible in this browser only. To turn it off, add ?ck_debug=0 to the URL.'
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// banner language (ConsentKit config) -> navigator.language -> en.
|
|
250
|
+
// `cfgLang` is ConsentKit.config.language, which may be 'auto'.
|
|
251
|
+
function pickLang(cfgLang, navLang) {
|
|
252
|
+
var raw = String(cfgLang || '').toLowerCase();
|
|
253
|
+
if (!raw || raw === 'auto') { raw = String(navLang || '').toLowerCase(); }
|
|
254
|
+
return raw.slice(0, 2) === 'ru' ? 'ru' : 'en';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Testable surface. Published before the activation check so the inactive
|
|
258
|
+
// path is testable too; it is not a public API.
|
|
259
|
+
var API = {
|
|
260
|
+
parseActivation: parseActivation,
|
|
261
|
+
buildReport: buildReport,
|
|
262
|
+
buildRequests: buildRequests,
|
|
263
|
+
stripUrl: stripUrl,
|
|
264
|
+
pickLang: pickLang,
|
|
265
|
+
strings: STRINGS,
|
|
266
|
+
active: false
|
|
267
|
+
};
|
|
268
|
+
try {
|
|
269
|
+
if (global.ConsentKit) { global.ConsentKit._debug = API; }
|
|
270
|
+
global.__ckDebug = API;
|
|
271
|
+
} catch (e) { /* noop */ }
|
|
272
|
+
|
|
273
|
+
// ---------------------------------------------------------------------------
|
|
274
|
+
// Everything below runs only when activated.
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
var doc = global.document;
|
|
277
|
+
var loc = global.location;
|
|
278
|
+
|
|
279
|
+
function lsGet(k) {
|
|
280
|
+
try { return global.localStorage ? global.localStorage.getItem(k) : null; } catch (e) { return null; }
|
|
281
|
+
}
|
|
282
|
+
function lsSet(k, v) {
|
|
283
|
+
try { if (global.localStorage) { global.localStorage.setItem(k, v); } } catch (e) { /* noop */ }
|
|
284
|
+
}
|
|
285
|
+
function lsDel(k) {
|
|
286
|
+
try { if (global.localStorage) { global.localStorage.removeItem(k); } } catch (e) { /* noop */ }
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
var act = parseActivation(loc && loc.search, loc && loc.hash, lsGet(LS_KEY));
|
|
290
|
+
if (act.persist === 'on') { lsSet(LS_KEY, '1'); }
|
|
291
|
+
if (act.persist === 'off') { lsDel(LS_KEY); }
|
|
292
|
+
if (!act.active) { return; }
|
|
293
|
+
if (!doc || typeof doc.createElement !== 'function') { return; }
|
|
294
|
+
API.active = true;
|
|
295
|
+
|
|
296
|
+
var CK = global.ConsentKit;
|
|
297
|
+
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
// Observation (installed only when active)
|
|
300
|
+
// ---------------------------------------------------------------------------
|
|
301
|
+
var resources = [];
|
|
302
|
+
var consentAtMs = null; // performance clock, set when consent is given
|
|
303
|
+
var consentMode = []; // last ck_* dataLayer events and gtag consent calls
|
|
304
|
+
var CM_MAX = 20;
|
|
305
|
+
|
|
306
|
+
function nowMs() {
|
|
307
|
+
try {
|
|
308
|
+
if (global.performance && typeof global.performance.now === 'function') {
|
|
309
|
+
return global.performance.now();
|
|
310
|
+
}
|
|
311
|
+
} catch (e) { /* noop */ }
|
|
312
|
+
return 0;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// A decision restored from a previous page load happened before this page
|
|
316
|
+
// started, so every request on this load counts as "after".
|
|
317
|
+
function seedConsentTime() {
|
|
318
|
+
try {
|
|
319
|
+
var st = CK && CK.getState ? CK.getState() : null;
|
|
320
|
+
if (st && st.decided && consentAtMs === null) { consentAtMs = 0; }
|
|
321
|
+
} catch (e) { /* noop */ }
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Stamp the moment the visitor decides, BEFORE the core acts on it.
|
|
325
|
+
//
|
|
326
|
+
// This cannot wait for ck:change: accept() revives the blocked scripts
|
|
327
|
+
// synchronously and dispatches ck:change afterwards, so by the time the event
|
|
328
|
+
// fires those scripts have already issued their requests. Stamping on the
|
|
329
|
+
// event would then place them *before* the cutoff and the panel would report
|
|
330
|
+
// "до согласия" for the very requests the consent just released — exactly
|
|
331
|
+
// backwards, and on the one line an owner reads most carefully.
|
|
332
|
+
//
|
|
333
|
+
// Wrapping is observe-only: the original method is called with the original
|
|
334
|
+
// arguments and its return value passed straight back.
|
|
335
|
+
function stampOnDecision() {
|
|
336
|
+
try {
|
|
337
|
+
if (!CK) { return; }
|
|
338
|
+
['accept', 'rejectAll'].forEach(function (name) {
|
|
339
|
+
var orig = CK[name];
|
|
340
|
+
if (typeof orig !== 'function' || orig.__ckDebugWrapped) { return; }
|
|
341
|
+
var wrapped = function () {
|
|
342
|
+
try { consentAtMs = nowMs(); } catch (e) { /* noop */ }
|
|
343
|
+
return orig.apply(CK, arguments);
|
|
344
|
+
};
|
|
345
|
+
wrapped.__ckDebugWrapped = true;
|
|
346
|
+
CK[name] = wrapped;
|
|
347
|
+
});
|
|
348
|
+
// withdraw() puts the page back to "no decision yet".
|
|
349
|
+
var w = CK.withdraw;
|
|
350
|
+
if (typeof w === 'function' && !w.__ckDebugWrapped) {
|
|
351
|
+
var wrappedW = function () {
|
|
352
|
+
try { consentAtMs = null; } catch (e) { /* noop */ }
|
|
353
|
+
return w.apply(CK, arguments);
|
|
354
|
+
};
|
|
355
|
+
wrappedW.__ckDebugWrapped = true;
|
|
356
|
+
CK.withdraw = wrappedW;
|
|
357
|
+
}
|
|
358
|
+
} catch (e) { /* noop */ }
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function collectResources() {
|
|
362
|
+
try {
|
|
363
|
+
var perf = global.performance;
|
|
364
|
+
if (!perf) { return; }
|
|
365
|
+
if (typeof perf.getEntriesByType === 'function') {
|
|
366
|
+
var buffered = perf.getEntriesByType('resource') || [];
|
|
367
|
+
for (var i = 0; i < buffered.length; i++) { resources.push(buffered[i]); }
|
|
368
|
+
}
|
|
369
|
+
if (typeof global.PerformanceObserver === 'function') {
|
|
370
|
+
var po = new global.PerformanceObserver(function (list) {
|
|
371
|
+
try {
|
|
372
|
+
var got = list.getEntries() || [];
|
|
373
|
+
for (var j = 0; j < got.length; j++) { resources.push(got[j]); }
|
|
374
|
+
schedule();
|
|
375
|
+
} catch (e2) { /* noop */ }
|
|
376
|
+
});
|
|
377
|
+
// buffered:true re-delivers what happened before we attached; harmless
|
|
378
|
+
// duplicates are deduped when the list is rendered.
|
|
379
|
+
try { po.observe({ type: 'resource', buffered: true }); }
|
|
380
|
+
catch (e3) { try { po.observe({ entryTypes: ['resource'] }); } catch (e4) { /* noop */ } }
|
|
381
|
+
}
|
|
382
|
+
} catch (e) { /* noop */ }
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Observe only: the existing dataLayer.push is called first and its return
|
|
386
|
+
// value passed through, so a GTM/other wrapper keeps working.
|
|
387
|
+
function watchDataLayer() {
|
|
388
|
+
try {
|
|
389
|
+
var dl = global.dataLayer;
|
|
390
|
+
if (!dl) { global.dataLayer = dl = []; }
|
|
391
|
+
if (typeof dl.push !== 'function' || dl.__ckDebugWatched) { return; }
|
|
392
|
+
// Whatever is already in the queue counts too.
|
|
393
|
+
for (var i = 0; i < dl.length; i++) { noteDataLayer(dl[i]); }
|
|
394
|
+
var prev = dl.push;
|
|
395
|
+
dl.push = function () {
|
|
396
|
+
try {
|
|
397
|
+
for (var j = 0; j < arguments.length; j++) { noteDataLayer(arguments[j]); }
|
|
398
|
+
} catch (e) { /* never break the host page */ }
|
|
399
|
+
return prev.apply(this, arguments);
|
|
400
|
+
};
|
|
401
|
+
dl.__ckDebugWatched = true;
|
|
402
|
+
} catch (e) { /* noop */ }
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function noteDataLayer(arg) {
|
|
406
|
+
try {
|
|
407
|
+
var rec = null;
|
|
408
|
+
// gtag() pushes an arguments object: ['consent', 'default'|'update', {...}]
|
|
409
|
+
if (arg && typeof arg === 'object' && typeof arg.length === 'number' && arg[0] === 'consent') {
|
|
410
|
+
var signals = {};
|
|
411
|
+
var payload = arg[2];
|
|
412
|
+
if (payload && typeof payload === 'object') {
|
|
413
|
+
for (var k in payload) {
|
|
414
|
+
if (Object.prototype.hasOwnProperty.call(payload, k)) { signals[k] = payload[k]; }
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
rec = { type: 'gtag consent ' + String(arg[1] || ''), signals: signals, at: Math.round(nowMs()) };
|
|
418
|
+
} else if (arg && typeof arg === 'object' && typeof arg.event === 'string' &&
|
|
419
|
+
arg.event.indexOf('ck_') === 0) {
|
|
420
|
+
rec = { type: arg.event, signals: null, at: Math.round(nowMs()) };
|
|
421
|
+
}
|
|
422
|
+
if (!rec) { return; }
|
|
423
|
+
consentMode.push(rec);
|
|
424
|
+
if (consentMode.length > CM_MAX) { consentMode.shift(); }
|
|
425
|
+
schedule();
|
|
426
|
+
} catch (e) { /* noop */ }
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function cookieNames() {
|
|
430
|
+
var names = [];
|
|
431
|
+
try {
|
|
432
|
+
var raw = typeof doc.cookie === 'string' ? doc.cookie : '';
|
|
433
|
+
var parts = raw.split(';');
|
|
434
|
+
for (var i = 0; i < parts.length; i++) {
|
|
435
|
+
var t = parts[i].trim();
|
|
436
|
+
if (!t) { continue; }
|
|
437
|
+
var eq = t.indexOf('=');
|
|
438
|
+
names.push(eq === -1 ? t : t.slice(0, eq)); // name only, never the value
|
|
439
|
+
}
|
|
440
|
+
} catch (e) { /* noop */ }
|
|
441
|
+
return names;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function saasInfo() {
|
|
445
|
+
var out = { siteId: null, etag: null };
|
|
446
|
+
try {
|
|
447
|
+
if (CK && CK._saas) {
|
|
448
|
+
out.siteId = CK._saas.siteId || null;
|
|
449
|
+
var cached = null;
|
|
450
|
+
try {
|
|
451
|
+
var raw = lsGet('ck_cfg_' + out.siteId);
|
|
452
|
+
cached = raw ? JSON.parse(raw) : null;
|
|
453
|
+
} catch (e2) { cached = null; }
|
|
454
|
+
out.etag = cached && cached.etag ? cached.etag : null;
|
|
455
|
+
}
|
|
456
|
+
} catch (e) { /* noop */ }
|
|
457
|
+
return out;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function reportInput() {
|
|
461
|
+
var s = saasInfo();
|
|
462
|
+
var cfg = (CK && CK.config) || {};
|
|
463
|
+
return {
|
|
464
|
+
now: new Date().toISOString(),
|
|
465
|
+
version: CK ? CK.version : null,
|
|
466
|
+
state: CK && CK.getState ? CK.getState() : {},
|
|
467
|
+
config: cfg,
|
|
468
|
+
siteId: s.siteId,
|
|
469
|
+
etag: s.etag,
|
|
470
|
+
ttlDays: cfg.consentTtlDays == null ? null : cfg.consentTtlDays,
|
|
471
|
+
blocked: (CK && typeof CK._blocked === 'function') ? CK._blocked() : [],
|
|
472
|
+
entries: resources,
|
|
473
|
+
consentAtMs: consentAtMs,
|
|
474
|
+
classify: CK ? CK._categoryForUrl : null,
|
|
475
|
+
consentMode: consentMode,
|
|
476
|
+
cookieNames: cookieNames()
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// ---------------------------------------------------------------------------
|
|
481
|
+
// Panel (own Shadow DOM host, own styles)
|
|
482
|
+
// ---------------------------------------------------------------------------
|
|
483
|
+
var CSS = [
|
|
484
|
+
':host{all:initial;position:fixed;right:12px;bottom:12px;z-index:2147483646;',
|
|
485
|
+
'font:12px/1.45 ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace}',
|
|
486
|
+
'*{box-sizing:border-box}',
|
|
487
|
+
'.w{width:min(360px,calc(100vw - 24px));background:#12151c;color:#dfe3ea;',
|
|
488
|
+
'border:1px solid #2b3040;border-radius:8px;box-shadow:0 6px 24px rgba(0,0,0,.4);overflow:hidden}',
|
|
489
|
+
'.hd{display:flex;align-items:center;gap:8px;padding:7px 9px;background:#1a1f2a;',
|
|
490
|
+
'border-bottom:1px solid #2b3040}',
|
|
491
|
+
'.hd b{font-weight:600;color:#fff;font-size:12px}',
|
|
492
|
+
'.hd .sp{flex:1}',
|
|
493
|
+
'button{font:inherit;color:#dfe3ea;background:#232936;border:1px solid #39415400;',
|
|
494
|
+
'border-color:#394154;border-radius:5px;padding:3px 8px;cursor:pointer}',
|
|
495
|
+
'button:hover{background:#2c3444}',
|
|
496
|
+
'button:focus-visible{outline:2px solid #7aa2ff;outline-offset:1px}',
|
|
497
|
+
'.bd{max-height:min(60vh,460px);overflow:auto;padding:2px 9px 9px}',
|
|
498
|
+
'.bd[hidden]{display:none}',
|
|
499
|
+
'section{border-top:1px solid #232936;padding:7px 0}',
|
|
500
|
+
'section:first-child{border-top:0}',
|
|
501
|
+
'h2{margin:0 0 4px;font-size:11px;font-weight:600;color:#8d96ab;text-transform:uppercase;',
|
|
502
|
+
'letter-spacing:.04em}',
|
|
503
|
+
'dl{margin:0;display:grid;grid-template-columns:auto 1fr;gap:1px 8px}',
|
|
504
|
+
'dt{color:#8d96ab}',
|
|
505
|
+
'dd{margin:0;overflow-wrap:anywhere}',
|
|
506
|
+
'ul{margin:0;padding:0;list-style:none}',
|
|
507
|
+
'li{padding:2px 0;border-top:1px dotted #262c3a;overflow-wrap:anywhere}',
|
|
508
|
+
'li:first-child{border-top:0}',
|
|
509
|
+
'.t{display:inline-block;padding:0 5px;border-radius:3px;font-size:10px;',
|
|
510
|
+
'background:#2a3142;color:#a9b3c9;margin-right:4px}',
|
|
511
|
+
'.t.on{background:#173a24;color:#7ee2a0}',
|
|
512
|
+
'.t.off{background:#3a1a1d;color:#ff9aa2}',
|
|
513
|
+
'.mut{color:#8d96ab}',
|
|
514
|
+
'.note{margin:6px 0 0;color:#8d96ab;font-size:11px;line-height:1.4}',
|
|
515
|
+
'.row{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px}',
|
|
516
|
+
'@media (prefers-reduced-motion:no-preference){button{transition:background-color .12s ease}}'
|
|
517
|
+
].join('');
|
|
518
|
+
|
|
519
|
+
var host = null, root = null, body = null, toggleBtn = null, collapsed = false;
|
|
520
|
+
var frame = 0;
|
|
521
|
+
|
|
522
|
+
function el(tag, attrs, text) {
|
|
523
|
+
var n = doc.createElement(tag);
|
|
524
|
+
if (attrs) {
|
|
525
|
+
for (var k in attrs) {
|
|
526
|
+
if (Object.prototype.hasOwnProperty.call(attrs, k)) { n.setAttribute(k, attrs[k]); }
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (text != null) { n.textContent = String(text); }
|
|
530
|
+
return n;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function section(title) {
|
|
534
|
+
var s = doc.createElement('section');
|
|
535
|
+
s.appendChild(el('h2', null, title));
|
|
536
|
+
return s;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function defs(pairs) {
|
|
540
|
+
var dl = doc.createElement('dl');
|
|
541
|
+
for (var i = 0; i < pairs.length; i++) {
|
|
542
|
+
dl.appendChild(el('dt', null, pairs[i][0]));
|
|
543
|
+
dl.appendChild(el('dd', null, pairs[i][1] == null || pairs[i][1] === '' ? '—' : pairs[i][1]));
|
|
544
|
+
}
|
|
545
|
+
return dl;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function tag(text, cls) { return el('span', { class: 't' + (cls ? ' ' + cls : '') }, text); }
|
|
549
|
+
|
|
550
|
+
// Resolved lazily, not at parse time: this file runs before ConsentKit.init()
|
|
551
|
+
// has merged the site's config, so asking for the language now would always
|
|
552
|
+
// read the built-in default. Re-resolved on every render so a page that
|
|
553
|
+
// switches language at runtime switches the panel too.
|
|
554
|
+
var T = STRINGS.en;
|
|
555
|
+
function refreshLang() {
|
|
556
|
+
var nav = global.navigator;
|
|
557
|
+
T = STRINGS[pickLang(
|
|
558
|
+
(CK && CK.config && CK.config.language) || '',
|
|
559
|
+
(nav && (nav.language || nav.userLanguage)) || ''
|
|
560
|
+
)] || STRINGS.en;
|
|
561
|
+
return T;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function render() {
|
|
565
|
+
if (!body) { return; }
|
|
566
|
+
refreshLang();
|
|
567
|
+
var r = buildReport(reportInput());
|
|
568
|
+
body.textContent = '';
|
|
569
|
+
|
|
570
|
+
// 1. Client
|
|
571
|
+
var s1 = section(T.secClient);
|
|
572
|
+
s1.appendChild(defs([
|
|
573
|
+
[T.version, r.client.version],
|
|
574
|
+
[T.source, r.client.source === 'saas' ? T.srcSaas : T.srcInline],
|
|
575
|
+
['siteId', r.client.siteId],
|
|
576
|
+
['policyVersion', r.client.policyVersion],
|
|
577
|
+
['ETag', r.client.etag]
|
|
578
|
+
]));
|
|
579
|
+
body.appendChild(s1);
|
|
580
|
+
|
|
581
|
+
// 2. Consent
|
|
582
|
+
var s2 = section(T.secConsent);
|
|
583
|
+
var line = el('div');
|
|
584
|
+
line.appendChild(tag(T.status[r.consent.status] || r.consent.status,
|
|
585
|
+
r.consent.status === 'accepted' ? 'on' : r.consent.status === 'rejected' ? 'off' : ''));
|
|
586
|
+
s2.appendChild(line);
|
|
587
|
+
var cl = doc.createElement('div');
|
|
588
|
+
['necessary', 'functional', 'analytics', 'marketing'].forEach(function (c) {
|
|
589
|
+
cl.appendChild(tag((r.consent.categories[c] ? '✓ ' : '✗ ') + c,
|
|
590
|
+
r.consent.categories[c] ? 'on' : 'off'));
|
|
591
|
+
});
|
|
592
|
+
s2.appendChild(cl);
|
|
593
|
+
s2.appendChild(defs([
|
|
594
|
+
[T.decidedAt, r.consent.decidedAt],
|
|
595
|
+
[T.method, r.consent.method],
|
|
596
|
+
[T.ttl, r.consent.ttlDays == null ? null : r.consent.ttlDays + T.days]
|
|
597
|
+
]));
|
|
598
|
+
body.appendChild(s2);
|
|
599
|
+
|
|
600
|
+
// 3. Blocked until consent
|
|
601
|
+
var s3 = section(T.secBlocked + ' (' + r.blocked.length + ')');
|
|
602
|
+
if (!r.blocked.length) {
|
|
603
|
+
s3.appendChild(el('div', { class: 'mut' }, T.noBlocked));
|
|
604
|
+
} else {
|
|
605
|
+
var u3 = doc.createElement('ul');
|
|
606
|
+
r.blocked.forEach(function (b) {
|
|
607
|
+
var li = doc.createElement('li');
|
|
608
|
+
li.appendChild(tag(b.kind));
|
|
609
|
+
li.appendChild(tag(b.category || '?'));
|
|
610
|
+
if (b.strict) { li.appendChild(tag(T.strict, 'off')); }
|
|
611
|
+
li.appendChild(doc.createTextNode(b.host + b.path +
|
|
612
|
+
(b.origin === 'markup' ? T.markup : '') +
|
|
613
|
+
(b.revived === false ? T.notRevived : '')));
|
|
614
|
+
u3.appendChild(li);
|
|
615
|
+
});
|
|
616
|
+
s3.appendChild(u3);
|
|
617
|
+
}
|
|
618
|
+
body.appendChild(s3);
|
|
619
|
+
|
|
620
|
+
// 4. Tracker requests
|
|
621
|
+
var s4 = section(T.secRequests + ' (' + r.requests.length + ')');
|
|
622
|
+
if (!r.requests.length) {
|
|
623
|
+
s4.appendChild(el('div', { class: 'mut' }, T.noRequests));
|
|
624
|
+
} else {
|
|
625
|
+
var u4 = doc.createElement('ul');
|
|
626
|
+
r.requests.forEach(function (q) {
|
|
627
|
+
var li = doc.createElement('li');
|
|
628
|
+
li.appendChild(tag(q.when === 'after' ? T.after : T.before,
|
|
629
|
+
q.when === 'after' ? 'on' : 'off'));
|
|
630
|
+
li.appendChild(tag(q.category));
|
|
631
|
+
li.appendChild(doc.createTextNode(q.host + q.path + ' · ' + q.at + T.ms +
|
|
632
|
+
(q.count > 1 ? ' ×' + q.count : '')));
|
|
633
|
+
u4.appendChild(li);
|
|
634
|
+
});
|
|
635
|
+
s4.appendChild(u4);
|
|
636
|
+
}
|
|
637
|
+
s4.appendChild(el('p', { class: 'note' }, T.note));
|
|
638
|
+
body.appendChild(s4);
|
|
639
|
+
|
|
640
|
+
// 5. Consent Mode
|
|
641
|
+
var s5 = section(T.secConsentMode + ' (' + r.consentMode.length + ')');
|
|
642
|
+
if (!r.consentMode.length) {
|
|
643
|
+
s5.appendChild(el('div', { class: 'mut' }, T.noEvents));
|
|
644
|
+
} else {
|
|
645
|
+
var u5 = doc.createElement('ul');
|
|
646
|
+
r.consentMode.slice().reverse().forEach(function (c) {
|
|
647
|
+
var li = doc.createElement('li');
|
|
648
|
+
li.appendChild(tag(c.at + T.ms));
|
|
649
|
+
var txt = c.type;
|
|
650
|
+
if (c.signals) {
|
|
651
|
+
var bits = [];
|
|
652
|
+
for (var k in c.signals) {
|
|
653
|
+
if (Object.prototype.hasOwnProperty.call(c.signals, k)) {
|
|
654
|
+
bits.push(k + '=' + c.signals[k]);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
if (bits.length) { txt += ': ' + bits.join(', '); }
|
|
658
|
+
}
|
|
659
|
+
li.appendChild(doc.createTextNode(txt));
|
|
660
|
+
u5.appendChild(li);
|
|
661
|
+
});
|
|
662
|
+
s5.appendChild(u5);
|
|
663
|
+
}
|
|
664
|
+
body.appendChild(s5);
|
|
665
|
+
|
|
666
|
+
// 6. Buttons
|
|
667
|
+
var s6 = section(T.secActions);
|
|
668
|
+
var row = el('div', { class: 'row' });
|
|
669
|
+
var bReset = el('button', { type: 'button' }, T.reset);
|
|
670
|
+
bReset.addEventListener('click', resetConsent);
|
|
671
|
+
var bShow = el('button', { type: 'button' }, T.showPrefs);
|
|
672
|
+
bShow.addEventListener('click', showBanner);
|
|
673
|
+
var bCopy = el('button', { type: 'button' }, T.copy);
|
|
674
|
+
bCopy.addEventListener('click', function () { copyReport(bCopy); });
|
|
675
|
+
row.appendChild(bReset); row.appendChild(bShow); row.appendChild(bCopy);
|
|
676
|
+
s6.appendChild(row);
|
|
677
|
+
s6.appendChild(el('p', { class: 'note' }, T.footer));
|
|
678
|
+
body.appendChild(s6);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function schedule() {
|
|
682
|
+
if (frame) { return; }
|
|
683
|
+
frame = 1;
|
|
684
|
+
var run = function () { frame = 0; try { render(); } catch (e) { /* noop */ } };
|
|
685
|
+
try {
|
|
686
|
+
if (typeof global.requestAnimationFrame === 'function') { global.requestAnimationFrame(run); }
|
|
687
|
+
else { global.setTimeout(run, 50); }
|
|
688
|
+
} catch (e) { run(); }
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// ---------------------------------------------------------------------------
|
|
692
|
+
// Buttons
|
|
693
|
+
// ---------------------------------------------------------------------------
|
|
694
|
+
function resetConsent() {
|
|
695
|
+
try { if (CK && CK.withdraw) { CK.withdraw(); } } catch (e) { /* noop */ }
|
|
696
|
+
// withdraw() already clears the record; belt and braces for a page whose
|
|
697
|
+
// cookie was written on a parent domain.
|
|
698
|
+
try {
|
|
699
|
+
var h = global.location && global.location.hostname ? global.location.hostname : '';
|
|
700
|
+
var variants = ['', h ? '; domain=' + h : '', h ? '; domain=.' + h : ''];
|
|
701
|
+
var labels = h ? h.split('.') : [];
|
|
702
|
+
if (labels.length > 2) { variants.push('; domain=.' + labels.slice(-2).join('.')); }
|
|
703
|
+
for (var i = 0; i < variants.length; i++) {
|
|
704
|
+
doc.cookie = 'ck_consent=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/' + variants[i];
|
|
705
|
+
}
|
|
706
|
+
} catch (e) { /* noop */ }
|
|
707
|
+
lsDel('ck_consent');
|
|
708
|
+
try { global.location.reload(); } catch (e) { /* noop */ }
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
function showBanner() {
|
|
712
|
+
// Non-destructive on purpose: this opens the preferences panel and leaves
|
|
713
|
+
// the stored decision alone. Erasing consent is the other button's job, and
|
|
714
|
+
// on a live site a "show banner" click must never wipe the owner's record.
|
|
715
|
+
try { if (CK && CK.show) { CK.show(); } } catch (e) { /* noop */ }
|
|
716
|
+
schedule();
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function copyReport(btn) {
|
|
720
|
+
var text = '';
|
|
721
|
+
try { text = JSON.stringify(buildReport(reportInput()), null, 2); } catch (e) { text = '{}'; }
|
|
722
|
+
var done = function (ok) {
|
|
723
|
+
try {
|
|
724
|
+
btn.textContent = ok ? T.copied : T.copyFailed;
|
|
725
|
+
global.setTimeout(function () { btn.textContent = T.copy; }, 1500);
|
|
726
|
+
} catch (e2) { /* noop */ }
|
|
727
|
+
};
|
|
728
|
+
try {
|
|
729
|
+
if (global.navigator && global.navigator.clipboard && global.navigator.clipboard.writeText) {
|
|
730
|
+
global.navigator.clipboard.writeText(text).then(function () { done(true); },
|
|
731
|
+
function () { done(fallbackCopy(text)); });
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
} catch (e) { /* noop */ }
|
|
735
|
+
done(fallbackCopy(text));
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
function fallbackCopy(text) {
|
|
739
|
+
try {
|
|
740
|
+
var ta = doc.createElement('textarea');
|
|
741
|
+
ta.value = text;
|
|
742
|
+
ta.setAttribute('style', 'position:fixed;top:-9999px;left:-9999px');
|
|
743
|
+
doc.body.appendChild(ta);
|
|
744
|
+
ta.select();
|
|
745
|
+
var ok = doc.execCommand ? doc.execCommand('copy') : false;
|
|
746
|
+
doc.body.removeChild(ta);
|
|
747
|
+
return !!ok;
|
|
748
|
+
} catch (e) { return false; }
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// ---------------------------------------------------------------------------
|
|
752
|
+
// Mount
|
|
753
|
+
// ---------------------------------------------------------------------------
|
|
754
|
+
// The banner's bar layout sits at bottom:16 across the full width and the UI's
|
|
755
|
+
// floating button at bottom-left; lift the panel above whatever is at the
|
|
756
|
+
// bottom right now so its buttons stay clickable.
|
|
757
|
+
function avoidBanner() {
|
|
758
|
+
if (!host) { return; }
|
|
759
|
+
var bottom = 12;
|
|
760
|
+
try {
|
|
761
|
+
var ckRoot = doc.getElementById('ck-root');
|
|
762
|
+
var sr = ckRoot && ckRoot.shadowRoot;
|
|
763
|
+
// The bar spans the width at bottom:16; the box-right card sits exactly
|
|
764
|
+
// where this panel does. Both must be cleared (the UI's floating button
|
|
765
|
+
// is bottom-left and never collides).
|
|
766
|
+
var banner = sr && sr.querySelector(
|
|
767
|
+
'.ck-banner--bar.ck-pos-bottom, .ck-banner--box.ck-pos-bottom-right');
|
|
768
|
+
if (banner && banner.getBoundingClientRect) {
|
|
769
|
+
var r = banner.getBoundingClientRect();
|
|
770
|
+
if (r.height > 0 && r.bottom > (global.innerHeight || 0) - r.height - 40) {
|
|
771
|
+
bottom = Math.round(r.height) + 24;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
} catch (e) { /* noop */ }
|
|
775
|
+
try { host.style.bottom = bottom + 'px'; } catch (e2) { /* noop */ }
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
function mount() {
|
|
779
|
+
if (host || !doc.body) { return; }
|
|
780
|
+
// A page can carry the loader (inline block) AND a manual <script> tag for
|
|
781
|
+
// the panel: the second copy must not mount a second panel.
|
|
782
|
+
if (doc.querySelector('ck-debug')) { return; }
|
|
783
|
+
refreshLang();
|
|
784
|
+
host = doc.createElement('ck-debug');
|
|
785
|
+
host.setAttribute('aria-live', 'off');
|
|
786
|
+
root = host.attachShadow ? host.attachShadow({ mode: 'open' }) : null;
|
|
787
|
+
if (!root) { host = null; return; }
|
|
788
|
+
|
|
789
|
+
var style = doc.createElement('style');
|
|
790
|
+
style.textContent = CSS;
|
|
791
|
+
root.appendChild(style);
|
|
792
|
+
|
|
793
|
+
var wrap = el('div', { class: 'w', role: 'region', 'aria-label': T.regionLabel });
|
|
794
|
+
var head = el('div', { class: 'hd' });
|
|
795
|
+
head.appendChild(el('b', null, 'ConsentKit debug'));
|
|
796
|
+
head.appendChild(el('span', { class: 'sp' }));
|
|
797
|
+
|
|
798
|
+
toggleBtn = el('button', { type: 'button', 'aria-expanded': 'true' }, T.collapse);
|
|
799
|
+
toggleBtn.addEventListener('click', function () {
|
|
800
|
+
collapsed = !collapsed;
|
|
801
|
+
body.hidden = collapsed;
|
|
802
|
+
toggleBtn.textContent = collapsed ? T.expand : T.collapse;
|
|
803
|
+
toggleBtn.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
|
|
804
|
+
if (!collapsed) { schedule(); }
|
|
805
|
+
});
|
|
806
|
+
head.appendChild(toggleBtn);
|
|
807
|
+
|
|
808
|
+
var closeBtn = el('button', { type: 'button', 'aria-label': T.closeLabel }, '×');
|
|
809
|
+
closeBtn.addEventListener('click', function () {
|
|
810
|
+
lsDel(LS_KEY);
|
|
811
|
+
try { host.parentNode.removeChild(host); } catch (e) { /* noop */ }
|
|
812
|
+
host = null;
|
|
813
|
+
API.active = false;
|
|
814
|
+
});
|
|
815
|
+
head.appendChild(closeBtn);
|
|
816
|
+
|
|
817
|
+
wrap.appendChild(head);
|
|
818
|
+
body = el('div', { class: 'bd' });
|
|
819
|
+
wrap.appendChild(body);
|
|
820
|
+
root.appendChild(wrap);
|
|
821
|
+
doc.body.appendChild(host);
|
|
822
|
+
|
|
823
|
+
render();
|
|
824
|
+
avoidBanner();
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function boot() {
|
|
828
|
+
seedConsentTime();
|
|
829
|
+
stampOnDecision();
|
|
830
|
+
collectResources();
|
|
831
|
+
watchDataLayer();
|
|
832
|
+
mount();
|
|
833
|
+
|
|
834
|
+
try {
|
|
835
|
+
doc.addEventListener('ck:change', function () {
|
|
836
|
+
// Fallback for a decision made through some other path than
|
|
837
|
+
// accept()/rejectAll() (a host page calling into the core directly).
|
|
838
|
+
// stampOnDecision() has usually set this already, and its stamp is the
|
|
839
|
+
// accurate one — do not overwrite it here: entry.startTime is on the
|
|
840
|
+
// performance clock, and by now the revived scripts have already run.
|
|
841
|
+
try {
|
|
842
|
+
var st = CK && CK.getState ? CK.getState() : null;
|
|
843
|
+
if (st && st.decided) { if (consentAtMs === null) { consentAtMs = nowMs(); } }
|
|
844
|
+
else { consentAtMs = null; }
|
|
845
|
+
} catch (e2) { /* noop */ }
|
|
846
|
+
schedule();
|
|
847
|
+
global.setTimeout(avoidBanner, 60);
|
|
848
|
+
}, false);
|
|
849
|
+
doc.addEventListener('ck:init', function () { schedule(); global.setTimeout(avoidBanner, 60); }, false);
|
|
850
|
+
} catch (e) { /* noop */ }
|
|
851
|
+
|
|
852
|
+
// Late trackers and revived scripts keep arriving after the first render.
|
|
853
|
+
try { global.setInterval(schedule, 3000); } catch (e) { /* noop */ }
|
|
854
|
+
try { global.addEventListener('resize', avoidBanner, false); } catch (e) { /* noop */ }
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
try {
|
|
858
|
+
if (doc.readyState === 'loading') {
|
|
859
|
+
doc.addEventListener('DOMContentLoaded', function () { try { boot(); } catch (e) { /* noop */ } }, false);
|
|
860
|
+
} else { boot(); }
|
|
861
|
+
} catch (e) { /* noop */ }
|
|
862
|
+
})(typeof window !== 'undefined' ? window : (typeof globalThis !== 'undefined' ? globalThis : this));
|