@solvenza/cookies-compliance 1.2.1 → 1.5.3
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 +960 -36
- package/bin/audit-cli.js +134 -13
- package/dist/audit.cjs +811 -0
- package/dist/audit.cjs.map +1 -0
- package/dist/audit.d.cts +60 -0
- package/dist/audit.d.ts +60 -0
- package/dist/audit.js +380 -0
- package/dist/audit.js.map +1 -0
- package/dist/chunk-C5QPXHTH.js +3557 -0
- package/dist/chunk-C5QPXHTH.js.map +1 -0
- package/dist/chunk-OHOHIAK6.js +442 -0
- package/dist/chunk-OHOHIAK6.js.map +1 -0
- package/dist/chunk-Q3FZFXQE.js +89 -0
- package/dist/chunk-UQU54ANZ.js +225 -0
- package/dist/chunk-UQU54ANZ.js.map +1 -0
- package/dist/chunk-W5MG56VI.js +269 -0
- package/dist/chunk-W5MG56VI.js.map +1 -0
- package/dist/consent.esm.cjs +2195 -84
- package/dist/consent.esm.cjs.map +1 -1
- package/dist/consent.esm.d.cts +173 -6
- package/dist/consent.esm.d.ts +173 -6
- package/dist/consent.esm.js +57 -23
- package/dist/consent.esm.js.map +1 -1
- package/dist/consent.min.js +437 -55
- package/dist/consent.min.js.map +1 -1
- package/dist/presets.cjs +495 -0
- package/dist/presets.cjs.map +1 -0
- package/dist/presets.d.cts +51 -0
- package/dist/presets.d.ts +51 -0
- package/dist/presets.js +16 -0
- package/dist/presets.js.map +1 -0
- package/dist/types--kyI7OOR.d.cts +314 -0
- package/dist/types--kyI7OOR.d.ts +314 -0
- package/dist/wrappers/angular.cjs +2341 -65
- package/dist/wrappers/angular.cjs.map +1 -1
- package/dist/wrappers/angular.d.cts +85 -5
- package/dist/wrappers/angular.d.ts +85 -5
- package/dist/wrappers/angular.js +217 -3
- package/dist/wrappers/angular.js.map +1 -1
- package/dist/wrappers/next.cjs +2353 -103
- package/dist/wrappers/next.cjs.map +1 -1
- package/dist/wrappers/next.d.cts +25 -1
- package/dist/wrappers/next.d.ts +25 -1
- package/dist/wrappers/next.js +30 -5
- package/dist/wrappers/next.js.map +1 -1
- package/dist/wrappers/nuxt.cjs +4171 -0
- package/dist/wrappers/nuxt.cjs.map +1 -0
- package/dist/wrappers/nuxt.d.cts +33 -0
- package/dist/wrappers/nuxt.d.ts +33 -0
- package/dist/wrappers/nuxt.js +51 -0
- package/dist/wrappers/nuxt.js.map +1 -0
- package/dist/wrappers/react.cjs +2336 -141
- package/dist/wrappers/react.cjs.map +1 -1
- package/dist/wrappers/react.d.cts +87 -1
- package/dist/wrappers/react.d.ts +87 -1
- package/dist/wrappers/react.js +21 -51
- package/dist/wrappers/react.js.map +1 -1
- package/dist/wrappers/vue.cjs +4163 -0
- package/dist/wrappers/vue.cjs.map +1 -0
- package/dist/wrappers/vue.d.cts +144 -0
- package/dist/wrappers/vue.d.ts +144 -0
- package/dist/wrappers/vue.js +28 -0
- package/dist/wrappers/vue.js.map +1 -0
- package/dist/wrappers/wordpress.js +1 -1
- package/package.json +26 -5
- package/schema.json +143 -4
- package/dist/chunk-DDAAVRWG.js +0 -25
- package/dist/chunk-FBRP4MSA.js +0 -1907
- package/dist/chunk-FBRP4MSA.js.map +0 -1
- package/dist/consent-engine-DOMyEKfk.d.cts +0 -193
- package/dist/consent-engine-DOMyEKfk.d.ts +0 -193
- /package/dist/{chunk-DDAAVRWG.js.map → chunk-Q3FZFXQE.js.map} +0 -0
|
@@ -0,0 +1,3557 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveConfigPresets
|
|
3
|
+
} from "./chunk-OHOHIAK6.js";
|
|
4
|
+
import {
|
|
5
|
+
__spreadValues
|
|
6
|
+
} from "./chunk-Q3FZFXQE.js";
|
|
7
|
+
|
|
8
|
+
// src/core/config-validator.ts
|
|
9
|
+
var ConfigValidationError = class extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(`[ConsentSDK Config Error] ${message}`);
|
|
12
|
+
this.name = "ConfigValidationError";
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
function validateConfig(config) {
|
|
16
|
+
if (!config) {
|
|
17
|
+
throw new ConfigValidationError("Configuration object is null or undefined.");
|
|
18
|
+
}
|
|
19
|
+
if (typeof config.schemaVersion !== "number" || config.schemaVersion < 1) {
|
|
20
|
+
throw new ConfigValidationError("Invalid or missing 'schemaVersion'. Expected integer >= 1.");
|
|
21
|
+
}
|
|
22
|
+
if (!config.policyVersion || typeof config.policyVersion !== "string") {
|
|
23
|
+
throw new ConfigValidationError("Invalid or missing 'policyVersion'. String required.");
|
|
24
|
+
}
|
|
25
|
+
if (!config.categories || typeof config.categories !== "object") {
|
|
26
|
+
throw new ConfigValidationError("Missing 'categories' map in configuration.");
|
|
27
|
+
}
|
|
28
|
+
const categoryKeys = Object.keys(config.categories);
|
|
29
|
+
if (categoryKeys.length === 0) {
|
|
30
|
+
throw new ConfigValidationError("At least one category must be defined in 'categories'.");
|
|
31
|
+
}
|
|
32
|
+
const necessaryCategory = config.categories["necessary"];
|
|
33
|
+
if (!necessaryCategory) {
|
|
34
|
+
throw new ConfigValidationError("Category 'necessary' must be defined.");
|
|
35
|
+
}
|
|
36
|
+
if (necessaryCategory.required !== true) {
|
|
37
|
+
throw new ConfigValidationError("Category 'necessary' must have 'required: true'.");
|
|
38
|
+
}
|
|
39
|
+
for (const [catId, catConfig] of Object.entries(config.categories)) {
|
|
40
|
+
if (catId !== "necessary" && !catConfig.required) {
|
|
41
|
+
if (catConfig.default === true) {
|
|
42
|
+
throw new ConfigValidationError(
|
|
43
|
+
`Legal violation (AEPD): Optional category '${catId}' cannot have default: true. All optional categories must be opt-in (default: false).`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (config.services && typeof config.services === "object") {
|
|
49
|
+
for (const [serviceId, serviceConfig] of Object.entries(config.services)) {
|
|
50
|
+
const category = serviceConfig.category;
|
|
51
|
+
if (!category) {
|
|
52
|
+
throw new ConfigValidationError(
|
|
53
|
+
`Service '${serviceId}' missing 'category' reference.`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (!config.categories[category]) {
|
|
57
|
+
throw new ConfigValidationError(
|
|
58
|
+
`Service '${serviceId}' references non-existent category '${category}'.`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/core/security.ts
|
|
66
|
+
function sha256Sync(ascii) {
|
|
67
|
+
let i, j;
|
|
68
|
+
let result = "";
|
|
69
|
+
const asciiLength = ascii.length * 8;
|
|
70
|
+
let hash = [
|
|
71
|
+
1779033703,
|
|
72
|
+
3144134277,
|
|
73
|
+
1013904242,
|
|
74
|
+
2773480762,
|
|
75
|
+
1359893119,
|
|
76
|
+
2600822924,
|
|
77
|
+
528734635,
|
|
78
|
+
1541459225
|
|
79
|
+
];
|
|
80
|
+
const k = [
|
|
81
|
+
1116352408,
|
|
82
|
+
1899447441,
|
|
83
|
+
3049323471,
|
|
84
|
+
3921009573,
|
|
85
|
+
961987163,
|
|
86
|
+
1508970993,
|
|
87
|
+
2453635748,
|
|
88
|
+
2870763221,
|
|
89
|
+
3624381080,
|
|
90
|
+
310598401,
|
|
91
|
+
607225278,
|
|
92
|
+
1426881987,
|
|
93
|
+
1925078388,
|
|
94
|
+
2162078206,
|
|
95
|
+
2614888103,
|
|
96
|
+
3248222580,
|
|
97
|
+
3835390401,
|
|
98
|
+
4022224774,
|
|
99
|
+
264347078,
|
|
100
|
+
604807628,
|
|
101
|
+
770255983,
|
|
102
|
+
1249150122,
|
|
103
|
+
1555081692,
|
|
104
|
+
1996064986,
|
|
105
|
+
2554220882,
|
|
106
|
+
2821834349,
|
|
107
|
+
2952996808,
|
|
108
|
+
3210313671,
|
|
109
|
+
3336571891,
|
|
110
|
+
3584528711,
|
|
111
|
+
113926993,
|
|
112
|
+
338241895,
|
|
113
|
+
666307205,
|
|
114
|
+
773529912,
|
|
115
|
+
1294757372,
|
|
116
|
+
1396182291,
|
|
117
|
+
1695183700,
|
|
118
|
+
1986661051,
|
|
119
|
+
2177026350,
|
|
120
|
+
2456956037,
|
|
121
|
+
2730485921,
|
|
122
|
+
2820302411,
|
|
123
|
+
3259730800,
|
|
124
|
+
3345764771,
|
|
125
|
+
3516065817,
|
|
126
|
+
3600352804,
|
|
127
|
+
4094571909,
|
|
128
|
+
275423344,
|
|
129
|
+
430227734,
|
|
130
|
+
506948616,
|
|
131
|
+
659060556,
|
|
132
|
+
883997877,
|
|
133
|
+
958139571,
|
|
134
|
+
1322822218,
|
|
135
|
+
1537002063,
|
|
136
|
+
1747873779,
|
|
137
|
+
1955562222,
|
|
138
|
+
2024104815,
|
|
139
|
+
2227730452,
|
|
140
|
+
2361852424,
|
|
141
|
+
2428436474,
|
|
142
|
+
2756734187,
|
|
143
|
+
3204031479,
|
|
144
|
+
3329325298
|
|
145
|
+
];
|
|
146
|
+
const blocks = [];
|
|
147
|
+
for (i = 0; i < ascii.length; i++) {
|
|
148
|
+
blocks[i >> 2] |= ascii.charCodeAt(i) << 24 - i % 4 * 8;
|
|
149
|
+
}
|
|
150
|
+
blocks[asciiLength >> 5] |= 128 << 24 - asciiLength % 32;
|
|
151
|
+
blocks[(asciiLength + 64 >> 9 << 4) + 15] = asciiLength;
|
|
152
|
+
for (i = 0; i < blocks.length; i += 16) {
|
|
153
|
+
const w = blocks.slice(i, i + 16);
|
|
154
|
+
const oldHash = [...hash];
|
|
155
|
+
for (j = 0; j < 64; j++) {
|
|
156
|
+
const w15 = w[j - 15], w2 = w[j - 2];
|
|
157
|
+
const s0 = (w15 >>> 7 | w15 << 25) ^ (w15 >>> 18 | w15 << 14) ^ w15 >>> 3;
|
|
158
|
+
const s1 = (w2 >>> 17 | w2 << 15) ^ (w2 >>> 19 | w2 << 13) ^ w2 >>> 10;
|
|
159
|
+
w[j] = j < 16 ? w[j] || 0 : w[j - 16] + s0 + w[j - 7] + s1 | 0;
|
|
160
|
+
const ch = hash[4] & hash[5] ^ ~hash[4] & hash[6];
|
|
161
|
+
const maj = hash[0] & hash[1] ^ hash[0] & hash[2] ^ hash[1] & hash[2];
|
|
162
|
+
const sig0 = (hash[0] >>> 2 | hash[0] << 30) ^ (hash[0] >>> 13 | hash[0] << 19) ^ (hash[0] >>> 22 | hash[0] << 10);
|
|
163
|
+
const sig1 = (hash[4] >>> 6 | hash[4] << 26) ^ (hash[4] >>> 11 | hash[4] << 21) ^ (hash[4] >>> 25 | hash[4] << 7);
|
|
164
|
+
const t1 = hash[7] + sig1 + ch + k[j] + (w[j] | 0);
|
|
165
|
+
const t2 = sig0 + maj;
|
|
166
|
+
hash[7] = hash[6];
|
|
167
|
+
hash[6] = hash[5];
|
|
168
|
+
hash[5] = hash[4];
|
|
169
|
+
hash[4] = hash[3] + t1 | 0;
|
|
170
|
+
hash[3] = hash[2];
|
|
171
|
+
hash[2] = hash[1];
|
|
172
|
+
hash[1] = hash[0];
|
|
173
|
+
hash[0] = t1 + t2 | 0;
|
|
174
|
+
}
|
|
175
|
+
for (j = 0; j < 8; j++) {
|
|
176
|
+
hash[j] = hash[j] + oldHash[j] | 0;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for (i = 0; i < 8; i++) {
|
|
180
|
+
for (j = 3; j >= 0; j--) {
|
|
181
|
+
const b = hash[i] >> j * 8 & 255;
|
|
182
|
+
result += (b < 16 ? "0" : "") + b.toString(16);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
function computeReceiptSignature(payload, secretKey) {
|
|
188
|
+
if (secretKey) {
|
|
189
|
+
return sha256Sync(`${secretKey}:${payload}:${secretKey}`);
|
|
190
|
+
}
|
|
191
|
+
let hash = 2166136261;
|
|
192
|
+
for (let i = 0; i < payload.length; i++) {
|
|
193
|
+
hash ^= payload.charCodeAt(i);
|
|
194
|
+
hash = Math.imul(hash, 16777619);
|
|
195
|
+
}
|
|
196
|
+
return (hash >>> 0).toString(16);
|
|
197
|
+
}
|
|
198
|
+
function verifyReceiptIntegrity(payload, expectedSignature, secretKey) {
|
|
199
|
+
if (!expectedSignature) return false;
|
|
200
|
+
return computeReceiptSignature(payload, secretKey) === expectedSignature;
|
|
201
|
+
}
|
|
202
|
+
function sanitizeHtml(str) {
|
|
203
|
+
if (!str) return "";
|
|
204
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
205
|
+
}
|
|
206
|
+
function sanitizeUrl(url) {
|
|
207
|
+
if (!url) return "#";
|
|
208
|
+
const trimmed = url.trim().toLowerCase();
|
|
209
|
+
if (trimmed.startsWith("javascript:") || trimmed.startsWith("data:") || trimmed.startsWith("vbscript:")) {
|
|
210
|
+
return "#";
|
|
211
|
+
}
|
|
212
|
+
return url;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// src/core/receipt.ts
|
|
216
|
+
function generateUUID() {
|
|
217
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
218
|
+
return crypto.randomUUID();
|
|
219
|
+
}
|
|
220
|
+
return "xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
221
|
+
const r = Math.random() * 16 | 0;
|
|
222
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
223
|
+
return v.toString(16);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
function createReceipt(policyVersion, choices, source, existingReceipt, secretKey) {
|
|
227
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
228
|
+
const receiptId = (existingReceipt == null ? void 0 : existingReceipt.receiptId) || generateUUID();
|
|
229
|
+
const decidedAt = (existingReceipt == null ? void 0 : existingReceipt.decidedAt) || now;
|
|
230
|
+
const payloadToSign = `${receiptId}:${policyVersion}:${JSON.stringify(choices)}`;
|
|
231
|
+
const signature = computeReceiptSignature(payloadToSign, secretKey);
|
|
232
|
+
return {
|
|
233
|
+
schema: 1,
|
|
234
|
+
receiptId,
|
|
235
|
+
policyVersion,
|
|
236
|
+
decidedAt,
|
|
237
|
+
updatedAt: now,
|
|
238
|
+
source,
|
|
239
|
+
choices,
|
|
240
|
+
signature
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function parseReceipt(jsonStr) {
|
|
244
|
+
try {
|
|
245
|
+
const data = JSON.parse(jsonStr);
|
|
246
|
+
if (typeof data === "object" && data !== null && typeof data.receiptId === "string" && typeof data.policyVersion === "string" && typeof data.choices === "object") {
|
|
247
|
+
return data;
|
|
248
|
+
}
|
|
249
|
+
return null;
|
|
250
|
+
} catch (e) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function isReceiptExpired(receipt, maxAgeDays = 365) {
|
|
255
|
+
if (!receipt.updatedAt) return true;
|
|
256
|
+
const updated = new Date(receipt.updatedAt).getTime();
|
|
257
|
+
const now = Date.now();
|
|
258
|
+
const maxAgeMs = maxAgeDays * 86400 * 1e3;
|
|
259
|
+
return now - updated > maxAgeMs;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// src/storage/cookie-store.ts
|
|
263
|
+
var CookieStorageProvider = class {
|
|
264
|
+
get(key) {
|
|
265
|
+
return CookieStore.get(key);
|
|
266
|
+
}
|
|
267
|
+
set(key, value, options) {
|
|
268
|
+
CookieStore.set(key, value, options);
|
|
269
|
+
}
|
|
270
|
+
remove(key, path = "/", domain) {
|
|
271
|
+
CookieStore.remove(key, path, domain);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
var CookieStore = class {
|
|
275
|
+
static get(name) {
|
|
276
|
+
if (typeof document === "undefined") return null;
|
|
277
|
+
const nameEQ = encodeURIComponent(name) + "=";
|
|
278
|
+
const ca = document.cookie.split(";");
|
|
279
|
+
for (let i = 0; i < ca.length; i++) {
|
|
280
|
+
let c = ca[i];
|
|
281
|
+
while (c.charAt(0) === " ") c = c.substring(1, c.length);
|
|
282
|
+
if (c.indexOf(nameEQ) === 0) {
|
|
283
|
+
return decodeURIComponent(c.substring(nameEQ.length, c.length));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
static set(name, value, options = {}) {
|
|
289
|
+
var _a, _b;
|
|
290
|
+
if (typeof document === "undefined") return;
|
|
291
|
+
const path = options.path || "/";
|
|
292
|
+
const maxAgeDays = (_a = options.maxAgeDays) != null ? _a : 365;
|
|
293
|
+
const maxAgeSeconds = Math.floor(maxAgeDays * 86400);
|
|
294
|
+
const sameSite = options.sameSite || "Lax";
|
|
295
|
+
const isHttps = typeof window !== "undefined" && window.location.protocol === "https:";
|
|
296
|
+
const secure = (_b = options.secure) != null ? _b : isHttps;
|
|
297
|
+
let cookieStr = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; Path=${path}; Max-Age=${maxAgeSeconds}; SameSite=${sameSite}`;
|
|
298
|
+
if (options.domain) {
|
|
299
|
+
cookieStr += `; Domain=${options.domain}`;
|
|
300
|
+
}
|
|
301
|
+
if (secure) {
|
|
302
|
+
cookieStr += "; Secure";
|
|
303
|
+
}
|
|
304
|
+
document.cookie = cookieStr;
|
|
305
|
+
}
|
|
306
|
+
static remove(name, path = "/", domain) {
|
|
307
|
+
if (typeof document === "undefined") return;
|
|
308
|
+
let cookieStr = `${encodeURIComponent(name)}=; Path=${path}; Max-Age=0; SameSite=Lax`;
|
|
309
|
+
if (domain) {
|
|
310
|
+
cookieStr += `; Domain=${domain}`;
|
|
311
|
+
}
|
|
312
|
+
document.cookie = cookieStr;
|
|
313
|
+
}
|
|
314
|
+
static clearServiceCookies(config, revokedCategory) {
|
|
315
|
+
var _a, _b;
|
|
316
|
+
if (typeof document === "undefined" || !config.services) return;
|
|
317
|
+
const path = ((_a = config.storage) == null ? void 0 : _a.path) || "/";
|
|
318
|
+
const domain = (_b = config.storage) == null ? void 0 : _b.domain;
|
|
319
|
+
for (const service of Object.values(config.services)) {
|
|
320
|
+
if (service.category === revokedCategory && service.cookies) {
|
|
321
|
+
for (const cookieItem of service.cookies) {
|
|
322
|
+
this.remove(cookieItem.name, path, domain || cookieItem.domain);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// src/storage/memory-store.ts
|
|
330
|
+
var _MemoryStorageProvider = class _MemoryStorageProvider {
|
|
331
|
+
get(key) {
|
|
332
|
+
var _a;
|
|
333
|
+
return (_a = _MemoryStorageProvider.store.get(key)) != null ? _a : null;
|
|
334
|
+
}
|
|
335
|
+
set(key, value) {
|
|
336
|
+
_MemoryStorageProvider.store.set(key, value);
|
|
337
|
+
}
|
|
338
|
+
remove(key) {
|
|
339
|
+
_MemoryStorageProvider.store.delete(key);
|
|
340
|
+
}
|
|
341
|
+
clear() {
|
|
342
|
+
_MemoryStorageProvider.store.clear();
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
_MemoryStorageProvider.store = /* @__PURE__ */ new Map();
|
|
346
|
+
var MemoryStorageProvider = _MemoryStorageProvider;
|
|
347
|
+
var MemoryStore = {
|
|
348
|
+
get: (name) => new MemoryStorageProvider().get(name),
|
|
349
|
+
set: (name, val) => new MemoryStorageProvider().set(name, val),
|
|
350
|
+
remove: (name) => new MemoryStorageProvider().remove(name),
|
|
351
|
+
clear: () => new MemoryStorageProvider().clear()
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// src/storage/storage-cleaner.ts
|
|
355
|
+
var StorageCleaner = class {
|
|
356
|
+
/**
|
|
357
|
+
* Evaluates if a given storage key or cookie name matches a pattern.
|
|
358
|
+
* Supports:
|
|
359
|
+
* - Wildcard glob: "*", "_ga*", "ph_*_posthog", "*session*"
|
|
360
|
+
* - Exact string match (case-insensitive)
|
|
361
|
+
*/
|
|
362
|
+
static matchesPattern(key, pattern) {
|
|
363
|
+
if (!key || !pattern) return false;
|
|
364
|
+
if (pattern === "*" || key === pattern) return true;
|
|
365
|
+
if (pattern.includes("*")) {
|
|
366
|
+
const escaped = pattern.split("*").map((segment) => segment.replace(/[-[\]{}()+?.,\\^$|#\s]/g, "\\$&")).join(".*");
|
|
367
|
+
try {
|
|
368
|
+
const regex = new RegExp(`^${escaped}$`, "i");
|
|
369
|
+
return regex.test(key);
|
|
370
|
+
} catch (e) {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
return key.toLowerCase() === pattern.toLowerCase();
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Purges keys from window.localStorage that match any of the provided patterns.
|
|
378
|
+
* Safe in SSR, sandboxed iframes, and private browsing modes.
|
|
379
|
+
*/
|
|
380
|
+
static purgeLocalStorage(patterns) {
|
|
381
|
+
if (typeof window === "undefined" || !patterns || patterns.length === 0) return [];
|
|
382
|
+
try {
|
|
383
|
+
if (typeof window.localStorage === "undefined") return [];
|
|
384
|
+
const keysToRemove = [];
|
|
385
|
+
const len = window.localStorage.length;
|
|
386
|
+
for (let i = 0; i < len; i++) {
|
|
387
|
+
const key = window.localStorage.key(i);
|
|
388
|
+
if (key && patterns.some((p) => this.matchesPattern(key, p))) {
|
|
389
|
+
keysToRemove.push(key);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
for (const key of keysToRemove) {
|
|
393
|
+
window.localStorage.removeItem(key);
|
|
394
|
+
}
|
|
395
|
+
return keysToRemove;
|
|
396
|
+
} catch (e) {
|
|
397
|
+
return [];
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Purges keys from window.sessionStorage that match any of the provided patterns.
|
|
402
|
+
* Safe in SSR, sandboxed iframes, and private browsing modes.
|
|
403
|
+
*/
|
|
404
|
+
static purgeSessionStorage(patterns) {
|
|
405
|
+
if (typeof window === "undefined" || !patterns || patterns.length === 0) return [];
|
|
406
|
+
try {
|
|
407
|
+
if (typeof window.sessionStorage === "undefined") return [];
|
|
408
|
+
const keysToRemove = [];
|
|
409
|
+
const len = window.sessionStorage.length;
|
|
410
|
+
for (let i = 0; i < len; i++) {
|
|
411
|
+
const key = window.sessionStorage.key(i);
|
|
412
|
+
if (key && patterns.some((p) => this.matchesPattern(key, p))) {
|
|
413
|
+
keysToRemove.push(key);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
for (const key of keysToRemove) {
|
|
417
|
+
window.sessionStorage.removeItem(key);
|
|
418
|
+
}
|
|
419
|
+
return keysToRemove;
|
|
420
|
+
} catch (e) {
|
|
421
|
+
return [];
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Purges cookies declared for services, supporting wildcard glob names (e.g. "_ga_*").
|
|
426
|
+
*/
|
|
427
|
+
static purgeCookies(cookieDeclarations, defaultPath = "/", defaultDomain) {
|
|
428
|
+
if (typeof document === "undefined" || !cookieDeclarations || cookieDeclarations.length === 0) {
|
|
429
|
+
return [];
|
|
430
|
+
}
|
|
431
|
+
const removedCookies = [];
|
|
432
|
+
const allExistingCookies = this.getAllCookieNames();
|
|
433
|
+
for (const cookieItem of cookieDeclarations) {
|
|
434
|
+
const targetDomain = cookieItem.domain || defaultDomain;
|
|
435
|
+
if (cookieItem.name.includes("*")) {
|
|
436
|
+
for (const existingName of allExistingCookies) {
|
|
437
|
+
if (this.matchesPattern(existingName, cookieItem.name)) {
|
|
438
|
+
CookieStore.remove(existingName, defaultPath, targetDomain);
|
|
439
|
+
if (!removedCookies.includes(existingName)) {
|
|
440
|
+
removedCookies.push(existingName);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
} else {
|
|
445
|
+
CookieStore.remove(cookieItem.name, defaultPath, targetDomain);
|
|
446
|
+
if (!removedCookies.includes(cookieItem.name)) {
|
|
447
|
+
removedCookies.push(cookieItem.name);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return removedCookies;
|
|
452
|
+
}
|
|
453
|
+
static getAllCookieNames() {
|
|
454
|
+
if (typeof document === "undefined") return [];
|
|
455
|
+
try {
|
|
456
|
+
return document.cookie.split(";").map((c) => c.trim().split("=")[0]).filter((n) => !!n);
|
|
457
|
+
} catch (e) {
|
|
458
|
+
return [];
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Purges all cookies, localStorage, and sessionStorage keys associated with a category.
|
|
463
|
+
*/
|
|
464
|
+
static purgeCategory(config, category) {
|
|
465
|
+
var _a, _b, _c;
|
|
466
|
+
const report = {
|
|
467
|
+
category,
|
|
468
|
+
purgedCookies: [],
|
|
469
|
+
purgedLocalStorage: [],
|
|
470
|
+
purgedSessionStorage: []
|
|
471
|
+
};
|
|
472
|
+
if (!config) return report;
|
|
473
|
+
const path = ((_a = config.storage) == null ? void 0 : _a.path) || "/";
|
|
474
|
+
const defaultDomain = (_b = config.storage) == null ? void 0 : _b.domain;
|
|
475
|
+
const cookiesToPurge = [];
|
|
476
|
+
const localStoragePatterns = /* @__PURE__ */ new Set();
|
|
477
|
+
const sessionStoragePatterns = /* @__PURE__ */ new Set();
|
|
478
|
+
const catConfig = (_c = config.categories) == null ? void 0 : _c[category];
|
|
479
|
+
if (catConfig) {
|
|
480
|
+
if (catConfig.storageKeys) {
|
|
481
|
+
catConfig.storageKeys.forEach((k) => {
|
|
482
|
+
localStoragePatterns.add(k);
|
|
483
|
+
sessionStoragePatterns.add(k);
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
if (catConfig.localStorage) {
|
|
487
|
+
catConfig.localStorage.forEach((k) => localStoragePatterns.add(k));
|
|
488
|
+
}
|
|
489
|
+
if (catConfig.sessionStorage) {
|
|
490
|
+
catConfig.sessionStorage.forEach((k) => sessionStoragePatterns.add(k));
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (config.services) {
|
|
494
|
+
for (const service of Object.values(config.services)) {
|
|
495
|
+
if (service.category === category) {
|
|
496
|
+
if (service.cookies) {
|
|
497
|
+
for (const cookieItem of service.cookies) {
|
|
498
|
+
cookiesToPurge.push({
|
|
499
|
+
name: cookieItem.name,
|
|
500
|
+
domain: cookieItem.domain || defaultDomain
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
if (service.storageKeys) {
|
|
505
|
+
service.storageKeys.forEach((k) => {
|
|
506
|
+
localStoragePatterns.add(k);
|
|
507
|
+
sessionStoragePatterns.add(k);
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
if (service.localStorage) {
|
|
511
|
+
service.localStorage.forEach((k) => localStoragePatterns.add(k));
|
|
512
|
+
}
|
|
513
|
+
if (service.sessionStorage) {
|
|
514
|
+
service.sessionStorage.forEach((k) => sessionStoragePatterns.add(k));
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
report.purgedCookies = this.purgeCookies(cookiesToPurge, path, defaultDomain);
|
|
520
|
+
report.purgedLocalStorage = this.purgeLocalStorage(Array.from(localStoragePatterns));
|
|
521
|
+
report.purgedSessionStorage = this.purgeSessionStorage(Array.from(sessionStoragePatterns));
|
|
522
|
+
return report;
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
// src/blocker/script-gate.ts
|
|
527
|
+
var ScriptResourceBlocker = class {
|
|
528
|
+
constructor() {
|
|
529
|
+
this.type = "script";
|
|
530
|
+
}
|
|
531
|
+
process(isCategoryAllowed, isServiceAllowed) {
|
|
532
|
+
ScriptGate.scanAndActivate(isCategoryAllowed, isServiceAllowed);
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
var ScriptGate = class {
|
|
536
|
+
static scanAndActivate(isCategoryAllowed, isServiceAllowed, onServiceLoaded) {
|
|
537
|
+
var _a;
|
|
538
|
+
if (typeof document === "undefined") return;
|
|
539
|
+
const blockedScripts = Array.from(
|
|
540
|
+
document.querySelectorAll(
|
|
541
|
+
'script[type="text/plain"][data-consent], script[type="text/plain"][data-service]'
|
|
542
|
+
)
|
|
543
|
+
);
|
|
544
|
+
for (const oldScript of blockedScripts) {
|
|
545
|
+
if (this.executedScripts.has(oldScript)) continue;
|
|
546
|
+
const category = oldScript.getAttribute("data-consent");
|
|
547
|
+
const serviceId = oldScript.getAttribute("data-service");
|
|
548
|
+
let isAllowed = false;
|
|
549
|
+
if (serviceId) {
|
|
550
|
+
isAllowed = isServiceAllowed(serviceId);
|
|
551
|
+
} else if (category) {
|
|
552
|
+
isAllowed = isCategoryAllowed(category);
|
|
553
|
+
}
|
|
554
|
+
if (!isAllowed) continue;
|
|
555
|
+
this.executedScripts.add(oldScript);
|
|
556
|
+
const newScript = document.createElement("script");
|
|
557
|
+
for (let i = 0; i < oldScript.attributes.length; i++) {
|
|
558
|
+
const attr = oldScript.attributes[i];
|
|
559
|
+
if (attr.name === "type") continue;
|
|
560
|
+
if (attr.name === "data-src") {
|
|
561
|
+
newScript.src = attr.value;
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
newScript.setAttribute(attr.name, attr.value);
|
|
565
|
+
}
|
|
566
|
+
newScript.type = "text/javascript";
|
|
567
|
+
if (!newScript.src && oldScript.textContent) {
|
|
568
|
+
newScript.textContent = oldScript.textContent;
|
|
569
|
+
}
|
|
570
|
+
(_a = oldScript.parentNode) == null ? void 0 : _a.replaceChild(newScript, oldScript);
|
|
571
|
+
if (serviceId || category) {
|
|
572
|
+
onServiceLoaded == null ? void 0 : onServiceLoaded(serviceId || category, category || "custom");
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
ScriptGate.executedScripts = /* @__PURE__ */ new Set();
|
|
578
|
+
|
|
579
|
+
// src/blocker/iframe-gate.ts
|
|
580
|
+
var IframeResourceBlocker = class {
|
|
581
|
+
constructor() {
|
|
582
|
+
this.type = "iframe";
|
|
583
|
+
}
|
|
584
|
+
process(isCategoryAllowed, isServiceAllowed) {
|
|
585
|
+
IframeGate.processIframes(isCategoryAllowed, isServiceAllowed);
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
var IframeGate = class {
|
|
589
|
+
static processIframes(isCategoryAllowed, isServiceAllowed, options = {}) {
|
|
590
|
+
if (typeof document === "undefined") return;
|
|
591
|
+
const iframes = Array.from(
|
|
592
|
+
document.querySelectorAll(
|
|
593
|
+
"iframe[data-consent], iframe[data-service]"
|
|
594
|
+
)
|
|
595
|
+
);
|
|
596
|
+
for (const iframe of iframes) {
|
|
597
|
+
const category = iframe.getAttribute("data-consent");
|
|
598
|
+
const serviceId = iframe.getAttribute("data-service");
|
|
599
|
+
let isAllowed = false;
|
|
600
|
+
if (serviceId) {
|
|
601
|
+
isAllowed = isServiceAllowed(serviceId);
|
|
602
|
+
} else if (category) {
|
|
603
|
+
isAllowed = isCategoryAllowed(category);
|
|
604
|
+
}
|
|
605
|
+
const originalSrc = iframe.getAttribute("data-src") || iframe.getAttribute("src");
|
|
606
|
+
if (!originalSrc) continue;
|
|
607
|
+
if (!iframe.getAttribute("data-src")) {
|
|
608
|
+
iframe.setAttribute("data-src", originalSrc);
|
|
609
|
+
}
|
|
610
|
+
if (isAllowed) {
|
|
611
|
+
this.unlockIframe(iframe, originalSrc);
|
|
612
|
+
} else {
|
|
613
|
+
this.lockIframe(
|
|
614
|
+
iframe,
|
|
615
|
+
category || "marketing",
|
|
616
|
+
serviceId || void 0,
|
|
617
|
+
options
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
static lockIframe(iframe, category, serviceId, options) {
|
|
623
|
+
var _a;
|
|
624
|
+
if (iframe.hasAttribute("src")) {
|
|
625
|
+
iframe.removeAttribute("src");
|
|
626
|
+
}
|
|
627
|
+
if (this.placeholders.has(iframe)) return;
|
|
628
|
+
const container = document.createElement("div");
|
|
629
|
+
container.className = "consent-iframe-placeholder";
|
|
630
|
+
container.style.cssText = `
|
|
631
|
+
width: ${iframe.width ? iframe.width.endsWith("%") || iframe.width.endsWith("px") ? iframe.width : iframe.width + "px" : "100%"};
|
|
632
|
+
height: ${iframe.height ? iframe.height.endsWith("%") || iframe.height.endsWith("px") ? iframe.height : iframe.height + "px" : "315px"};
|
|
633
|
+
min-height: 200px;
|
|
634
|
+
background: #0f172a;
|
|
635
|
+
color: #f8fafc;
|
|
636
|
+
display: flex;
|
|
637
|
+
flex-direction: column;
|
|
638
|
+
align-items: center;
|
|
639
|
+
justify-content: center;
|
|
640
|
+
text-align: center;
|
|
641
|
+
padding: 1.5rem;
|
|
642
|
+
border-radius: 8px;
|
|
643
|
+
box-sizing: border-box;
|
|
644
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
645
|
+
`;
|
|
646
|
+
const titleText = serviceId ? `Contenido de ${serviceId} bloqueado` : "Contenido externo bloqueado";
|
|
647
|
+
const bodyText = "Este contenido est\xE1 bloqueado hasta que autorices esta finalidad de privacidad.";
|
|
648
|
+
container.innerHTML = `
|
|
649
|
+
<div style="font-weight: 600; font-size: 1.1rem; margin-bottom: 0.5rem; color: #f8fafc;">
|
|
650
|
+
${titleText}
|
|
651
|
+
</div>
|
|
652
|
+
<p style="font-size: 0.9rem; color: #94a3b8; margin: 0 0 1rem 0; max-width: 400px;">
|
|
653
|
+
${bodyText}
|
|
654
|
+
</p>
|
|
655
|
+
<button type="button" class="consent-placeholder-allow-btn" style="
|
|
656
|
+
background: #2563eb;
|
|
657
|
+
color: #ffffff;
|
|
658
|
+
border: none;
|
|
659
|
+
padding: 0.6rem 1.2rem;
|
|
660
|
+
border-radius: 6px;
|
|
661
|
+
font-weight: 500;
|
|
662
|
+
cursor: pointer;
|
|
663
|
+
font-size: 0.9rem;
|
|
664
|
+
transition: background 0.2s ease;
|
|
665
|
+
">
|
|
666
|
+
Permitir y mostrar contenido
|
|
667
|
+
</button>
|
|
668
|
+
`;
|
|
669
|
+
const button = container.querySelector(
|
|
670
|
+
".consent-placeholder-allow-btn"
|
|
671
|
+
);
|
|
672
|
+
button == null ? void 0 : button.addEventListener("click", () => {
|
|
673
|
+
var _a2;
|
|
674
|
+
(_a2 = options.onAllowClick) == null ? void 0 : _a2.call(options, category, serviceId);
|
|
675
|
+
});
|
|
676
|
+
iframe.style.display = "none";
|
|
677
|
+
(_a = iframe.parentNode) == null ? void 0 : _a.insertBefore(container, iframe);
|
|
678
|
+
this.placeholders.set(iframe, container);
|
|
679
|
+
}
|
|
680
|
+
static unlockIframe(iframe, src) {
|
|
681
|
+
var _a;
|
|
682
|
+
if (iframe.getAttribute("src") !== src) {
|
|
683
|
+
iframe.setAttribute("src", src);
|
|
684
|
+
}
|
|
685
|
+
iframe.style.display = "";
|
|
686
|
+
const placeholder = this.placeholders.get(iframe);
|
|
687
|
+
if (placeholder) {
|
|
688
|
+
(_a = placeholder.parentNode) == null ? void 0 : _a.removeChild(placeholder);
|
|
689
|
+
this.placeholders.delete(iframe);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
IframeGate.placeholders = /* @__PURE__ */ new Map();
|
|
694
|
+
|
|
695
|
+
// src/blocker/resource-gate.ts
|
|
696
|
+
var ImageResourceBlocker = class {
|
|
697
|
+
constructor() {
|
|
698
|
+
this.type = "image";
|
|
699
|
+
}
|
|
700
|
+
process(isCategoryAllowed, isServiceAllowed) {
|
|
701
|
+
ResourceGate.processImages(isCategoryAllowed, isServiceAllowed);
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
var ResourceGate = class {
|
|
705
|
+
static processImages(isCategoryAllowed, isServiceAllowed) {
|
|
706
|
+
if (typeof document === "undefined") return;
|
|
707
|
+
const images = Array.from(
|
|
708
|
+
document.querySelectorAll(
|
|
709
|
+
"img[data-consent], img[data-service]"
|
|
710
|
+
)
|
|
711
|
+
);
|
|
712
|
+
for (const img of images) {
|
|
713
|
+
const category = img.getAttribute("data-consent");
|
|
714
|
+
const serviceId = img.getAttribute("data-service");
|
|
715
|
+
let isAllowed = false;
|
|
716
|
+
if (serviceId) {
|
|
717
|
+
isAllowed = isServiceAllowed(serviceId);
|
|
718
|
+
} else if (category) {
|
|
719
|
+
isAllowed = isCategoryAllowed(category);
|
|
720
|
+
}
|
|
721
|
+
const originalSrc = img.getAttribute("data-src") || img.getAttribute("src");
|
|
722
|
+
if (!originalSrc) continue;
|
|
723
|
+
if (!img.getAttribute("data-src")) {
|
|
724
|
+
img.setAttribute("data-src", originalSrc);
|
|
725
|
+
}
|
|
726
|
+
if (isAllowed) {
|
|
727
|
+
if (img.getAttribute("src") !== originalSrc) {
|
|
728
|
+
img.setAttribute("src", originalSrc);
|
|
729
|
+
}
|
|
730
|
+
} else {
|
|
731
|
+
if (img.hasAttribute("src")) {
|
|
732
|
+
img.removeAttribute("src");
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
// src/services/google.ts
|
|
740
|
+
var GoogleConsentAdapter = class {
|
|
741
|
+
static initDefault(_configMap) {
|
|
742
|
+
if (typeof window === "undefined") return;
|
|
743
|
+
window.dataLayer = window.dataLayer || [];
|
|
744
|
+
if (!window.gtag) {
|
|
745
|
+
window.gtag = function() {
|
|
746
|
+
var _a;
|
|
747
|
+
(_a = window.dataLayer) == null ? void 0 : _a.push(arguments);
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
if (!this.initialized) {
|
|
751
|
+
window.gtag("consent", "default", {
|
|
752
|
+
analytics_storage: "denied",
|
|
753
|
+
ad_storage: "denied",
|
|
754
|
+
ad_user_data: "denied",
|
|
755
|
+
ad_personalization: "denied",
|
|
756
|
+
wait_for_update: 500
|
|
757
|
+
});
|
|
758
|
+
this.initialized = true;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
static update(choices, configMap) {
|
|
762
|
+
if (typeof window === "undefined") return;
|
|
763
|
+
this.initDefault(configMap);
|
|
764
|
+
const analyticsCategory = (configMap == null ? void 0 : configMap.analytics_storage) || "analytics";
|
|
765
|
+
const marketingCategory = (configMap == null ? void 0 : configMap.ad_storage) || "marketing";
|
|
766
|
+
const isAnalyticsAllowed = choices[analyticsCategory] === true;
|
|
767
|
+
const isMarketingAllowed = choices[marketingCategory] === true;
|
|
768
|
+
window.gtag("consent", "update", {
|
|
769
|
+
analytics_storage: isAnalyticsAllowed ? "granted" : "denied",
|
|
770
|
+
ad_storage: isMarketingAllowed ? "granted" : "denied",
|
|
771
|
+
ad_user_data: isMarketingAllowed ? "granted" : "denied",
|
|
772
|
+
ad_personalization: isMarketingAllowed ? "granted" : "denied"
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
GoogleConsentAdapter.initialized = false;
|
|
777
|
+
|
|
778
|
+
// src/ui/styles.ts
|
|
779
|
+
function injectStyles(nonce) {
|
|
780
|
+
if (typeof document === "undefined") return;
|
|
781
|
+
const existing = document.getElementById("consent-sdk-styles");
|
|
782
|
+
if (existing) {
|
|
783
|
+
existing.remove();
|
|
784
|
+
}
|
|
785
|
+
const styleEl = document.createElement("style");
|
|
786
|
+
styleEl.id = "consent-sdk-styles";
|
|
787
|
+
if (nonce) {
|
|
788
|
+
styleEl.setAttribute("nonce", nonce);
|
|
789
|
+
}
|
|
790
|
+
styleEl.textContent = `
|
|
791
|
+
:root {
|
|
792
|
+
--consent-bg: #ffffff;
|
|
793
|
+
--consent-fg: #0f172a;
|
|
794
|
+
--consent-muted: #64748b;
|
|
795
|
+
--consent-divider: #f1f5f9;
|
|
796
|
+
--consent-border: #e2e8f0;
|
|
797
|
+
--consent-hover-bg: #f8fafc;
|
|
798
|
+
--consent-primary: #0f172a;
|
|
799
|
+
--consent-primary-hover: #1e293b;
|
|
800
|
+
--consent-primary-fg: #ffffff;
|
|
801
|
+
--consent-secondary: #f8fafc;
|
|
802
|
+
--consent-secondary-hover: #f1f5f9;
|
|
803
|
+
--consent-secondary-fg: #0f172a;
|
|
804
|
+
--consent-accent: #2563eb;
|
|
805
|
+
--consent-focus-ring: rgba(37, 99, 235, 0.3);
|
|
806
|
+
--consent-font: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Segoe UI", Roboto, sans-serif;
|
|
807
|
+
--consent-modal-shadow: 0 32px 64px -16px rgba(15, 23, 42, 0.2), 0 0 1px rgba(15, 23, 42, 0.12);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
@media (prefers-color-scheme: dark) {
|
|
811
|
+
:root {
|
|
812
|
+
--consent-bg: #0f172a;
|
|
813
|
+
--consent-fg: #f8fafc;
|
|
814
|
+
--consent-muted: #94a3b8;
|
|
815
|
+
--consent-divider: #1e293b;
|
|
816
|
+
--consent-border: #334155;
|
|
817
|
+
--consent-hover-bg: #1e293b;
|
|
818
|
+
--consent-primary: #f8fafc;
|
|
819
|
+
--consent-primary-hover: #ffffff;
|
|
820
|
+
--consent-primary-fg: #0f172a;
|
|
821
|
+
--consent-secondary: #1e293b;
|
|
822
|
+
--consent-secondary-hover: #334155;
|
|
823
|
+
--consent-secondary-fg: #f8fafc;
|
|
824
|
+
--consent-modal-shadow: 0 32px 64px -16px rgba(0, 0, 0, 0.6), 0 0 1px rgba(255, 255, 255, 0.12);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
@keyframes consentSlideUp {
|
|
829
|
+
from {
|
|
830
|
+
opacity: 0;
|
|
831
|
+
transform: translateY(16px) scale(0.985);
|
|
832
|
+
}
|
|
833
|
+
to {
|
|
834
|
+
opacity: 1;
|
|
835
|
+
transform: translateY(0) scale(1);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
@keyframes consentFadeIn {
|
|
840
|
+
from { opacity: 0; }
|
|
841
|
+
to { opacity: 1; }
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/* 1st Layer Banner - Clean & Seamless */
|
|
845
|
+
.consent-banner-wrapper {
|
|
846
|
+
position: fixed;
|
|
847
|
+
bottom: 1.25rem;
|
|
848
|
+
left: 1.25rem;
|
|
849
|
+
right: 1.25rem;
|
|
850
|
+
max-width: 1060px;
|
|
851
|
+
margin: 0 auto;
|
|
852
|
+
z-index: 2147483645;
|
|
853
|
+
background: var(--consent-bg);
|
|
854
|
+
color: var(--consent-fg);
|
|
855
|
+
border: 1px solid var(--consent-border);
|
|
856
|
+
border-radius: 20px;
|
|
857
|
+
box-shadow: var(--consent-modal-shadow);
|
|
858
|
+
padding: 1.5rem 1.75rem;
|
|
859
|
+
font-family: var(--consent-font);
|
|
860
|
+
font-size: 0.92rem;
|
|
861
|
+
line-height: 1.5;
|
|
862
|
+
animation: consentSlideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
|
863
|
+
box-sizing: border-box;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
.consent-banner-container {
|
|
867
|
+
display: flex;
|
|
868
|
+
flex-direction: column;
|
|
869
|
+
gap: 1.25rem;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
@media (min-width: 860px) {
|
|
873
|
+
.consent-banner-container {
|
|
874
|
+
flex-direction: row;
|
|
875
|
+
align-items: center;
|
|
876
|
+
justify-content: space-between;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
.consent-banner-text {
|
|
881
|
+
flex: 1;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
.consent-banner-title {
|
|
885
|
+
font-size: 1.1rem;
|
|
886
|
+
font-weight: 700;
|
|
887
|
+
letter-spacing: -0.015em;
|
|
888
|
+
margin: 0 0 0.35rem 0;
|
|
889
|
+
color: var(--consent-fg);
|
|
890
|
+
display: flex;
|
|
891
|
+
align-items: center;
|
|
892
|
+
gap: 0.5rem;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
.consent-banner-title-icon {
|
|
896
|
+
display: inline-flex;
|
|
897
|
+
align-items: center;
|
|
898
|
+
justify-content: center;
|
|
899
|
+
width: 22px;
|
|
900
|
+
height: 22px;
|
|
901
|
+
border-radius: 6px;
|
|
902
|
+
background: rgba(37, 99, 235, 0.1);
|
|
903
|
+
color: var(--consent-accent);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
.consent-banner-desc {
|
|
907
|
+
margin: 0;
|
|
908
|
+
color: var(--consent-muted);
|
|
909
|
+
font-size: 0.88rem;
|
|
910
|
+
max-width: 720px;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
.consent-banner-links {
|
|
914
|
+
margin-top: 0.4rem;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
.consent-banner-links a {
|
|
918
|
+
color: var(--consent-muted);
|
|
919
|
+
font-size: 0.83rem;
|
|
920
|
+
text-decoration: underline;
|
|
921
|
+
text-underline-offset: 3px;
|
|
922
|
+
font-weight: 500;
|
|
923
|
+
margin-right: 1.2rem;
|
|
924
|
+
transition: color 0.15s ease;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
.consent-banner-links a:hover {
|
|
928
|
+
color: var(--consent-fg);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/* Equal Visual Prominence CTAs */
|
|
932
|
+
.consent-banner-actions {
|
|
933
|
+
display: flex;
|
|
934
|
+
flex-direction: column;
|
|
935
|
+
gap: 0.5rem;
|
|
936
|
+
width: 100%;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
@media (min-width: 640px) {
|
|
940
|
+
.consent-banner-actions {
|
|
941
|
+
flex-direction: row;
|
|
942
|
+
width: auto;
|
|
943
|
+
align-items: center;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
.consent-btn {
|
|
948
|
+
display: inline-flex;
|
|
949
|
+
align-items: center;
|
|
950
|
+
justify-content: center;
|
|
951
|
+
padding: 0.6rem 1.2rem;
|
|
952
|
+
font-size: 0.88rem;
|
|
953
|
+
font-weight: 600;
|
|
954
|
+
border-radius: 10px;
|
|
955
|
+
border: 1px solid var(--consent-border);
|
|
956
|
+
cursor: pointer;
|
|
957
|
+
font-family: inherit;
|
|
958
|
+
transition: all 0.18s cubic-bezier(0.16, 1, 0.3, 1);
|
|
959
|
+
min-height: 42px;
|
|
960
|
+
min-width: 110px;
|
|
961
|
+
box-sizing: border-box;
|
|
962
|
+
user-select: none;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
.consent-btn:hover {
|
|
966
|
+
transform: translateY(-1px);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
.consent-btn:active {
|
|
970
|
+
transform: translateY(0) scale(0.98);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
.consent-btn:focus-visible {
|
|
974
|
+
outline: 3px solid var(--consent-focus-ring);
|
|
975
|
+
outline-offset: 2px;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
.consent-btn-reject {
|
|
979
|
+
background: var(--consent-secondary);
|
|
980
|
+
color: var(--consent-secondary-fg);
|
|
981
|
+
border-color: var(--consent-border);
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
.consent-btn-reject:hover {
|
|
985
|
+
background: var(--consent-secondary-hover);
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
.consent-btn-configure {
|
|
989
|
+
background: transparent;
|
|
990
|
+
color: var(--consent-muted);
|
|
991
|
+
border-color: transparent;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
.consent-btn-configure:hover {
|
|
995
|
+
color: var(--consent-fg);
|
|
996
|
+
background: var(--consent-secondary);
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
.consent-btn-accept {
|
|
1000
|
+
background: var(--consent-primary);
|
|
1001
|
+
color: var(--consent-primary-fg);
|
|
1002
|
+
border-color: var(--consent-primary);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
.consent-btn-accept:hover {
|
|
1006
|
+
background: var(--consent-primary-hover);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/* 2nd Layer Preference Modal - Ultra Clean Linear List Style */
|
|
1010
|
+
.consent-dialog-backdrop {
|
|
1011
|
+
position: fixed;
|
|
1012
|
+
top: 0;
|
|
1013
|
+
left: 0;
|
|
1014
|
+
width: 100vw;
|
|
1015
|
+
height: 100vh;
|
|
1016
|
+
background: rgba(15, 23, 42, 0.45);
|
|
1017
|
+
backdrop-filter: blur(8px);
|
|
1018
|
+
-webkit-backdrop-filter: blur(8px);
|
|
1019
|
+
z-index: 2147483646;
|
|
1020
|
+
display: flex;
|
|
1021
|
+
align-items: center;
|
|
1022
|
+
justify-content: center;
|
|
1023
|
+
padding: 1.25rem;
|
|
1024
|
+
box-sizing: border-box;
|
|
1025
|
+
animation: consentFadeIn 0.2s ease forwards;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
.consent-dialog {
|
|
1029
|
+
background: var(--consent-bg);
|
|
1030
|
+
color: var(--consent-fg);
|
|
1031
|
+
border: 1px solid var(--consent-border);
|
|
1032
|
+
border-radius: 20px;
|
|
1033
|
+
width: 100%;
|
|
1034
|
+
max-width: 600px;
|
|
1035
|
+
max-height: 85vh;
|
|
1036
|
+
display: flex;
|
|
1037
|
+
flex-direction: column;
|
|
1038
|
+
box-shadow: var(--consent-modal-shadow);
|
|
1039
|
+
font-family: var(--consent-font);
|
|
1040
|
+
overflow: hidden;
|
|
1041
|
+
animation: consentSlideUp 0.25s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
.consent-dialog-header {
|
|
1045
|
+
padding: 1.5rem 1.75rem 1.25rem 1.75rem;
|
|
1046
|
+
border-bottom: 1px solid var(--consent-divider);
|
|
1047
|
+
display: flex;
|
|
1048
|
+
justify-content: space-between;
|
|
1049
|
+
align-items: flex-start;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
.consent-dialog-title {
|
|
1053
|
+
font-size: 1.15rem;
|
|
1054
|
+
font-weight: 700;
|
|
1055
|
+
letter-spacing: -0.015em;
|
|
1056
|
+
margin: 0;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
.consent-dialog-close {
|
|
1060
|
+
background: transparent;
|
|
1061
|
+
border: none;
|
|
1062
|
+
color: var(--consent-muted);
|
|
1063
|
+
cursor: pointer;
|
|
1064
|
+
padding: 0.35rem;
|
|
1065
|
+
border-radius: 8px;
|
|
1066
|
+
display: flex;
|
|
1067
|
+
align-items: center;
|
|
1068
|
+
justify-content: center;
|
|
1069
|
+
transition: background 0.15s ease, color 0.15s ease;
|
|
1070
|
+
margin: -0.35rem -0.35rem 0 0;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
.consent-dialog-close:hover {
|
|
1074
|
+
background: var(--consent-secondary);
|
|
1075
|
+
color: var(--consent-fg);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/* Seamless Category List Body (NO individual boxed divs) */
|
|
1079
|
+
.consent-dialog-body {
|
|
1080
|
+
padding: 0;
|
|
1081
|
+
overflow-y: auto;
|
|
1082
|
+
display: flex;
|
|
1083
|
+
flex-direction: column;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
.consent-category-row {
|
|
1087
|
+
padding: 1.25rem 1.75rem;
|
|
1088
|
+
border-bottom: 1px solid var(--consent-divider);
|
|
1089
|
+
transition: background 0.15s ease;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
.consent-category-row:last-child {
|
|
1093
|
+
border-bottom: none;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
.consent-category-row:hover {
|
|
1097
|
+
background: var(--consent-hover-bg);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
.consent-category-header {
|
|
1101
|
+
display: flex;
|
|
1102
|
+
justify-content: space-between;
|
|
1103
|
+
align-items: flex-start;
|
|
1104
|
+
gap: 1rem;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
.consent-category-name {
|
|
1108
|
+
font-weight: 600;
|
|
1109
|
+
font-size: 0.95rem;
|
|
1110
|
+
color: var(--consent-fg);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
.consent-category-desc {
|
|
1114
|
+
margin: 0.3rem 0 0 0;
|
|
1115
|
+
font-size: 0.85rem;
|
|
1116
|
+
color: var(--consent-muted);
|
|
1117
|
+
line-height: 1.45;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/* Minimalist Badges */
|
|
1121
|
+
.consent-badge {
|
|
1122
|
+
display: inline-flex;
|
|
1123
|
+
align-items: center;
|
|
1124
|
+
padding: 0.15rem 0.5rem;
|
|
1125
|
+
border-radius: 6px;
|
|
1126
|
+
font-size: 0.7rem;
|
|
1127
|
+
font-weight: 600;
|
|
1128
|
+
letter-spacing: 0.01em;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
.consent-badge-required {
|
|
1132
|
+
background: rgba(16, 185, 129, 0.1);
|
|
1133
|
+
color: #059669;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
.consent-badge-optional {
|
|
1137
|
+
background: rgba(100, 116, 139, 0.08);
|
|
1138
|
+
color: var(--consent-muted);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
/* Inline Service Disclosure Button */
|
|
1142
|
+
.consent-toggle-services-btn {
|
|
1143
|
+
border: none;
|
|
1144
|
+
background: transparent;
|
|
1145
|
+
color: var(--consent-accent);
|
|
1146
|
+
font-size: 0.78rem;
|
|
1147
|
+
font-weight: 600;
|
|
1148
|
+
cursor: pointer;
|
|
1149
|
+
padding: 0.35rem 0 0 0;
|
|
1150
|
+
display: inline-flex;
|
|
1151
|
+
align-items: center;
|
|
1152
|
+
gap: 0.25rem;
|
|
1153
|
+
font-family: inherit;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
.consent-toggle-services-btn:hover {
|
|
1157
|
+
opacity: 0.85;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
/* Minimalist Toggle Switch */
|
|
1161
|
+
.consent-toggle {
|
|
1162
|
+
position: relative;
|
|
1163
|
+
display: inline-block;
|
|
1164
|
+
width: 40px;
|
|
1165
|
+
height: 22px;
|
|
1166
|
+
flex-shrink: 0;
|
|
1167
|
+
margin-top: 0.15rem;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
.consent-toggle input {
|
|
1171
|
+
opacity: 0;
|
|
1172
|
+
width: 0;
|
|
1173
|
+
height: 0;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
.consent-toggle-slider {
|
|
1177
|
+
position: absolute;
|
|
1178
|
+
cursor: pointer;
|
|
1179
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
1180
|
+
background-color: #cbd5e1;
|
|
1181
|
+
transition: background-color 0.2s ease;
|
|
1182
|
+
border-radius: 22px;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
.consent-toggle-slider:before {
|
|
1186
|
+
position: absolute;
|
|
1187
|
+
content: "";
|
|
1188
|
+
height: 16px;
|
|
1189
|
+
width: 16px;
|
|
1190
|
+
left: 3px;
|
|
1191
|
+
bottom: 3px;
|
|
1192
|
+
background-color: white;
|
|
1193
|
+
transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1194
|
+
border-radius: 50%;
|
|
1195
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
.consent-toggle input:checked + .consent-toggle-slider {
|
|
1199
|
+
background-color: var(--consent-accent);
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
.consent-toggle input:checked + .consent-toggle-slider:before {
|
|
1203
|
+
transform: translateX(18px);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
.consent-toggle input:disabled + .consent-toggle-slider {
|
|
1207
|
+
opacity: 0.45;
|
|
1208
|
+
cursor: not-allowed;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
/* Footer */
|
|
1212
|
+
.consent-dialog-footer {
|
|
1213
|
+
padding: 1.25rem 1.75rem;
|
|
1214
|
+
border-top: 1px solid var(--consent-divider);
|
|
1215
|
+
background: var(--consent-bg);
|
|
1216
|
+
display: flex;
|
|
1217
|
+
flex-direction: column;
|
|
1218
|
+
gap: 0.6rem;
|
|
1219
|
+
justify-content: space-between;
|
|
1220
|
+
align-items: center;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
@media (min-width: 520px) {
|
|
1224
|
+
.consent-dialog-footer {
|
|
1225
|
+
flex-direction: row;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
/* Floating Revocation & Preference Badge */
|
|
1230
|
+
.consent-floating-badge {
|
|
1231
|
+
position: fixed;
|
|
1232
|
+
z-index: 2147483640;
|
|
1233
|
+
font-family: var(--consent-font);
|
|
1234
|
+
opacity: 0;
|
|
1235
|
+
transform: scale(0.85);
|
|
1236
|
+
pointer-events: none;
|
|
1237
|
+
transition: opacity 0.28s cubic-bezier(0.16, 1, 0.3, 1), transform 0.28s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
.consent-floating-badge.is-visible {
|
|
1241
|
+
opacity: 1;
|
|
1242
|
+
transform: scale(1);
|
|
1243
|
+
pointer-events: auto;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
.consent-floating-badge--bottom-left {
|
|
1247
|
+
bottom: 1.25rem;
|
|
1248
|
+
left: 1.25rem;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
.consent-floating-badge--bottom-right {
|
|
1252
|
+
bottom: 1.25rem;
|
|
1253
|
+
right: 1.25rem;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
.consent-floating-badge--top-left {
|
|
1257
|
+
top: 1.25rem;
|
|
1258
|
+
left: 1.25rem;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
.consent-floating-badge--top-right {
|
|
1262
|
+
top: 1.25rem;
|
|
1263
|
+
right: 1.25rem;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
.consent-floating-badge-inner {
|
|
1267
|
+
position: relative;
|
|
1268
|
+
display: inline-flex;
|
|
1269
|
+
align-items: center;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
.consent-floating-badge-btn {
|
|
1273
|
+
display: inline-flex;
|
|
1274
|
+
align-items: center;
|
|
1275
|
+
justify-content: center;
|
|
1276
|
+
gap: 0.5rem;
|
|
1277
|
+
width: 46px;
|
|
1278
|
+
height: 46px;
|
|
1279
|
+
min-width: 46px;
|
|
1280
|
+
border-radius: 9999px;
|
|
1281
|
+
background: var(--consent-bg);
|
|
1282
|
+
color: var(--consent-fg);
|
|
1283
|
+
border: 1px solid var(--consent-border);
|
|
1284
|
+
box-shadow: 0 10px 25px -5px rgba(15, 23, 42, 0.15), 0 4px 6px -2px rgba(15, 23, 42, 0.05);
|
|
1285
|
+
cursor: pointer;
|
|
1286
|
+
padding: 0;
|
|
1287
|
+
margin: 0;
|
|
1288
|
+
transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.2s ease, border-color 0.2s ease, color 0.2s ease;
|
|
1289
|
+
outline: none;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
.consent-floating-badge-btn.has-label {
|
|
1293
|
+
width: auto;
|
|
1294
|
+
padding: 0 1rem;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
.consent-floating-badge-btn:hover {
|
|
1298
|
+
transform: translateY(-2px);
|
|
1299
|
+
box-shadow: 0 14px 28px -6px rgba(15, 23, 42, 0.2), 0 6px 10px -3px rgba(15, 23, 42, 0.08);
|
|
1300
|
+
border-color: var(--consent-accent);
|
|
1301
|
+
color: var(--consent-accent);
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
.consent-floating-badge-btn:active {
|
|
1305
|
+
transform: translateY(0) scale(0.96);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
.consent-floating-badge-btn:focus-visible {
|
|
1309
|
+
box-shadow: 0 0 0 3px var(--consent-focus-ring), 0 10px 25px -5px rgba(15, 23, 42, 0.15);
|
|
1310
|
+
border-color: var(--consent-accent);
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
.consent-floating-badge-icon {
|
|
1314
|
+
display: inline-flex;
|
|
1315
|
+
align-items: center;
|
|
1316
|
+
justify-content: center;
|
|
1317
|
+
width: 22px;
|
|
1318
|
+
height: 22px;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
.consent-floating-badge-text {
|
|
1322
|
+
font-size: 0.85rem;
|
|
1323
|
+
font-weight: 600;
|
|
1324
|
+
white-space: nowrap;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
.consent-floating-badge-tooltip {
|
|
1328
|
+
position: absolute;
|
|
1329
|
+
white-space: nowrap;
|
|
1330
|
+
pointer-events: none;
|
|
1331
|
+
opacity: 0;
|
|
1332
|
+
visibility: hidden;
|
|
1333
|
+
background: var(--consent-primary);
|
|
1334
|
+
color: var(--consent-primary-fg);
|
|
1335
|
+
font-size: 0.76rem;
|
|
1336
|
+
font-weight: 500;
|
|
1337
|
+
padding: 0.4rem 0.75rem;
|
|
1338
|
+
border-radius: 8px;
|
|
1339
|
+
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.18);
|
|
1340
|
+
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s;
|
|
1341
|
+
z-index: 2147483641;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
.consent-floating-badge--bottom-left .consent-floating-badge-tooltip {
|
|
1345
|
+
bottom: calc(100% + 8px);
|
|
1346
|
+
left: 0;
|
|
1347
|
+
transform: translateY(4px);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
.consent-floating-badge--bottom-right .consent-floating-badge-tooltip {
|
|
1351
|
+
bottom: calc(100% + 8px);
|
|
1352
|
+
right: 0;
|
|
1353
|
+
transform: translateY(4px);
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
.consent-floating-badge--top-left .consent-floating-badge-tooltip {
|
|
1357
|
+
top: calc(100% + 8px);
|
|
1358
|
+
left: 0;
|
|
1359
|
+
transform: translateY(-4px);
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
.consent-floating-badge--top-right .consent-floating-badge-tooltip {
|
|
1363
|
+
top: calc(100% + 8px);
|
|
1364
|
+
right: 0;
|
|
1365
|
+
transform: translateY(-4px);
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
.consent-floating-badge-inner:hover .consent-floating-badge-tooltip,
|
|
1369
|
+
.consent-floating-badge-btn:focus-visible + .consent-floating-badge-tooltip {
|
|
1370
|
+
opacity: 1;
|
|
1371
|
+
visibility: visible;
|
|
1372
|
+
transform: translateY(0);
|
|
1373
|
+
}
|
|
1374
|
+
`;
|
|
1375
|
+
document.head.appendChild(styleEl);
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// src/ui/floating-badge.ts
|
|
1379
|
+
var FloatingBadge = class {
|
|
1380
|
+
constructor() {
|
|
1381
|
+
this.element = null;
|
|
1382
|
+
this.isVisible = false;
|
|
1383
|
+
}
|
|
1384
|
+
getIconSvg(icon) {
|
|
1385
|
+
switch (icon) {
|
|
1386
|
+
case "shield":
|
|
1387
|
+
return `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
1388
|
+
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
|
1389
|
+
<path d="m9 12 2 2 4-4"/>
|
|
1390
|
+
</svg>`;
|
|
1391
|
+
case "settings":
|
|
1392
|
+
return `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
1393
|
+
<circle cx="12" cy="12" r="3"/>
|
|
1394
|
+
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
|
1395
|
+
</svg>`;
|
|
1396
|
+
case "cookie":
|
|
1397
|
+
default:
|
|
1398
|
+
return `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
1399
|
+
<path d="M12 2a10 10 0 1 0 10 10 4 4 0 0 1-5-5 4 4 0 0 1-5-5"/>
|
|
1400
|
+
<path d="M8.5 8.5v.01"/>
|
|
1401
|
+
<path d="M16 15.5v.01"/>
|
|
1402
|
+
<path d="M12 12v.01"/>
|
|
1403
|
+
<path d="M11 17v.01"/>
|
|
1404
|
+
<path d="M7 13v.01"/>
|
|
1405
|
+
</svg>`;
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
render(config, handlers) {
|
|
1409
|
+
var _a, _b, _c;
|
|
1410
|
+
if (typeof document === "undefined") return;
|
|
1411
|
+
this.remove();
|
|
1412
|
+
injectStyles((_a = config.csp) == null ? void 0 : _a.nonce);
|
|
1413
|
+
const badgeConfig = typeof ((_b = config.ui) == null ? void 0 : _b.floatingBadge) === "object" && ((_c = config.ui) == null ? void 0 : _c.floatingBadge) !== null ? config.ui.floatingBadge : {};
|
|
1414
|
+
const position = badgeConfig.position || "bottom-left";
|
|
1415
|
+
const labelText = badgeConfig.label ? sanitizeHtml(badgeConfig.label) : "";
|
|
1416
|
+
const ariaLabel = badgeConfig.ariaLabel ? sanitizeHtml(badgeConfig.ariaLabel) : labelText || "Configuraci\xF3n y revocaci\xF3n de cookies";
|
|
1417
|
+
const tooltipText = badgeConfig.tooltip ? sanitizeHtml(badgeConfig.tooltip) : labelText || "Configurar o declinar cookies";
|
|
1418
|
+
const showLabel = badgeConfig.showLabel === true && !!labelText;
|
|
1419
|
+
const iconSvg = this.getIconSvg(badgeConfig.icon);
|
|
1420
|
+
const container = document.createElement("div");
|
|
1421
|
+
container.className = `consent-floating-badge consent-floating-badge--${position}`;
|
|
1422
|
+
container.setAttribute("role", "complementary");
|
|
1423
|
+
container.setAttribute("aria-label", ariaLabel);
|
|
1424
|
+
container.innerHTML = `
|
|
1425
|
+
<div class="consent-floating-badge-inner">
|
|
1426
|
+
<button
|
|
1427
|
+
type="button"
|
|
1428
|
+
class="consent-floating-badge-btn ${showLabel ? "has-label" : ""}"
|
|
1429
|
+
id="consent-floating-trigger"
|
|
1430
|
+
data-consent-open
|
|
1431
|
+
aria-label="${ariaLabel}"
|
|
1432
|
+
>
|
|
1433
|
+
<span class="consent-floating-badge-icon" aria-hidden="true">
|
|
1434
|
+
${iconSvg}
|
|
1435
|
+
</span>
|
|
1436
|
+
${showLabel ? `<span class="consent-floating-badge-text">${labelText}</span>` : ""}
|
|
1437
|
+
</button>
|
|
1438
|
+
<div class="consent-floating-badge-tooltip" role="tooltip" aria-hidden="true">
|
|
1439
|
+
${tooltipText}
|
|
1440
|
+
</div>
|
|
1441
|
+
</div>
|
|
1442
|
+
`;
|
|
1443
|
+
const button = container.querySelector("#consent-floating-trigger");
|
|
1444
|
+
button == null ? void 0 : button.addEventListener("click", (e) => {
|
|
1445
|
+
e.preventDefault();
|
|
1446
|
+
handlers.onClick();
|
|
1447
|
+
});
|
|
1448
|
+
document.body.appendChild(container);
|
|
1449
|
+
this.element = container;
|
|
1450
|
+
}
|
|
1451
|
+
show() {
|
|
1452
|
+
if (this.element) {
|
|
1453
|
+
this.element.classList.add("is-visible");
|
|
1454
|
+
this.isVisible = true;
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
hide() {
|
|
1458
|
+
if (this.element) {
|
|
1459
|
+
this.element.classList.remove("is-visible");
|
|
1460
|
+
this.isVisible = false;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
getIsVisible() {
|
|
1464
|
+
return this.isVisible;
|
|
1465
|
+
}
|
|
1466
|
+
hasElement() {
|
|
1467
|
+
return this.element !== null;
|
|
1468
|
+
}
|
|
1469
|
+
getElement() {
|
|
1470
|
+
return this.element;
|
|
1471
|
+
}
|
|
1472
|
+
remove() {
|
|
1473
|
+
if (this.element && this.element.parentNode) {
|
|
1474
|
+
this.element.parentNode.removeChild(this.element);
|
|
1475
|
+
this.element = null;
|
|
1476
|
+
this.isVisible = false;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
};
|
|
1480
|
+
|
|
1481
|
+
// src/diagnostics/resource-scanner.ts
|
|
1482
|
+
var ResourceScanner = class _ResourceScanner {
|
|
1483
|
+
static runDiagnostic(_config, isConsentGiven) {
|
|
1484
|
+
const thirdPartyScripts = [];
|
|
1485
|
+
if (typeof document !== "undefined") {
|
|
1486
|
+
const scripts = document.querySelectorAll("script[src]");
|
|
1487
|
+
scripts.forEach((script) => {
|
|
1488
|
+
const src = script.getAttribute("src");
|
|
1489
|
+
if (src && (src.startsWith("http://") || src.startsWith("https://"))) {
|
|
1490
|
+
thirdPartyScripts.push(src);
|
|
1491
|
+
}
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
return {
|
|
1495
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1496
|
+
unblockedThirdPartyScripts: isConsentGiven ? [] : thirdPartyScripts,
|
|
1497
|
+
compliant: isConsentGiven || thirdPartyScripts.length === 0
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
scanThirdPartyResources() {
|
|
1501
|
+
return _ResourceScanner.runDiagnostic({}, true).unblockedThirdPartyScripts;
|
|
1502
|
+
}
|
|
1503
|
+
};
|
|
1504
|
+
|
|
1505
|
+
// src/ui/policy-generator.ts
|
|
1506
|
+
var PolicyGenerator = class {
|
|
1507
|
+
/**
|
|
1508
|
+
* Extract all cookie and storage key details across categories and services (including presets).
|
|
1509
|
+
*/
|
|
1510
|
+
static extractCookieRows(config) {
|
|
1511
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
1512
|
+
const rows = [];
|
|
1513
|
+
if (config.services) {
|
|
1514
|
+
for (const [srvId, srv] of Object.entries(config.services)) {
|
|
1515
|
+
const cat = config.categories[srv.category || "necessary"] || { label: srv.category || "General" };
|
|
1516
|
+
const providerName = srv.provider || srv.label || srvId;
|
|
1517
|
+
if (srv.cookies && srv.cookies.length > 0) {
|
|
1518
|
+
for (const c of srv.cookies) {
|
|
1519
|
+
rows.push({
|
|
1520
|
+
name: c.name,
|
|
1521
|
+
type: "Cookie",
|
|
1522
|
+
provider: providerName,
|
|
1523
|
+
purpose: c.purpose || srv.description || cat.description || "Funcionalidad del servicio",
|
|
1524
|
+
duration: c.duration || "Persistente",
|
|
1525
|
+
categoryLabel: cat.label,
|
|
1526
|
+
policyUrl: srv.policyUrl
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
if (srv.storageKeys && srv.storageKeys.length > 0) {
|
|
1531
|
+
for (const k of srv.storageKeys) {
|
|
1532
|
+
rows.push({
|
|
1533
|
+
name: k,
|
|
1534
|
+
type: "localStorage",
|
|
1535
|
+
provider: providerName,
|
|
1536
|
+
purpose: srv.description || cat.description || "Almacenamiento de estado y preferencias",
|
|
1537
|
+
duration: "Persistente",
|
|
1538
|
+
categoryLabel: cat.label,
|
|
1539
|
+
policyUrl: srv.policyUrl
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
if (srv.localStorage && srv.localStorage.length > 0) {
|
|
1544
|
+
for (const k of srv.localStorage) {
|
|
1545
|
+
rows.push({
|
|
1546
|
+
name: k,
|
|
1547
|
+
type: "localStorage",
|
|
1548
|
+
provider: providerName,
|
|
1549
|
+
purpose: srv.description || cat.description || "Almacenamiento web local",
|
|
1550
|
+
duration: "Persistente",
|
|
1551
|
+
categoryLabel: cat.label,
|
|
1552
|
+
policyUrl: srv.policyUrl
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
if (srv.sessionStorage && srv.sessionStorage.length > 0) {
|
|
1557
|
+
for (const k of srv.sessionStorage) {
|
|
1558
|
+
rows.push({
|
|
1559
|
+
name: k,
|
|
1560
|
+
type: "sessionStorage",
|
|
1561
|
+
provider: providerName,
|
|
1562
|
+
purpose: srv.description || cat.description || "Almacenamiento temporal de sesi\xF3n",
|
|
1563
|
+
duration: "Sesi\xF3n",
|
|
1564
|
+
categoryLabel: cat.label,
|
|
1565
|
+
policyUrl: srv.policyUrl
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
if (!((_a = srv.cookies) == null ? void 0 : _a.length) && !((_b = srv.storageKeys) == null ? void 0 : _b.length) && !((_c = srv.localStorage) == null ? void 0 : _c.length) && !((_d = srv.sessionStorage) == null ? void 0 : _d.length)) {
|
|
1570
|
+
rows.push({
|
|
1571
|
+
name: srvId,
|
|
1572
|
+
type: "Cookie",
|
|
1573
|
+
provider: providerName,
|
|
1574
|
+
purpose: srv.description || cat.description || "Servicio integrado",
|
|
1575
|
+
duration: "Variable",
|
|
1576
|
+
categoryLabel: cat.label,
|
|
1577
|
+
policyUrl: srv.policyUrl
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
if (config.categories) {
|
|
1583
|
+
for (const [, cat] of Object.entries(config.categories)) {
|
|
1584
|
+
if (cat.storageKeys) {
|
|
1585
|
+
for (const k of cat.storageKeys) {
|
|
1586
|
+
if (!rows.some((r) => r.name === k)) {
|
|
1587
|
+
rows.push({
|
|
1588
|
+
name: k,
|
|
1589
|
+
type: "localStorage",
|
|
1590
|
+
provider: "Propia / Sitio Web",
|
|
1591
|
+
purpose: cat.description,
|
|
1592
|
+
duration: "Persistente",
|
|
1593
|
+
categoryLabel: cat.label
|
|
1594
|
+
});
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
if (cat.localStorage) {
|
|
1599
|
+
for (const k of cat.localStorage) {
|
|
1600
|
+
if (!rows.some((r) => r.name === k)) {
|
|
1601
|
+
rows.push({
|
|
1602
|
+
name: k,
|
|
1603
|
+
type: "localStorage",
|
|
1604
|
+
provider: "Propia / Sitio Web",
|
|
1605
|
+
purpose: cat.description,
|
|
1606
|
+
duration: "Persistente",
|
|
1607
|
+
categoryLabel: cat.label
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
if (cat.sessionStorage) {
|
|
1613
|
+
for (const k of cat.sessionStorage) {
|
|
1614
|
+
if (!rows.some((r) => r.name === k)) {
|
|
1615
|
+
rows.push({
|
|
1616
|
+
name: k,
|
|
1617
|
+
type: "sessionStorage",
|
|
1618
|
+
provider: "Propia / Sitio Web",
|
|
1619
|
+
purpose: cat.description,
|
|
1620
|
+
duration: "Sesi\xF3n",
|
|
1621
|
+
categoryLabel: cat.label
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
const consentCookieName = ((_e = config.storage) == null ? void 0 : _e.name) || "site_consent";
|
|
1629
|
+
if (!rows.some((r) => r.name === consentCookieName)) {
|
|
1630
|
+
rows.unshift({
|
|
1631
|
+
name: consentCookieName,
|
|
1632
|
+
type: ((_f = config.storage) == null ? void 0 : _f.type) === "memory" ? "localStorage" : "Cookie",
|
|
1633
|
+
provider: "Propia (Solvenza Cookies Compliance)",
|
|
1634
|
+
purpose: "Guarda las preferencias y recibo firmado de consentimiento del usuario.",
|
|
1635
|
+
duration: `${(_h = (_g = config.consent) == null ? void 0 : _g.maxAgeDays) != null ? _h : 365} d\xEDas`,
|
|
1636
|
+
categoryLabel: ((_j = (_i = config.categories) == null ? void 0 : _i.necessary) == null ? void 0 : _j.label) || "Necesarias"
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
return rows;
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1642
|
+
* Render stylized table of cookies and storage keys.
|
|
1643
|
+
*/
|
|
1644
|
+
static renderTable(config, options = {}) {
|
|
1645
|
+
const isDark = options.theme === "dark";
|
|
1646
|
+
const bgHeader = isDark ? "#1e293b" : "#f8fafc";
|
|
1647
|
+
const borderCol = isDark ? "#334155" : "#e2e8f0";
|
|
1648
|
+
const textCol = isDark ? "#f8fafc" : "#0f172a";
|
|
1649
|
+
const mutedCol = isDark ? "#94a3b8" : "#64748b";
|
|
1650
|
+
const rowAltBg = isDark ? "#0f172a" : "#ffffff";
|
|
1651
|
+
const rowBg = isDark ? "#1e293b" : "#f8fafc";
|
|
1652
|
+
let html = `
|
|
1653
|
+
<div class="solvenza-policy-table-wrapper" style="overflow-x: auto; margin: 1.5rem 0; border: 1px solid ${borderCol}; border-radius: 10px; box-shadow: 0 1px 3px rgba(0,0,0,0.05); font-family: system-ui, -apple-system, sans-serif;">
|
|
1654
|
+
<table style="width: 100%; border-collapse: collapse; text-align: left; font-size: 0.88rem; color: ${textCol};">
|
|
1655
|
+
<thead>
|
|
1656
|
+
<tr style="background: ${bgHeader}; border-bottom: 2px solid ${borderCol};">
|
|
1657
|
+
<th style="padding: 0.85rem 1rem; font-weight: 700;">Nombre / Clave</th>
|
|
1658
|
+
<th style="padding: 0.85rem 1rem; font-weight: 700;">Tipo</th>
|
|
1659
|
+
<th style="padding: 0.85rem 1rem; font-weight: 700;">Categor\xEDa</th>
|
|
1660
|
+
<th style="padding: 0.85rem 1rem; font-weight: 700;">Proveedor</th>
|
|
1661
|
+
<th style="padding: 0.85rem 1rem; font-weight: 700;">Finalidad</th>
|
|
1662
|
+
<th style="padding: 0.85rem 1rem; font-weight: 700;">Duraci\xF3n</th>
|
|
1663
|
+
</tr>
|
|
1664
|
+
</thead>
|
|
1665
|
+
<tbody>
|
|
1666
|
+
`;
|
|
1667
|
+
const rows = this.extractCookieRows(config);
|
|
1668
|
+
if (rows.length === 0) {
|
|
1669
|
+
html += `
|
|
1670
|
+
<tr>
|
|
1671
|
+
<td colspan="6" style="padding: 1.5rem; text-align: center; color: ${mutedCol};">
|
|
1672
|
+
No se han registrado cookies ni elementos de almacenamiento en la configuraci\xF3n.
|
|
1673
|
+
</td>
|
|
1674
|
+
</tr>
|
|
1675
|
+
`;
|
|
1676
|
+
} else {
|
|
1677
|
+
rows.forEach((r, idx) => {
|
|
1678
|
+
const bg = idx % 2 === 0 ? rowAltBg : rowBg;
|
|
1679
|
+
const providerHtml = r.policyUrl ? `<a href="${sanitizeHtml(r.policyUrl)}" target="_blank" rel="noopener noreferrer" style="color: #2563eb; text-decoration: underline;">${sanitizeHtml(r.provider)} \u2197</a>` : sanitizeHtml(r.provider);
|
|
1680
|
+
html += `
|
|
1681
|
+
<tr style="background: ${bg}; border-bottom: 1px solid ${borderCol};">
|
|
1682
|
+
<td style="padding: 0.75rem 1rem; font-family: monospace; font-weight: 600; color: #2563eb;">${sanitizeHtml(r.name)}</td>
|
|
1683
|
+
<td style="padding: 0.75rem 1rem;">
|
|
1684
|
+
<span style="display: inline-block; padding: 0.15rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 600; background: ${r.type === "Cookie" ? "#e0e7ff" : "#fef3c7"}; color: ${r.type === "Cookie" ? "#3730a3" : "#92400e"};">
|
|
1685
|
+
${r.type}
|
|
1686
|
+
</span>
|
|
1687
|
+
</td>
|
|
1688
|
+
<td style="padding: 0.75rem 1rem; font-weight: 500;">${sanitizeHtml(r.categoryLabel)}</td>
|
|
1689
|
+
<td style="padding: 0.75rem 1rem;">${providerHtml}</td>
|
|
1690
|
+
<td style="padding: 0.75rem 1rem; color: ${mutedCol};">${sanitizeHtml(r.purpose)}</td>
|
|
1691
|
+
<td style="padding: 0.75rem 1rem; white-space: nowrap;">${sanitizeHtml(r.duration)}</td>
|
|
1692
|
+
</tr>
|
|
1693
|
+
`;
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
html += `
|
|
1697
|
+
</tbody>
|
|
1698
|
+
</table>
|
|
1699
|
+
</div>
|
|
1700
|
+
`;
|
|
1701
|
+
return html;
|
|
1702
|
+
}
|
|
1703
|
+
/**
|
|
1704
|
+
* Render complete legal Cookie Policy document adapted to LSSI art. 22.2 and AEPD 2024.
|
|
1705
|
+
*/
|
|
1706
|
+
static renderCookiePolicy(config, options = {}) {
|
|
1707
|
+
var _a, _b, _c;
|
|
1708
|
+
if (options.view === "table-only") {
|
|
1709
|
+
return this.renderTable(config, options);
|
|
1710
|
+
}
|
|
1711
|
+
const companyName = ((_a = config.legalEntity) == null ? void 0 : _a.tradeName) || ((_b = config.legalEntity) == null ? void 0 : _b.name) || "el Titular del Sitio Web";
|
|
1712
|
+
const lastUpdated = ((_c = config.legalNotice) == null ? void 0 : _c.lastUpdated) || config.policyVersion || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1713
|
+
const isDark = options.theme === "dark";
|
|
1714
|
+
const textCol = isDark ? "#f8fafc" : "#0f172a";
|
|
1715
|
+
const mutedCol = isDark ? "#94a3b8" : "#475569";
|
|
1716
|
+
const cardBg = isDark ? "#1e293b" : "#ffffff";
|
|
1717
|
+
const borderCol = isDark ? "#334155" : "#e2e8f0";
|
|
1718
|
+
const tableHtml = this.renderTable(config, options);
|
|
1719
|
+
let html = `
|
|
1720
|
+
<article class="solvenza-cookie-policy-document ${options.className || ""}" style="font-family: system-ui, -apple-system, sans-serif; color: ${textCol}; line-height: 1.7; max-width: 900px; margin: 0 auto; padding: 1rem 0;">
|
|
1721
|
+
<header style="margin-bottom: 2rem; border-bottom: 1px solid ${borderCol}; padding-bottom: 1.5rem;">
|
|
1722
|
+
<h1 style="font-size: 1.85rem; font-weight: 800; margin: 0 0 0.5rem 0; color: ${textCol};">Pol\xEDtica de Cookies</h1>
|
|
1723
|
+
<p style="margin: 0; font-size: 0.9rem; color: ${mutedCol};">
|
|
1724
|
+
Conforme al art\xEDculo 22.2 de la Ley 34/2002 (LSSI-CE), el RGPD (UE 2016/679) y la Gu\xEDa sobre el uso de cookies de la AEPD.
|
|
1725
|
+
<br><em>\xDAltima actualizaci\xF3n: ${sanitizeHtml(lastUpdated)}</em>
|
|
1726
|
+
</p>
|
|
1727
|
+
</header>
|
|
1728
|
+
|
|
1729
|
+
<section style="margin-bottom: 2rem;">
|
|
1730
|
+
<h2 style="font-size: 1.3rem; font-weight: 700; color: ${textCol};">1. \xBFQu\xE9 son las cookies y tecnolog\xEDas de almacenamiento local?</h2>
|
|
1731
|
+
<p style="color: ${mutedCol};">
|
|
1732
|
+
Este sitio web, titularidad de <strong>${sanitizeHtml(companyName)}</strong>, utiliza cookies y tecnolog\xEDas de almacenamiento similares (tales como <code>localStorage</code>, <code>sessionStorage</code>, p\xEDxeles de seguimiento y etiquetas) para garantizar el funcionamiento t\xE9cnico del sitio, optimizar la experiencia de navegaci\xF3n, medir el uso de la web y, en su caso, mostrar contenidos personalizados o multimedia.
|
|
1733
|
+
</p>
|
|
1734
|
+
<p style="color: ${mutedCol};">
|
|
1735
|
+
Una <strong>cookie</strong> es un peque\xF1o fichero de texto que se descarga y almacena en el navegador del usuario al acceder a determinadas p\xE1ginas web. Permite a una p\xE1gina web, entre otras cosas, recordar las preferencias de navegaci\xF3n, almacenar y recuperar informaci\xF3n sobre los h\xE1bitos de visita y reconocer al usuario en visitas posteriores.
|
|
1736
|
+
</p>
|
|
1737
|
+
</section>
|
|
1738
|
+
|
|
1739
|
+
<section style="margin-bottom: 2rem;">
|
|
1740
|
+
<h2 style="font-size: 1.3rem; font-weight: 700; color: ${textCol};">2. Tipos de cookies y finalidades utilizadas</h2>
|
|
1741
|
+
<p style="color: ${mutedCol};">
|
|
1742
|
+
En funci\xF3n de su finalidad, titularidad y plazo de permanencia, en este sitio web se utilizan las siguientes categor\xEDas:
|
|
1743
|
+
</p>
|
|
1744
|
+
<ul style="color: ${mutedCol}; padding-left: 1.5rem; margin-bottom: 1.5rem;">
|
|
1745
|
+
`;
|
|
1746
|
+
for (const [, cat] of Object.entries(config.categories)) {
|
|
1747
|
+
const isReq = cat.required === true;
|
|
1748
|
+
html += `
|
|
1749
|
+
<li style="margin-bottom: 0.6rem;">
|
|
1750
|
+
<strong>${sanitizeHtml(cat.label)}</strong> (${isReq ? "T\xE9cnicas / Exentas de consentimiento" : "Opcionales / Sujetas a consentimiento"}):
|
|
1751
|
+
${sanitizeHtml(cat.description)}
|
|
1752
|
+
</li>
|
|
1753
|
+
`;
|
|
1754
|
+
}
|
|
1755
|
+
html += `
|
|
1756
|
+
</ul>
|
|
1757
|
+
|
|
1758
|
+
<h3 style="font-size: 1.1rem; font-weight: 700; color: ${textCol}; margin-top: 1.5rem;">Inventario detallado de cookies y almacenamiento web</h3>
|
|
1759
|
+
<p style="color: ${mutedCol}; font-size: 0.9rem;">
|
|
1760
|
+
A continuaci\xF3n se detallan de forma transparente todas las cookies y claves de almacenamiento registradas en la aplicaci\xF3n:
|
|
1761
|
+
</p>
|
|
1762
|
+
${tableHtml}
|
|
1763
|
+
</section>
|
|
1764
|
+
|
|
1765
|
+
<section style="margin-bottom: 2rem; background: ${cardBg}; border: 1px solid ${borderCol}; border-radius: 12px; padding: 1.5rem; box-shadow: 0 2px 4px rgba(0,0,0,0.03);">
|
|
1766
|
+
<h2 style="font-size: 1.3rem; font-weight: 700; color: ${textCol}; margin-top: 0;">3. Gesti\xF3n, configuraci\xF3n y revocaci\xF3n del consentimiento</h2>
|
|
1767
|
+
<p style="color: ${mutedCol};">
|
|
1768
|
+
De acuerdo con las directrices de la Agencia Espa\xF1ola de Protecci\xF3n de Datos (AEPD), retirar o modificar el consentimiento debe ser tan f\xE1cil como otorgarlo. Puedes modificar tus preferencias o revocar el consentimiento en cualquier momento:
|
|
1769
|
+
</p>
|
|
1770
|
+
<div style="display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 1.25rem;">
|
|
1771
|
+
<button
|
|
1772
|
+
type="button"
|
|
1773
|
+
data-consent-open
|
|
1774
|
+
onclick="if(window.Consent) window.Consent.openPreferences()"
|
|
1775
|
+
style="background: #0f172a; color: #ffffff; border: none; padding: 0.65rem 1.3rem; border-radius: 8px; font-weight: 600; font-size: 0.9rem; cursor: pointer; display: inline-flex; align-items: center; gap: 0.5rem;"
|
|
1776
|
+
>
|
|
1777
|
+
\u2699\uFE0F Abrir panel de preferencias de cookies
|
|
1778
|
+
</button>
|
|
1779
|
+
<button
|
|
1780
|
+
type="button"
|
|
1781
|
+
onclick="if(window.Consent) window.Consent.withdraw()"
|
|
1782
|
+
style="background: #ffffff; color: #dc2626; border: 1px solid #fca5a5; padding: 0.65rem 1.3rem; border-radius: 8px; font-weight: 600; font-size: 0.9rem; cursor: pointer; display: inline-flex; align-items: center; gap: 0.5rem;"
|
|
1783
|
+
>
|
|
1784
|
+
\u{1F504} Revocar consentimiento y purgar datos
|
|
1785
|
+
</button>
|
|
1786
|
+
</div>
|
|
1787
|
+
</section>
|
|
1788
|
+
|
|
1789
|
+
<section style="margin-bottom: 2rem;">
|
|
1790
|
+
<h2 style="font-size: 1.3rem; font-weight: 700; color: ${textCol};">4. C\xF3mo deshabilitar o eliminar las cookies en los navegadores</h2>
|
|
1791
|
+
<p style="color: ${mutedCol};">
|
|
1792
|
+
El usuario puede permitir, bloquear o eliminar las cookies instaladas en su equipo mediante la configuraci\xF3n de las opciones de su navegador web:
|
|
1793
|
+
</p>
|
|
1794
|
+
<ul style="color: #2563eb; padding-left: 1.5rem;">
|
|
1795
|
+
<li style="margin-bottom: 0.4rem;"><a href="https://support.google.com/chrome/answer/95647" target="_blank" rel="noopener noreferrer" style="color: inherit; text-decoration: underline;">Google Chrome</a></li>
|
|
1796
|
+
<li style="margin-bottom: 0.4rem;"><a href="https://support.mozilla.org/es/kb/habilitar-y-deshabilitar-cookies-sitios-web-rastrear-preferencias" target="_blank" rel="noopener noreferrer" style="color: inherit; text-decoration: underline;">Mozilla Firefox</a></li>
|
|
1797
|
+
<li style="margin-bottom: 0.4rem;"><a href="https://support.apple.com/es-es/guide/safari/sfri11471/mac" target="_blank" rel="noopener noreferrer" style="color: inherit; text-decoration: underline;">Apple Safari</a></li>
|
|
1798
|
+
<li style="margin-bottom: 0.4rem;"><a href="https://support.microsoft.com/es-es/microsoft-edge/eliminar-las-cookies-en-microsoft-edge-63947406-40ac-c3b8-57b9-2a946a29ae09" target="_blank" rel="noopener noreferrer" style="color: inherit; text-decoration: underline;">Microsoft Edge</a></li>
|
|
1799
|
+
<li style="margin-bottom: 0.4rem;"><a href="https://help.opera.com/en/latest/web-preferences/#cookies" target="_blank" rel="noopener noreferrer" style="color: inherit; text-decoration: underline;">Opera Browser</a></li>
|
|
1800
|
+
</ul>
|
|
1801
|
+
</section>
|
|
1802
|
+
|
|
1803
|
+
<section style="margin-bottom: 2rem;">
|
|
1804
|
+
<h2 style="font-size: 1.3rem; font-weight: 700; color: ${textCol};">5. Reconocimiento de Global Privacy Control (GPC)</h2>
|
|
1805
|
+
<p style="color: ${mutedCol};">
|
|
1806
|
+
Este sitio web est\xE1 adaptado al est\xE1ndar <strong>Global Privacy Control (GPC)</strong> y a la cabecera <code>Do Not Track (DNT)</code>. Si tu navegador emite una se\xF1al universal de no seguimiento, nuestro sistema desactivar\xE1 autom\xE1ticamente todas las cookies no necesarias sin requerir interacci\xF3n manual.
|
|
1807
|
+
</p>
|
|
1808
|
+
</section>
|
|
1809
|
+
|
|
1810
|
+
<section style="margin-bottom: 2rem;">
|
|
1811
|
+
<h2 style="font-size: 1.3rem; font-weight: 700; color: ${textCol};">6. Transferencias internacionales de datos</h2>
|
|
1812
|
+
<p style="color: ${mutedCol};">
|
|
1813
|
+
Determinadas cookies de terceros (tales como Google Analytics o Meta) pueden implicar la transferencia internacional de datos a servidores ubicados en Estados Unidos u otros pa\xEDses fuera del Espacio Econ\xF3mico Europeo (EEE). Dichas transferencias se encuentran amparadas bajo el Marco de Privacidad de Datos UE-EE.UU. (Data Privacy Framework) o Cl\xE1usulas Contractuales Tipo aprobadas por la Comisi\xF3n Europea.
|
|
1814
|
+
</p>
|
|
1815
|
+
</section>
|
|
1816
|
+
</article>
|
|
1817
|
+
`;
|
|
1818
|
+
return html;
|
|
1819
|
+
}
|
|
1820
|
+
/**
|
|
1821
|
+
* Render complete legal notice (Aviso Legal) conforming to LSSI-CE Art. 10.
|
|
1822
|
+
*/
|
|
1823
|
+
static renderLegalNotice(config, options = {}) {
|
|
1824
|
+
var _a, _b, _c;
|
|
1825
|
+
const entity = config.legalEntity || {
|
|
1826
|
+
name: "[Raz\xF3n Social del Titular]",
|
|
1827
|
+
taxId: "[NIF / CIF]",
|
|
1828
|
+
address: "[Domicilio Social]",
|
|
1829
|
+
email: "[Email de Contacto]"
|
|
1830
|
+
};
|
|
1831
|
+
const law = ((_a = config.legalNotice) == null ? void 0 : _a.applicableLaw) || "Legislaci\xF3n espa\xF1ola (LSSI-CE, LOPDGDD) y Reglamento General de Protecci\xF3n de Datos (RGPD UE 2016/679)";
|
|
1832
|
+
const jurisdiction = ((_b = config.legalNotice) == null ? void 0 : _b.jurisdiction) || "Juzgados y Tribunales competentes conforme a la normativa de consumidores y usuarios";
|
|
1833
|
+
const lastUpdated = ((_c = config.legalNotice) == null ? void 0 : _c.lastUpdated) || config.policyVersion || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1834
|
+
const isDark = options.theme === "dark";
|
|
1835
|
+
const textCol = isDark ? "#f8fafc" : "#0f172a";
|
|
1836
|
+
const mutedCol = isDark ? "#94a3b8" : "#475569";
|
|
1837
|
+
const borderCol = isDark ? "#334155" : "#e2e8f0";
|
|
1838
|
+
return `
|
|
1839
|
+
<article class="solvenza-legal-notice-document ${options.className || ""}" style="font-family: system-ui, -apple-system, sans-serif; color: ${textCol}; line-height: 1.7; max-width: 900px; margin: 0 auto; padding: 1rem 0;">
|
|
1840
|
+
<header style="margin-bottom: 2rem; border-bottom: 1px solid ${borderCol}; padding-bottom: 1.5rem;">
|
|
1841
|
+
<h1 style="font-size: 1.85rem; font-weight: 800; margin: 0 0 0.5rem 0; color: ${textCol};">Aviso Legal</h1>
|
|
1842
|
+
<p style="margin: 0; font-size: 0.9rem; color: ${mutedCol};">
|
|
1843
|
+
En cumplimiento del art\xEDculo 10 de la Ley 34/2002, de 11 de julio, de Servicios de la Sociedad de la Informaci\xF3n y de Comercio Electr\xF3nico (LSSI-CE).
|
|
1844
|
+
<br><em>\xDAltima actualizaci\xF3n: ${sanitizeHtml(lastUpdated)}</em>
|
|
1845
|
+
</p>
|
|
1846
|
+
</header>
|
|
1847
|
+
|
|
1848
|
+
<section style="margin-bottom: 2rem;">
|
|
1849
|
+
<h2 style="font-size: 1.3rem; font-weight: 700; color: ${textCol};">1. Datos identificativos del titular</h2>
|
|
1850
|
+
<table style="width: 100%; border-collapse: collapse; margin-top: 1rem; border: 1px solid ${borderCol};">
|
|
1851
|
+
<tbody>
|
|
1852
|
+
<tr style="border-bottom: 1px solid ${borderCol};">
|
|
1853
|
+
<td style="padding: 0.75rem 1rem; font-weight: 600; width: 30%;">Raz\xF3n Social:</td>
|
|
1854
|
+
<td style="padding: 0.75rem 1rem; color: ${mutedCol};">${sanitizeHtml(entity.name)}</td>
|
|
1855
|
+
</tr>
|
|
1856
|
+
${entity.tradeName ? `<tr style="border-bottom: 1px solid ${borderCol};"><td style="padding: 0.75rem 1rem; font-weight: 600;">Nombre Comercial:</td><td style="padding: 0.75rem 1rem; color: ${mutedCol};">${sanitizeHtml(entity.tradeName)}</td></tr>` : ""}
|
|
1857
|
+
<tr style="border-bottom: 1px solid ${borderCol};">
|
|
1858
|
+
<td style="padding: 0.75rem 1rem; font-weight: 600;">NIF / CIF:</td>
|
|
1859
|
+
<td style="padding: 0.75rem 1rem; color: ${mutedCol};">${sanitizeHtml(entity.taxId || "-")}</td>
|
|
1860
|
+
</tr>
|
|
1861
|
+
<tr style="border-bottom: 1px solid ${borderCol};">
|
|
1862
|
+
<td style="padding: 0.75rem 1rem; font-weight: 600;">Domicilio Social:</td>
|
|
1863
|
+
<td style="padding: 0.75rem 1rem; color: ${mutedCol};">${sanitizeHtml(entity.address || "-")}</td>
|
|
1864
|
+
</tr>
|
|
1865
|
+
<tr style="border-bottom: 1px solid ${borderCol};">
|
|
1866
|
+
<td style="padding: 0.75rem 1rem; font-weight: 600;">Correo Electr\xF3nico:</td>
|
|
1867
|
+
<td style="padding: 0.75rem 1rem; color: ${mutedCol};"><a href="mailto:${sanitizeHtml(entity.email || "")}" style="color: #2563eb;">${sanitizeHtml(entity.email || "-")}</a></td>
|
|
1868
|
+
</tr>
|
|
1869
|
+
${entity.phone ? `<tr style="border-bottom: 1px solid ${borderCol};"><td style="padding: 0.75rem 1rem; font-weight: 600;">Tel\xE9fono:</td><td style="padding: 0.75rem 1rem; color: ${mutedCol};">${sanitizeHtml(entity.phone)}</td></tr>` : ""}
|
|
1870
|
+
${entity.registryData ? `<tr style="border-bottom: 1px solid ${borderCol};"><td style="padding: 0.75rem 1rem; font-weight: 600;">Datos Registrales:</td><td style="padding: 0.75rem 1rem; color: ${mutedCol};">${sanitizeHtml(entity.registryData)}</td></tr>` : ""}
|
|
1871
|
+
${entity.dpoEmail ? `<tr><td style="padding: 0.75rem 1rem; font-weight: 600;">Delegado de Protecci\xF3n de Datos (DPO):</td><td style="padding: 0.75rem 1rem; color: ${mutedCol};"><a href="mailto:${sanitizeHtml(entity.dpoEmail)}" style="color: #2563eb;">${sanitizeHtml(entity.dpoEmail)}</a></td></tr>` : ""}
|
|
1872
|
+
</tbody>
|
|
1873
|
+
</table>
|
|
1874
|
+
</section>
|
|
1875
|
+
|
|
1876
|
+
<section style="margin-bottom: 2rem;">
|
|
1877
|
+
<h2 style="font-size: 1.3rem; font-weight: 700; color: ${textCol};">2. Condiciones generales de uso</h2>
|
|
1878
|
+
<p style="color: ${mutedCol};">
|
|
1879
|
+
El acceso y/o uso de este sitio web atribuye la condici\xF3n de usuario, que acepta, desde dicho acceso y/o uso, las presentes condiciones generales. El usuario se compromete a hacer un uso adecuado de los contenidos y servicios que el titular ofrece a trav\xE9s de su sitio web y a no emplearlos para incurrir en actividades il\xEDcitas o contrarias a la buena fe y al orden p\xFAblico.
|
|
1880
|
+
</p>
|
|
1881
|
+
</section>
|
|
1882
|
+
|
|
1883
|
+
<section style="margin-bottom: 2rem;">
|
|
1884
|
+
<h2 style="font-size: 1.3rem; font-weight: 700; color: ${textCol};">3. Propiedad intelectual e industrial</h2>
|
|
1885
|
+
<p style="color: ${mutedCol};">
|
|
1886
|
+
Todos los derechos de propiedad intelectual e industrial sobre el dise\xF1o, marcas, logotipos, textos, c\xF3digo fuente e ilustraciones de este sitio web corresponden al titular o a sus leg\xEDtimos licenciantes. Queda expresamente prohibida la reproducci\xF3n, distribuci\xF3n o comunicaci\xF3n p\xFAblica de la totalidad o parte de los contenidos sin autorizaci\xF3n previa y por escrito.
|
|
1887
|
+
</p>
|
|
1888
|
+
</section>
|
|
1889
|
+
|
|
1890
|
+
<section style="margin-bottom: 2rem;">
|
|
1891
|
+
<h2 style="font-size: 1.3rem; font-weight: 700; color: ${textCol};">4. Exclusi\xF3n de garant\xEDas y responsabilidad</h2>
|
|
1892
|
+
<p style="color: ${mutedCol};">
|
|
1893
|
+
El titular no se hace responsable, en ning\xFAn caso, de los da\xF1os y perjuicios de cualquier naturaleza que pudieran ocasionar errores u omisiones en los contenidos, falta de disponibilidad del portal o la transmisi\xF3n de virus o programas maliciosos, a pesar de haber adoptado todas las medidas tecnol\xF3gicas necesarias para evitarlo.
|
|
1894
|
+
</p>
|
|
1895
|
+
</section>
|
|
1896
|
+
|
|
1897
|
+
<section style="margin-bottom: 2rem;">
|
|
1898
|
+
<h2 style="font-size: 1.3rem; font-weight: 700; color: ${textCol};">5. Legislaci\xF3n aplicable y jurisdicci\xF3n</h2>
|
|
1899
|
+
<p style="color: ${mutedCol};">
|
|
1900
|
+
Las relaciones entre el titular y el usuario se regir\xE1n por la <strong>${sanitizeHtml(law)}</strong>. Para la resoluci\xF3n de cualquier controversia, las partes se someten a los <strong>${sanitizeHtml(jurisdiction)}</strong>, sin perjuicio de los fueros imperativos legales aplicables.
|
|
1901
|
+
</p>
|
|
1902
|
+
</section>
|
|
1903
|
+
</article>
|
|
1904
|
+
`;
|
|
1905
|
+
}
|
|
1906
|
+
/**
|
|
1907
|
+
* Render Privacy Policy document (Política de Privacidad RGPD).
|
|
1908
|
+
*/
|
|
1909
|
+
static renderPrivacyPolicy(config, options = {}) {
|
|
1910
|
+
const entity = config.legalEntity || {
|
|
1911
|
+
name: "[Raz\xF3n Social del Responsable]",
|
|
1912
|
+
taxId: "[NIF / CIF]",
|
|
1913
|
+
address: "[Domicilio]",
|
|
1914
|
+
email: "privacidad@solvenza.es"
|
|
1915
|
+
};
|
|
1916
|
+
const isDark = options.theme === "dark";
|
|
1917
|
+
const textCol = isDark ? "#f8fafc" : "#0f172a";
|
|
1918
|
+
const mutedCol = isDark ? "#94a3b8" : "#475569";
|
|
1919
|
+
const borderCol = isDark ? "#334155" : "#e2e8f0";
|
|
1920
|
+
return `
|
|
1921
|
+
<article class="solvenza-privacy-policy-document ${options.className || ""}" style="font-family: system-ui, -apple-system, sans-serif; color: ${textCol}; line-height: 1.7; max-width: 900px; margin: 0 auto; padding: 1rem 0;">
|
|
1922
|
+
<header style="margin-bottom: 2rem; border-bottom: 1px solid ${borderCol}; padding-bottom: 1.5rem;">
|
|
1923
|
+
<h1 style="font-size: 1.85rem; font-weight: 800; margin: 0 0 0.5rem 0; color: ${textCol};">Pol\xEDtica de Privacidad y Protecci\xF3n de Datos</h1>
|
|
1924
|
+
<p style="margin: 0; font-size: 0.9rem; color: ${mutedCol};">
|
|
1925
|
+
Conforme al Reglamento General de Protecci\xF3n de Datos (RGPD UE 2016/679) y la Ley Org\xE1nica 3/2018 (LOPDGDD).
|
|
1926
|
+
</p>
|
|
1927
|
+
</header>
|
|
1928
|
+
|
|
1929
|
+
<section style="margin-bottom: 2rem;">
|
|
1930
|
+
<h2 style="font-size: 1.3rem; font-weight: 700; color: ${textCol};">1. Responsable del tratamiento</h2>
|
|
1931
|
+
<p style="color: ${mutedCol};">
|
|
1932
|
+
<strong>Identidad:</strong> ${sanitizeHtml(entity.name)}<br>
|
|
1933
|
+
<strong>NIF / CIF:</strong> ${sanitizeHtml(entity.taxId || "-")}<br>
|
|
1934
|
+
<strong>Direcci\xF3n:</strong> ${sanitizeHtml(entity.address || "-")}<br>
|
|
1935
|
+
<strong>Correo electr\xF3nico:</strong> <a href="mailto:${sanitizeHtml(entity.email || "")}" style="color: #2563eb;">${sanitizeHtml(entity.email || "-")}</a><br>
|
|
1936
|
+
${entity.dpoEmail ? `<strong>Contacto DPO:</strong> <a href="mailto:${sanitizeHtml(entity.dpoEmail)}" style="color: #2563eb;">${sanitizeHtml(entity.dpoEmail)}</a>` : ""}
|
|
1937
|
+
</p>
|
|
1938
|
+
</section>
|
|
1939
|
+
|
|
1940
|
+
<section style="margin-bottom: 2rem;">
|
|
1941
|
+
<h2 style="font-size: 1.3rem; font-weight: 700; color: ${textCol};">2. Finalidad del tratamiento y base jur\xEDdica</h2>
|
|
1942
|
+
<p style="color: ${mutedCol};">
|
|
1943
|
+
Tratamos los datos facilitados por los usuarios con las finalidades de gestionar sus solicitudes, prestar los servicios contratados y, en su caso, analizar el rendimiento de la web y remitir comunicaciones comerciales sobre la base de su <strong>consentimiento expl\xEDcito</strong> (art. 6.1.a RGPD) o en la <strong>ejecuci\xF3n contractual</strong> (art. 6.1.b RGPD).
|
|
1944
|
+
</p>
|
|
1945
|
+
</section>
|
|
1946
|
+
|
|
1947
|
+
<section style="margin-bottom: 2rem;">
|
|
1948
|
+
<h2 style="font-size: 1.3rem; font-weight: 700; color: ${textCol};">3. Derechos del interesado</h2>
|
|
1949
|
+
<p style="color: ${mutedCol};">
|
|
1950
|
+
Cualquier persona tiene derecho a obtener confirmaci\xF3n sobre si estamos tratando datos personales que le conciernen. Los interesados tienen derecho a acceder a sus datos personales, solicitar la rectificaci\xF3n de los datos inexactos o, en su caso, solicitar su supresi\xF3n cuando los datos ya no sean necesarios para los fines que fueron recogidos.
|
|
1951
|
+
</p>
|
|
1952
|
+
<p style="color: ${mutedCol};">
|
|
1953
|
+
Puedes ejercer tus derechos de Acceso, Rectificaci\xF3n, Supresi\xF3n, Limitaci\xF3n, Portabilidad y Oposici\xF3n enviando un correo a <a href="mailto:${sanitizeHtml(entity.email || "")}" style="color: #2563eb;">${sanitizeHtml(entity.email || "")}</a>. Asimismo, puedes presentar una reclamaci\xF3n ante la Agencia Espa\xF1ola de Protecci\xF3n de Datos (<a href="https://www.aepd.es" target="_blank" rel="noopener noreferrer" style="color: #2563eb;">www.aepd.es</a>).
|
|
1954
|
+
</p>
|
|
1955
|
+
</section>
|
|
1956
|
+
</article>
|
|
1957
|
+
`;
|
|
1958
|
+
}
|
|
1959
|
+
};
|
|
1960
|
+
|
|
1961
|
+
// src/i18n/engine.ts
|
|
1962
|
+
var BUILTIN_TRANSLATIONS = {
|
|
1963
|
+
es: {
|
|
1964
|
+
ui: {
|
|
1965
|
+
banner: {
|
|
1966
|
+
title: "Tu privacidad, bajo tu control",
|
|
1967
|
+
description: "Usamos tecnolog\xEDas necesarias para el funcionamiento del sitio. Con tu permiso, tambi\xE9n podemos utilizar anal\xEDtica y marketing.",
|
|
1968
|
+
accept: "Aceptar todas",
|
|
1969
|
+
reject: "Rechazar todas",
|
|
1970
|
+
configure: "Configurar",
|
|
1971
|
+
cookiesPolicy: "Pol\xEDtica de cookies",
|
|
1972
|
+
privacyPolicy: "Pol\xEDtica de privacidad",
|
|
1973
|
+
ariaLabel: "Gesti\xF3n de consentimiento de privacidad"
|
|
1974
|
+
},
|
|
1975
|
+
preferences: {
|
|
1976
|
+
title: "Preferencias de privacidad",
|
|
1977
|
+
subtitle: "Gestiona tus permisos de almacenamiento por finalidad (LSSI art. 22.2 & RGPD).",
|
|
1978
|
+
save: "Guardar selecci\xF3n",
|
|
1979
|
+
acceptAll: "Permitir todas",
|
|
1980
|
+
rejectAll: "Rechazar opcionales",
|
|
1981
|
+
closeLabel: "Cerrar ventana",
|
|
1982
|
+
requiredBadge: "Requerida",
|
|
1983
|
+
optionalBadge: "Opcional",
|
|
1984
|
+
servicesLabel: "Servicios incluidos",
|
|
1985
|
+
viewServices: "Ver servicios",
|
|
1986
|
+
hideServices: "Ocultar servicios",
|
|
1987
|
+
thirdParty: "Terceros"
|
|
1988
|
+
},
|
|
1989
|
+
floatingBadge: {
|
|
1990
|
+
label: "Cookies",
|
|
1991
|
+
ariaLabel: "Configuraci\xF3n y revocaci\xF3n de cookies",
|
|
1992
|
+
tooltip: "Configurar o declinar cookies"
|
|
1993
|
+
}
|
|
1994
|
+
},
|
|
1995
|
+
categories: {
|
|
1996
|
+
necessary: {
|
|
1997
|
+
label: "Cookies T\xE9cnicas y Necesarias",
|
|
1998
|
+
description: "Imprescindibles para que el sitio web funcione y no pueden ser desactivadas."
|
|
1999
|
+
},
|
|
2000
|
+
analytics: {
|
|
2001
|
+
label: "Medici\xF3n y Rendimiento",
|
|
2002
|
+
description: "Nos permiten analizar las visitas y fuentes de tr\xE1fico para optimizar el sitio."
|
|
2003
|
+
},
|
|
2004
|
+
marketing: {
|
|
2005
|
+
label: "Publicidad Personalizada",
|
|
2006
|
+
description: "Utilizadas para mostrar anuncios relevantes seg\xFAn tus intereses y navegaci\xF3n."
|
|
2007
|
+
},
|
|
2008
|
+
preferences: {
|
|
2009
|
+
label: "Preferencias y Personalizaci\xF3n",
|
|
2010
|
+
description: "Permiten recordar informaci\xF3n que cambia el aspecto o comportamiento del sitio."
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
},
|
|
2014
|
+
en: {
|
|
2015
|
+
ui: {
|
|
2016
|
+
banner: {
|
|
2017
|
+
title: "Your privacy, under your control",
|
|
2018
|
+
description: "We use essential technologies for our website to function. With your consent, we may also use analytics and marketing technologies.",
|
|
2019
|
+
accept: "Accept all",
|
|
2020
|
+
reject: "Reject all",
|
|
2021
|
+
configure: "Customize",
|
|
2022
|
+
cookiesPolicy: "Cookie policy",
|
|
2023
|
+
privacyPolicy: "Privacy policy",
|
|
2024
|
+
ariaLabel: "Privacy consent management"
|
|
2025
|
+
},
|
|
2026
|
+
preferences: {
|
|
2027
|
+
title: "Privacy Preferences",
|
|
2028
|
+
subtitle: "Manage your storage permissions by purpose (GDPR & ePrivacy compliant).",
|
|
2029
|
+
save: "Save preferences",
|
|
2030
|
+
acceptAll: "Allow all",
|
|
2031
|
+
rejectAll: "Reject optional",
|
|
2032
|
+
closeLabel: "Close dialog",
|
|
2033
|
+
requiredBadge: "Required",
|
|
2034
|
+
optionalBadge: "Optional",
|
|
2035
|
+
servicesLabel: "Included services",
|
|
2036
|
+
viewServices: "View services",
|
|
2037
|
+
hideServices: "Hide services",
|
|
2038
|
+
thirdParty: "Third-party"
|
|
2039
|
+
},
|
|
2040
|
+
floatingBadge: {
|
|
2041
|
+
label: "Cookies",
|
|
2042
|
+
ariaLabel: "Cookie preferences and revocation",
|
|
2043
|
+
tooltip: "Customize or decline cookies"
|
|
2044
|
+
}
|
|
2045
|
+
},
|
|
2046
|
+
categories: {
|
|
2047
|
+
necessary: {
|
|
2048
|
+
label: "Strictly Necessary Cookies",
|
|
2049
|
+
description: "Essential for the website to function properly and cannot be deactivated."
|
|
2050
|
+
},
|
|
2051
|
+
analytics: {
|
|
2052
|
+
label: "Performance & Analytics",
|
|
2053
|
+
description: "Help us understand visitor behavior and traffic sources to optimize performance."
|
|
2054
|
+
},
|
|
2055
|
+
marketing: {
|
|
2056
|
+
label: "Targeted Advertising",
|
|
2057
|
+
description: "Used to deliver relevant ads and track campaign effectiveness across websites."
|
|
2058
|
+
},
|
|
2059
|
+
preferences: {
|
|
2060
|
+
label: "Preferences & Personalization",
|
|
2061
|
+
description: "Enable the website to remember user choices like language and layout settings."
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
},
|
|
2065
|
+
ca: {
|
|
2066
|
+
ui: {
|
|
2067
|
+
banner: {
|
|
2068
|
+
title: "La teva privacitat, sota el teu control",
|
|
2069
|
+
description: "Utilitzem tecnologies necess\xE0ries per al funcionament del lloc. Amb el teu perm\xEDs, tamb\xE9 podem utilitzar anal\xEDtica i m\xE0rqueting.",
|
|
2070
|
+
accept: "Acceptar-les totes",
|
|
2071
|
+
reject: "Rebutjar-les totes",
|
|
2072
|
+
configure: "Configurar",
|
|
2073
|
+
cookiesPolicy: "Pol\xEDtica de cookies",
|
|
2074
|
+
privacyPolicy: "Pol\xEDtica de privacitat",
|
|
2075
|
+
ariaLabel: "Gesti\xF3 de consentiment de privacitat"
|
|
2076
|
+
},
|
|
2077
|
+
preferences: {
|
|
2078
|
+
title: "Prefer\xE8ncies de privacitat",
|
|
2079
|
+
subtitle: "Gestiona els teus permisos d'emmagatzematge per finalitat (LSSI art. 22.2 & RGPD).",
|
|
2080
|
+
save: "Desar selecci\xF3",
|
|
2081
|
+
acceptAll: "Permetre-les totes",
|
|
2082
|
+
rejectAll: "Rebutjar opcionals",
|
|
2083
|
+
closeLabel: "Tancar finestra",
|
|
2084
|
+
requiredBadge: "Requerida",
|
|
2085
|
+
optionalBadge: "Opcional",
|
|
2086
|
+
servicesLabel: "Serveis inclosos",
|
|
2087
|
+
viewServices: "Veure serveis",
|
|
2088
|
+
hideServices: "Amagar serveis",
|
|
2089
|
+
thirdParty: "Tercers"
|
|
2090
|
+
},
|
|
2091
|
+
floatingBadge: {
|
|
2092
|
+
label: "Cookies",
|
|
2093
|
+
ariaLabel: "Configuraci\xF3 i revocaci\xF3 de cookies",
|
|
2094
|
+
tooltip: "Configurar o declinar cookies"
|
|
2095
|
+
}
|
|
2096
|
+
},
|
|
2097
|
+
categories: {
|
|
2098
|
+
necessary: {
|
|
2099
|
+
label: "Cookies T\xE8cniques i Necess\xE0ries",
|
|
2100
|
+
description: "Imprescindibles perqu\xE8 el lloc web funcioni correctament i no es poden desactivar."
|
|
2101
|
+
},
|
|
2102
|
+
analytics: {
|
|
2103
|
+
label: "Mesurament i Rendiment",
|
|
2104
|
+
description: "Ens permeten analitzar les visites i fonts de tr\xE0nsit per optimitzar el lloc."
|
|
2105
|
+
},
|
|
2106
|
+
marketing: {
|
|
2107
|
+
label: "Publicidad Personalitzada",
|
|
2108
|
+
description: "Utilitzades per mostrar anuncis rellevants segons els teus interessos i navegaci\xF3."
|
|
2109
|
+
},
|
|
2110
|
+
preferences: {
|
|
2111
|
+
label: "Prefer\xE8ncies i Personalitzaci\xF3",
|
|
2112
|
+
description: "Permeten recordar informaci\xF3 que canvia l'aspecte o comportament del lloc."
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
},
|
|
2116
|
+
eu: {
|
|
2117
|
+
ui: {
|
|
2118
|
+
banner: {
|
|
2119
|
+
title: "Zure pribatutasuna, zure kontrolpean",
|
|
2120
|
+
description: "Webguneak funtzionatzeko beharrezkoak diren teknologiak erabiltzen ditugu. Zure baimenarekin, analitika eta marketina ere erabil ditzakegu.",
|
|
2121
|
+
accept: "Onartu guztiak",
|
|
2122
|
+
reject: "Baztertu guztiak",
|
|
2123
|
+
configure: "Konfiguratu",
|
|
2124
|
+
cookiesPolicy: "Cookie politika",
|
|
2125
|
+
privacyPolicy: "Pribatutasun politika",
|
|
2126
|
+
ariaLabel: "Pribatutasun-baimenen kudeaketa"
|
|
2127
|
+
},
|
|
2128
|
+
preferences: {
|
|
2129
|
+
title: "Pribatutasun-hobespenak",
|
|
2130
|
+
subtitle: "Kudeatu zure biltegiratze-baimenak helburuaren arabera (RGPD).",
|
|
2131
|
+
save: "Gorde hautapena",
|
|
2132
|
+
acceptAll: "Onartu guztiak",
|
|
2133
|
+
rejectAll: "Baztertu aukerakoak",
|
|
2134
|
+
closeLabel: "Itxi leihoa",
|
|
2135
|
+
requiredBadge: "Beharrezkoa",
|
|
2136
|
+
optionalBadge: "Aukerakoa",
|
|
2137
|
+
servicesLabel: "Barne dauden zerbitzuak",
|
|
2138
|
+
viewServices: "Ikusi zerbitzuak",
|
|
2139
|
+
hideServices: "Ezkutatu zerbitzuak",
|
|
2140
|
+
thirdParty: "Hirugarrenak"
|
|
2141
|
+
},
|
|
2142
|
+
floatingBadge: {
|
|
2143
|
+
label: "Cookieak",
|
|
2144
|
+
ariaLabel: "Cookien ezarpenak eta ezeztapena",
|
|
2145
|
+
tooltip: "Konfiguratu edo baztertu cookieak"
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
},
|
|
2149
|
+
gl: {
|
|
2150
|
+
ui: {
|
|
2151
|
+
banner: {
|
|
2152
|
+
title: "A t\xFAa privacidade, baixo o teu control",
|
|
2153
|
+
description: "Empregamos tecnolox\xEDas necesarias para o funcionamento do sitio. Co teu permiso, tam\xE9n podemos utilizar anal\xEDtica e m\xE1rketing.",
|
|
2154
|
+
accept: "Aceptar todas",
|
|
2155
|
+
reject: "Rexeitar todas",
|
|
2156
|
+
configure: "Configurar",
|
|
2157
|
+
cookiesPolicy: "Pol\xEDtica de cookies",
|
|
2158
|
+
privacyPolicy: "Pol\xEDtica de privacidade",
|
|
2159
|
+
ariaLabel: "Xesti\xF3n de consentimento de privacidade"
|
|
2160
|
+
},
|
|
2161
|
+
preferences: {
|
|
2162
|
+
title: "Preferencias de privacidade",
|
|
2163
|
+
subtitle: "Xestiona os teus permisos de almacenamento por finalidade (LSSI art. 22.2 & RGPD).",
|
|
2164
|
+
save: "Gardar selecci\xF3n",
|
|
2165
|
+
acceptAll: "Permitir todas",
|
|
2166
|
+
rejectAll: "Rexeitar opcionais",
|
|
2167
|
+
closeLabel: "Pechar xanela",
|
|
2168
|
+
requiredBadge: "Requirida",
|
|
2169
|
+
optionalBadge: "Opcional",
|
|
2170
|
+
servicesLabel: "Servizos inclu\xEDdos",
|
|
2171
|
+
viewServices: "Ver servizos",
|
|
2172
|
+
hideServices: "Ocultar servizos",
|
|
2173
|
+
thirdParty: "Terceiros"
|
|
2174
|
+
},
|
|
2175
|
+
floatingBadge: {
|
|
2176
|
+
label: "Cookies",
|
|
2177
|
+
ariaLabel: "Configuraci\xF3n e revocaci\xF3n de cookies",
|
|
2178
|
+
tooltip: "Configurar ou declinar cookies"
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
};
|
|
2183
|
+
var I18nEngine = class {
|
|
2184
|
+
constructor(defaultLocale = "es") {
|
|
2185
|
+
this.locale = "es";
|
|
2186
|
+
this.locale = defaultLocale;
|
|
2187
|
+
}
|
|
2188
|
+
setLocale(locale) {
|
|
2189
|
+
this.locale = locale.toLowerCase();
|
|
2190
|
+
}
|
|
2191
|
+
getLocale() {
|
|
2192
|
+
return this.locale;
|
|
2193
|
+
}
|
|
2194
|
+
detectBrowserLocale(supportedLocales) {
|
|
2195
|
+
if (typeof navigator !== "undefined" && navigator.language) {
|
|
2196
|
+
const detected = navigator.language.split("-")[0].toLowerCase();
|
|
2197
|
+
if (supportedLocales && supportedLocales.length > 0) {
|
|
2198
|
+
const normalized = supportedLocales.map((l) => l.toLowerCase());
|
|
2199
|
+
if (normalized.includes(detected)) {
|
|
2200
|
+
return detected;
|
|
2201
|
+
}
|
|
2202
|
+
return normalized[0];
|
|
2203
|
+
}
|
|
2204
|
+
return detected;
|
|
2205
|
+
}
|
|
2206
|
+
return "es";
|
|
2207
|
+
}
|
|
2208
|
+
isLocaleSupported(locale, config) {
|
|
2209
|
+
var _a, _b;
|
|
2210
|
+
const clean = locale.toLowerCase();
|
|
2211
|
+
if (((_a = config == null ? void 0 : config.locale) == null ? void 0 : _a.supported) && config.locale.supported.length > 0) {
|
|
2212
|
+
return config.locale.supported.map((s) => s.toLowerCase()).includes(clean);
|
|
2213
|
+
}
|
|
2214
|
+
if ((config == null ? void 0 : config.translations) && config.translations[clean]) {
|
|
2215
|
+
return true;
|
|
2216
|
+
}
|
|
2217
|
+
if (BUILTIN_TRANSLATIONS[clean]) {
|
|
2218
|
+
return true;
|
|
2219
|
+
}
|
|
2220
|
+
return clean === (((_b = config == null ? void 0 : config.locale) == null ? void 0 : _b.default) || "es").toLowerCase();
|
|
2221
|
+
}
|
|
2222
|
+
detectParentLocale(config) {
|
|
2223
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2224
|
+
const supported = (_a = config == null ? void 0 : config.locale) == null ? void 0 : _a.supported;
|
|
2225
|
+
const defaultLocale = (((_b = config == null ? void 0 : config.locale) == null ? void 0 : _b.default) || "es").toLowerCase();
|
|
2226
|
+
if (((_c = config == null ? void 0 : config.locale) == null ? void 0 : _c.syncHtmlLang) !== false && typeof document !== "undefined" && document.documentElement) {
|
|
2227
|
+
const htmlLang = document.documentElement.lang;
|
|
2228
|
+
if (htmlLang && htmlLang.trim().length > 0) {
|
|
2229
|
+
const clean = htmlLang.split("-")[0].toLowerCase().trim();
|
|
2230
|
+
if (this.isLocaleSupported(clean, config)) {
|
|
2231
|
+
return clean;
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
if (((_d = config == null ? void 0 : config.locale) == null ? void 0 : _d.syncUrl) !== false && typeof window !== "undefined" && window.location) {
|
|
2236
|
+
const pathSegments = window.location.pathname.split("/").filter(Boolean);
|
|
2237
|
+
if (pathSegments.length > 0) {
|
|
2238
|
+
const potentialLang = pathSegments[0].toLowerCase();
|
|
2239
|
+
if (this.isLocaleSupported(potentialLang, config)) {
|
|
2240
|
+
return potentialLang;
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
try {
|
|
2244
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
2245
|
+
const queryLang = (_e = urlParams.get("lang") || urlParams.get("locale")) == null ? void 0 : _e.toLowerCase();
|
|
2246
|
+
if (queryLang && this.isLocaleSupported(queryLang, config)) {
|
|
2247
|
+
return queryLang;
|
|
2248
|
+
}
|
|
2249
|
+
} catch (e) {
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
if ((_f = config == null ? void 0 : config.locale) == null ? void 0 : _f.autoDetect) {
|
|
2253
|
+
return this.detectBrowserLocale(supported);
|
|
2254
|
+
}
|
|
2255
|
+
return defaultLocale;
|
|
2256
|
+
}
|
|
2257
|
+
startParentSync(config, onLocaleChange) {
|
|
2258
|
+
var _a, _b;
|
|
2259
|
+
const cleanups = [];
|
|
2260
|
+
if (((_a = config.locale) == null ? void 0 : _a.syncHtmlLang) !== false && typeof document !== "undefined" && typeof MutationObserver !== "undefined" && document.documentElement) {
|
|
2261
|
+
const observer = new MutationObserver((mutations) => {
|
|
2262
|
+
for (const mutation of mutations) {
|
|
2263
|
+
if (mutation.type === "attributes" && mutation.attributeName === "lang") {
|
|
2264
|
+
const rawLang = document.documentElement.lang;
|
|
2265
|
+
if (rawLang && rawLang.trim().length > 0) {
|
|
2266
|
+
const clean = rawLang.split("-")[0].toLowerCase().trim();
|
|
2267
|
+
if (this.isLocaleSupported(clean, config) && clean !== this.locale) {
|
|
2268
|
+
onLocaleChange(clean);
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
});
|
|
2274
|
+
observer.observe(document.documentElement, {
|
|
2275
|
+
attributes: true,
|
|
2276
|
+
attributeFilter: ["lang"]
|
|
2277
|
+
});
|
|
2278
|
+
cleanups.push(() => observer.disconnect());
|
|
2279
|
+
}
|
|
2280
|
+
if (((_b = config.locale) == null ? void 0 : _b.autoDetect) && typeof window !== "undefined") {
|
|
2281
|
+
const handleLangChange = () => {
|
|
2282
|
+
var _a2;
|
|
2283
|
+
const detected = this.detectBrowserLocale((_a2 = config.locale) == null ? void 0 : _a2.supported);
|
|
2284
|
+
if (detected !== this.locale) {
|
|
2285
|
+
onLocaleChange(detected);
|
|
2286
|
+
}
|
|
2287
|
+
};
|
|
2288
|
+
window.addEventListener("languagechange", handleLangChange);
|
|
2289
|
+
cleanups.push(() => window.removeEventListener("languagechange", handleLangChange));
|
|
2290
|
+
}
|
|
2291
|
+
return () => {
|
|
2292
|
+
cleanups.forEach((cleanup) => cleanup());
|
|
2293
|
+
};
|
|
2294
|
+
}
|
|
2295
|
+
resolveConfig(config, targetLocale) {
|
|
2296
|
+
var _a, _b;
|
|
2297
|
+
const baseLocale = (((_a = config.locale) == null ? void 0 : _a.default) || "es").toLowerCase();
|
|
2298
|
+
const locale = (targetLocale || this.locale || baseLocale).toLowerCase();
|
|
2299
|
+
const resolved = JSON.parse(JSON.stringify(config));
|
|
2300
|
+
const builtin = BUILTIN_TRANSLATIONS[locale];
|
|
2301
|
+
if (builtin) {
|
|
2302
|
+
const isBaseLocale = locale === baseLocale;
|
|
2303
|
+
this.applyTranslation(resolved, builtin, !isBaseLocale);
|
|
2304
|
+
}
|
|
2305
|
+
const custom = (_b = config.translations) == null ? void 0 : _b[locale];
|
|
2306
|
+
if (custom) {
|
|
2307
|
+
this.applyTranslation(resolved, custom, true);
|
|
2308
|
+
}
|
|
2309
|
+
return resolved;
|
|
2310
|
+
}
|
|
2311
|
+
applyTranslation(target, translation, overwriteExisting) {
|
|
2312
|
+
if (translation.policy) {
|
|
2313
|
+
if (!target.policy) target.policy = {};
|
|
2314
|
+
if (translation.policy.privacyUrl && (overwriteExisting || !target.policy.privacyUrl)) {
|
|
2315
|
+
target.policy.privacyUrl = translation.policy.privacyUrl;
|
|
2316
|
+
}
|
|
2317
|
+
if (translation.policy.cookiesUrl && (overwriteExisting || !target.policy.cookiesUrl)) {
|
|
2318
|
+
target.policy.cookiesUrl = translation.policy.cookiesUrl;
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
if (translation.ui) {
|
|
2322
|
+
if (!target.ui) target.ui = {};
|
|
2323
|
+
if (translation.ui.banner) {
|
|
2324
|
+
if (!target.ui.banner) target.ui.banner = {};
|
|
2325
|
+
for (const [key, value] of Object.entries(this.filterDefined(translation.ui.banner))) {
|
|
2326
|
+
if (overwriteExisting || !target.ui.banner[key]) {
|
|
2327
|
+
target.ui.banner[key] = value;
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
if (translation.ui.preferences) {
|
|
2332
|
+
if (!target.ui.preferences) target.ui.preferences = {};
|
|
2333
|
+
for (const [key, value] of Object.entries(this.filterDefined(translation.ui.preferences))) {
|
|
2334
|
+
if (overwriteExisting || !target.ui.preferences[key]) {
|
|
2335
|
+
target.ui.preferences[key] = value;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
if (translation.ui.floatingBadge) {
|
|
2340
|
+
if (typeof target.ui.floatingBadge === "object" && target.ui.floatingBadge !== null) {
|
|
2341
|
+
for (const [key, value] of Object.entries(this.filterDefined(translation.ui.floatingBadge))) {
|
|
2342
|
+
if (overwriteExisting || !target.ui.floatingBadge[key]) {
|
|
2343
|
+
target.ui.floatingBadge[key] = value;
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
} else if (target.ui.floatingBadge === true) {
|
|
2347
|
+
target.ui.floatingBadge = __spreadValues({
|
|
2348
|
+
enabled: true,
|
|
2349
|
+
position: "bottom-left",
|
|
2350
|
+
icon: "cookie"
|
|
2351
|
+
}, this.filterDefined(translation.ui.floatingBadge));
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
if (translation.categories && target.categories) {
|
|
2356
|
+
for (const [catId, catTrans] of Object.entries(translation.categories)) {
|
|
2357
|
+
if (target.categories[catId]) {
|
|
2358
|
+
if (catTrans.label && (overwriteExisting || !target.categories[catId].label)) {
|
|
2359
|
+
target.categories[catId].label = catTrans.label;
|
|
2360
|
+
}
|
|
2361
|
+
if (catTrans.description && (overwriteExisting || !target.categories[catId].description)) {
|
|
2362
|
+
target.categories[catId].description = catTrans.description;
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
if (translation.services && target.services) {
|
|
2368
|
+
for (const [srvId, srvTrans] of Object.entries(translation.services)) {
|
|
2369
|
+
if (target.services[srvId]) {
|
|
2370
|
+
if (srvTrans.label && (overwriteExisting || !target.services[srvId].label)) {
|
|
2371
|
+
target.services[srvId].label = srvTrans.label;
|
|
2372
|
+
}
|
|
2373
|
+
if (srvTrans.provider && (overwriteExisting || !target.services[srvId].provider)) {
|
|
2374
|
+
target.services[srvId].provider = srvTrans.provider;
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
filterDefined(obj) {
|
|
2381
|
+
const result = {};
|
|
2382
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
2383
|
+
if (value !== void 0 && value !== null) {
|
|
2384
|
+
result[key] = value;
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
return result;
|
|
2388
|
+
}
|
|
2389
|
+
};
|
|
2390
|
+
|
|
2391
|
+
// src/core/gpc.ts
|
|
2392
|
+
function detectGpcSignal() {
|
|
2393
|
+
var _a, _b, _c, _d;
|
|
2394
|
+
if (typeof window === "undefined" && typeof navigator === "undefined" && typeof globalThis === "undefined") {
|
|
2395
|
+
return false;
|
|
2396
|
+
}
|
|
2397
|
+
const nav = typeof navigator !== "undefined" ? navigator : null;
|
|
2398
|
+
const win = typeof window !== "undefined" ? window : null;
|
|
2399
|
+
const glob = typeof globalThis !== "undefined" ? globalThis : null;
|
|
2400
|
+
if ((nav == null ? void 0 : nav.globalPrivacyControl) === true || (win == null ? void 0 : win.globalPrivacyControl) === true || ((_a = win == null ? void 0 : win.navigator) == null ? void 0 : _a.globalPrivacyControl) === true || ((_b = glob == null ? void 0 : glob.navigator) == null ? void 0 : _b.globalPrivacyControl) === true || (glob == null ? void 0 : glob.globalPrivacyControl) === true) {
|
|
2401
|
+
return true;
|
|
2402
|
+
}
|
|
2403
|
+
if ((nav == null ? void 0 : nav.doNotTrack) === "1" || (win == null ? void 0 : win.doNotTrack) === "1" || ((_c = win == null ? void 0 : win.navigator) == null ? void 0 : _c.doNotTrack) === "1" || ((_d = glob == null ? void 0 : glob.navigator) == null ? void 0 : _d.doNotTrack) === "1" || (glob == null ? void 0 : glob.doNotTrack) === "1" || (nav == null ? void 0 : nav.msDoNotTrack) === "1" || (win == null ? void 0 : win.external) && typeof win.external.msTrackingProtectionEnabled === "function" && win.external.msTrackingProtectionEnabled()) {
|
|
2404
|
+
return true;
|
|
2405
|
+
}
|
|
2406
|
+
return false;
|
|
2407
|
+
}
|
|
2408
|
+
function resolveGpcConfig(config) {
|
|
2409
|
+
const gpc = config == null ? void 0 : config.gpc;
|
|
2410
|
+
return {
|
|
2411
|
+
enabled: (gpc == null ? void 0 : gpc.enabled) !== false,
|
|
2412
|
+
respectSignal: (gpc == null ? void 0 : gpc.respectSignal) !== false,
|
|
2413
|
+
mode: (gpc == null ? void 0 : gpc.mode) || "auto-reject",
|
|
2414
|
+
categories: (gpc == null ? void 0 : gpc.categories) || [],
|
|
2415
|
+
noticeText: (gpc == null ? void 0 : gpc.noticeText) || "Se\xF1al de Privacidad Global (GPC) detectada: cookies no esenciales desactivadas."
|
|
2416
|
+
};
|
|
2417
|
+
}
|
|
2418
|
+
function applyGpcChoices(config, baseChoices) {
|
|
2419
|
+
const result = __spreadValues({}, baseChoices || {});
|
|
2420
|
+
const gpcOptions = resolveGpcConfig(config);
|
|
2421
|
+
for (const [catId, catConfig] of Object.entries(config.categories)) {
|
|
2422
|
+
if (catConfig.required || catId === "necessary") {
|
|
2423
|
+
result[catId] = true;
|
|
2424
|
+
} else {
|
|
2425
|
+
if (gpcOptions.categories.length === 0 || gpcOptions.categories.includes(catId)) {
|
|
2426
|
+
result[catId] = false;
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
return result;
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
// src/core/state.ts
|
|
2434
|
+
var StateManager = class {
|
|
2435
|
+
constructor() {
|
|
2436
|
+
this.state = {
|
|
2437
|
+
initialized: false,
|
|
2438
|
+
policyVersion: "",
|
|
2439
|
+
locale: "es",
|
|
2440
|
+
receipt: null,
|
|
2441
|
+
choices: {}
|
|
2442
|
+
};
|
|
2443
|
+
this.config = null;
|
|
2444
|
+
}
|
|
2445
|
+
init(config, choices, receipt, gpc) {
|
|
2446
|
+
var _a, _b;
|
|
2447
|
+
this.config = config;
|
|
2448
|
+
this.state = {
|
|
2449
|
+
initialized: true,
|
|
2450
|
+
policyVersion: config.policyVersion,
|
|
2451
|
+
locale: ((_a = config.locale) == null ? void 0 : _a.default) || "es",
|
|
2452
|
+
receipt,
|
|
2453
|
+
choices,
|
|
2454
|
+
gpc: (_b = gpc != null ? gpc : receipt == null ? void 0 : receipt.gpc) != null ? _b : false
|
|
2455
|
+
};
|
|
2456
|
+
}
|
|
2457
|
+
setLocale(locale) {
|
|
2458
|
+
this.state.locale = locale;
|
|
2459
|
+
}
|
|
2460
|
+
getConfig() {
|
|
2461
|
+
return this.config;
|
|
2462
|
+
}
|
|
2463
|
+
getState() {
|
|
2464
|
+
return __spreadValues({}, this.state);
|
|
2465
|
+
}
|
|
2466
|
+
getChoices() {
|
|
2467
|
+
return __spreadValues({}, this.state.choices);
|
|
2468
|
+
}
|
|
2469
|
+
getReceipt() {
|
|
2470
|
+
return this.state.receipt;
|
|
2471
|
+
}
|
|
2472
|
+
hasCategory(category) {
|
|
2473
|
+
return this.state.choices[category] === true;
|
|
2474
|
+
}
|
|
2475
|
+
hasService(serviceId) {
|
|
2476
|
+
if (!this.config || !this.config.services) return false;
|
|
2477
|
+
const service = this.config.services[serviceId];
|
|
2478
|
+
if (!service || !service.category) return false;
|
|
2479
|
+
return this.hasCategory(service.category);
|
|
2480
|
+
}
|
|
2481
|
+
updateChoices(receipt) {
|
|
2482
|
+
this.state.receipt = receipt;
|
|
2483
|
+
this.state.choices = __spreadValues({}, receipt.choices);
|
|
2484
|
+
}
|
|
2485
|
+
clearChoices() {
|
|
2486
|
+
if (!this.config) return;
|
|
2487
|
+
const resetChoices = {};
|
|
2488
|
+
for (const [catId, catConfig] of Object.entries(this.config.categories)) {
|
|
2489
|
+
resetChoices[catId] = catConfig.required === true;
|
|
2490
|
+
}
|
|
2491
|
+
this.state.choices = resetChoices;
|
|
2492
|
+
this.state.receipt = null;
|
|
2493
|
+
}
|
|
2494
|
+
};
|
|
2495
|
+
|
|
2496
|
+
// src/core/events.ts
|
|
2497
|
+
var EventBus = class {
|
|
2498
|
+
constructor() {
|
|
2499
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
2500
|
+
}
|
|
2501
|
+
on(event, handler) {
|
|
2502
|
+
if (!this.handlers.has(event)) {
|
|
2503
|
+
this.handlers.set(event, /* @__PURE__ */ new Set());
|
|
2504
|
+
}
|
|
2505
|
+
const set = this.handlers.get(event);
|
|
2506
|
+
set.add(handler);
|
|
2507
|
+
return () => {
|
|
2508
|
+
set.delete(handler);
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2511
|
+
emit(event, detail) {
|
|
2512
|
+
const set = this.handlers.get(event);
|
|
2513
|
+
if (!set) return;
|
|
2514
|
+
for (const handler of set) {
|
|
2515
|
+
try {
|
|
2516
|
+
handler(detail);
|
|
2517
|
+
} catch (err) {
|
|
2518
|
+
console.error(`[ConsentSDK Event Error] Handler for '${event}' failed:`, err);
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
clear() {
|
|
2523
|
+
this.handlers.clear();
|
|
2524
|
+
}
|
|
2525
|
+
};
|
|
2526
|
+
|
|
2527
|
+
// src/core/policy-engine.ts
|
|
2528
|
+
function evaluatePolicy(config, receipt) {
|
|
2529
|
+
var _a, _b, _c;
|
|
2530
|
+
const defaultChoices = {};
|
|
2531
|
+
for (const [catId, catConfig] of Object.entries(config.categories)) {
|
|
2532
|
+
defaultChoices[catId] = catConfig.required === true;
|
|
2533
|
+
}
|
|
2534
|
+
if (!receipt) {
|
|
2535
|
+
return {
|
|
2536
|
+
isValid: false,
|
|
2537
|
+
reason: "missing",
|
|
2538
|
+
choices: defaultChoices
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2541
|
+
if (receipt.policyVersion !== config.policyVersion) {
|
|
2542
|
+
return {
|
|
2543
|
+
isValid: false,
|
|
2544
|
+
reason: "policy_version_changed",
|
|
2545
|
+
choices: defaultChoices
|
|
2546
|
+
};
|
|
2547
|
+
}
|
|
2548
|
+
const maxAgeDays = (_b = (_a = config.consent) == null ? void 0 : _a.maxAgeDays) != null ? _b : 365;
|
|
2549
|
+
if (isReceiptExpired(receipt, maxAgeDays)) {
|
|
2550
|
+
return {
|
|
2551
|
+
isValid: false,
|
|
2552
|
+
reason: "expired",
|
|
2553
|
+
choices: defaultChoices
|
|
2554
|
+
};
|
|
2555
|
+
}
|
|
2556
|
+
if (receipt.signature) {
|
|
2557
|
+
const payloadToSign = `${receipt.receiptId}:${receipt.policyVersion}:${JSON.stringify(receipt.choices)}`;
|
|
2558
|
+
const expectedSignature = computeReceiptSignature(payloadToSign, (_c = config.security) == null ? void 0 : _c.secretKey);
|
|
2559
|
+
if (receipt.signature !== expectedSignature) {
|
|
2560
|
+
return {
|
|
2561
|
+
isValid: false,
|
|
2562
|
+
reason: "tampered",
|
|
2563
|
+
choices: defaultChoices
|
|
2564
|
+
};
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
const activeChoices = __spreadValues({}, defaultChoices);
|
|
2568
|
+
for (const catId of Object.keys(config.categories)) {
|
|
2569
|
+
if (catId === "necessary") {
|
|
2570
|
+
activeChoices[catId] = true;
|
|
2571
|
+
} else if (typeof receipt.choices[catId] === "boolean") {
|
|
2572
|
+
activeChoices[catId] = receipt.choices[catId];
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
return {
|
|
2576
|
+
isValid: true,
|
|
2577
|
+
reason: "valid",
|
|
2578
|
+
choices: activeChoices
|
|
2579
|
+
};
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
// src/blocker/registry.ts
|
|
2583
|
+
var BlockerRegistry = class {
|
|
2584
|
+
constructor() {
|
|
2585
|
+
this.observer = null;
|
|
2586
|
+
}
|
|
2587
|
+
init(isCategoryAllowed, isServiceAllowed, iframeOptions, onServiceLoaded) {
|
|
2588
|
+
const runBlockers = () => {
|
|
2589
|
+
ScriptGate.scanAndActivate(
|
|
2590
|
+
isCategoryAllowed,
|
|
2591
|
+
isServiceAllowed,
|
|
2592
|
+
onServiceLoaded
|
|
2593
|
+
);
|
|
2594
|
+
IframeGate.processIframes(
|
|
2595
|
+
isCategoryAllowed,
|
|
2596
|
+
isServiceAllowed,
|
|
2597
|
+
iframeOptions
|
|
2598
|
+
);
|
|
2599
|
+
ResourceGate.processImages(isCategoryAllowed, isServiceAllowed);
|
|
2600
|
+
};
|
|
2601
|
+
runBlockers();
|
|
2602
|
+
if (typeof MutationObserver !== "undefined" && typeof document !== "undefined") {
|
|
2603
|
+
this.observer = new MutationObserver(() => {
|
|
2604
|
+
runBlockers();
|
|
2605
|
+
});
|
|
2606
|
+
this.observer.observe(document.documentElement || document.body, {
|
|
2607
|
+
childList: true,
|
|
2608
|
+
subtree: true
|
|
2609
|
+
});
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
destroy() {
|
|
2613
|
+
if (this.observer) {
|
|
2614
|
+
this.observer.disconnect();
|
|
2615
|
+
this.observer = null;
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
};
|
|
2619
|
+
|
|
2620
|
+
// src/services/custom.ts
|
|
2621
|
+
var CustomServiceAdapter = class {
|
|
2622
|
+
static createWhen(eventBus, isCategoryAllowed, isServiceAllowed) {
|
|
2623
|
+
return function when(categoryOrService, callback) {
|
|
2624
|
+
if (isCategoryAllowed(categoryOrService) || isServiceAllowed(categoryOrService)) {
|
|
2625
|
+
try {
|
|
2626
|
+
callback();
|
|
2627
|
+
} catch (err) {
|
|
2628
|
+
console.error(`[ConsentSDK when()] Callback execution failed:`, err);
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
return eventBus.on("consent:changed", ({ choices }) => {
|
|
2632
|
+
if (choices[categoryOrService] === true || isServiceAllowed(categoryOrService)) {
|
|
2633
|
+
try {
|
|
2634
|
+
callback();
|
|
2635
|
+
} catch (err) {
|
|
2636
|
+
console.error(`[ConsentSDK when()] Callback execution failed:`, err);
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
});
|
|
2640
|
+
};
|
|
2641
|
+
}
|
|
2642
|
+
};
|
|
2643
|
+
|
|
2644
|
+
// src/ui/banner.ts
|
|
2645
|
+
var ConsentBanner = class {
|
|
2646
|
+
constructor() {
|
|
2647
|
+
this.element = null;
|
|
2648
|
+
}
|
|
2649
|
+
render(config, handlers) {
|
|
2650
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
2651
|
+
if (typeof document === "undefined") return;
|
|
2652
|
+
this.remove();
|
|
2653
|
+
injectStyles((_a = config.csp) == null ? void 0 : _a.nonce);
|
|
2654
|
+
const bannerConfig = ((_b = config.ui) == null ? void 0 : _b.banner) || {};
|
|
2655
|
+
const titleText = sanitizeHtml(bannerConfig.title || "Tu privacidad, bajo tu control");
|
|
2656
|
+
const descText = sanitizeHtml(
|
|
2657
|
+
bannerConfig.description || "Usamos tecnolog\xEDas necesarias para el funcionamiento del sitio. Con tu permiso, tambi\xE9n podemos utilizar anal\xEDtica y marketing."
|
|
2658
|
+
);
|
|
2659
|
+
const acceptText = sanitizeHtml(bannerConfig.accept || "Aceptar todas");
|
|
2660
|
+
const rejectText = sanitizeHtml(bannerConfig.reject || "Rechazar todas");
|
|
2661
|
+
const configureText = sanitizeHtml(bannerConfig.configure || "Configurar");
|
|
2662
|
+
const cookiesPolicyText = sanitizeHtml(bannerConfig.cookiesPolicy || "Pol\xEDtica de cookies");
|
|
2663
|
+
const privacyPolicyText = sanitizeHtml(bannerConfig.privacyPolicy || "Pol\xEDtica de privacidad");
|
|
2664
|
+
const bannerAriaLabel = sanitizeHtml(bannerConfig.ariaLabel || "Gesti\xF3n de consentimiento de privacidad");
|
|
2665
|
+
const privacyUrl = sanitizeUrl(((_c = config.policy) == null ? void 0 : _c.privacyUrl) || "/politica-privacidad");
|
|
2666
|
+
const cookiesUrl = sanitizeUrl(((_d = config.policy) == null ? void 0 : _d.cookiesUrl) || "/politica-cookies");
|
|
2667
|
+
const wrapper = document.createElement("div");
|
|
2668
|
+
wrapper.className = "consent-banner-wrapper";
|
|
2669
|
+
wrapper.setAttribute("role", "region");
|
|
2670
|
+
wrapper.setAttribute(
|
|
2671
|
+
"aria-label",
|
|
2672
|
+
bannerAriaLabel
|
|
2673
|
+
);
|
|
2674
|
+
wrapper.innerHTML = `
|
|
2675
|
+
<div class="consent-banner-container">
|
|
2676
|
+
<div class="consent-banner-text">
|
|
2677
|
+
<h2 class="consent-banner-title">
|
|
2678
|
+
<span class="consent-banner-title-icon">
|
|
2679
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
|
2680
|
+
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
|
2681
|
+
</svg>
|
|
2682
|
+
</span>
|
|
2683
|
+
${titleText}
|
|
2684
|
+
</h2>
|
|
2685
|
+
<p class="consent-banner-desc">${descText}</p>
|
|
2686
|
+
<div class="consent-banner-links">
|
|
2687
|
+
<a href="${cookiesUrl}" target="_blank" rel="noopener">${cookiesPolicyText}</a>
|
|
2688
|
+
<a href="${privacyUrl}" target="_blank" rel="noopener">${privacyPolicyText}</a>
|
|
2689
|
+
</div>
|
|
2690
|
+
</div>
|
|
2691
|
+
<div class="consent-banner-actions">
|
|
2692
|
+
<button type="button" class="consent-btn consent-btn-reject" id="consent-btn-reject">
|
|
2693
|
+
${rejectText}
|
|
2694
|
+
</button>
|
|
2695
|
+
<button type="button" class="consent-btn consent-btn-configure" id="consent-btn-configure">
|
|
2696
|
+
${configureText}
|
|
2697
|
+
</button>
|
|
2698
|
+
<button type="button" class="consent-btn consent-btn-accept" id="consent-btn-accept">
|
|
2699
|
+
${acceptText}
|
|
2700
|
+
</button>
|
|
2701
|
+
</div>
|
|
2702
|
+
</div>
|
|
2703
|
+
`;
|
|
2704
|
+
(_e = wrapper.querySelector("#consent-btn-reject")) == null ? void 0 : _e.addEventListener("click", () => {
|
|
2705
|
+
handlers.onRejectAll();
|
|
2706
|
+
this.remove();
|
|
2707
|
+
});
|
|
2708
|
+
(_f = wrapper.querySelector("#consent-btn-configure")) == null ? void 0 : _f.addEventListener("click", () => {
|
|
2709
|
+
handlers.onConfigure();
|
|
2710
|
+
});
|
|
2711
|
+
(_g = wrapper.querySelector("#consent-btn-accept")) == null ? void 0 : _g.addEventListener("click", () => {
|
|
2712
|
+
handlers.onAcceptAll();
|
|
2713
|
+
this.remove();
|
|
2714
|
+
});
|
|
2715
|
+
document.body.appendChild(wrapper);
|
|
2716
|
+
this.element = wrapper;
|
|
2717
|
+
}
|
|
2718
|
+
getIsVisible() {
|
|
2719
|
+
return this.element !== null;
|
|
2720
|
+
}
|
|
2721
|
+
remove() {
|
|
2722
|
+
if (this.element && this.element.parentNode) {
|
|
2723
|
+
this.element.parentNode.removeChild(this.element);
|
|
2724
|
+
this.element = null;
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
};
|
|
2728
|
+
|
|
2729
|
+
// src/ui/preferences.ts
|
|
2730
|
+
var PreferencesModal = class {
|
|
2731
|
+
constructor() {
|
|
2732
|
+
this.backdrop = null;
|
|
2733
|
+
this.lastFocusedElement = null;
|
|
2734
|
+
}
|
|
2735
|
+
render(config, currentChoices, handlers) {
|
|
2736
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
2737
|
+
if (typeof document === "undefined") return;
|
|
2738
|
+
this.close();
|
|
2739
|
+
injectStyles((_a = config.csp) == null ? void 0 : _a.nonce);
|
|
2740
|
+
this.lastFocusedElement = document.activeElement;
|
|
2741
|
+
const prefConfig = ((_b = config.ui) == null ? void 0 : _b.preferences) || {};
|
|
2742
|
+
const modalTitle = sanitizeHtml(prefConfig.title || "Preferencias de privacidad");
|
|
2743
|
+
const modalSubtitle = sanitizeHtml(
|
|
2744
|
+
prefConfig.subtitle || "Gestiona tus permisos de almacenamiento por finalidad (LSSI art. 22.2 & RGPD)."
|
|
2745
|
+
);
|
|
2746
|
+
const saveText = sanitizeHtml(prefConfig.save || "Guardar selecci\xF3n");
|
|
2747
|
+
const acceptAllText = sanitizeHtml(prefConfig.acceptAll || "Permitir todas");
|
|
2748
|
+
const rejectAllText = sanitizeHtml(prefConfig.rejectAll || "Rechazar opcionales");
|
|
2749
|
+
const closeLabel = sanitizeHtml(prefConfig.closeLabel || "Cerrar ventana");
|
|
2750
|
+
const requiredBadge = sanitizeHtml(prefConfig.requiredBadge || "Requerida");
|
|
2751
|
+
const optionalBadge = sanitizeHtml(prefConfig.optionalBadge || "Opcional");
|
|
2752
|
+
const servicesLabel = sanitizeHtml(prefConfig.servicesLabel || "Servicios incluidos");
|
|
2753
|
+
const viewServices = sanitizeHtml(prefConfig.viewServices || "Ver servicios");
|
|
2754
|
+
const hideServices = sanitizeHtml(prefConfig.hideServices || "Ocultar servicios");
|
|
2755
|
+
const thirdParty = sanitizeHtml(prefConfig.thirdParty || "Terceros");
|
|
2756
|
+
const backdrop = document.createElement("div");
|
|
2757
|
+
backdrop.className = "consent-dialog-backdrop";
|
|
2758
|
+
const dialog = document.createElement("div");
|
|
2759
|
+
dialog.className = "consent-dialog";
|
|
2760
|
+
dialog.setAttribute("role", "dialog");
|
|
2761
|
+
dialog.setAttribute("aria-modal", "true");
|
|
2762
|
+
dialog.setAttribute("aria-labelledby", "consent-dialog-title-id");
|
|
2763
|
+
const header = document.createElement("div");
|
|
2764
|
+
header.className = "consent-dialog-header";
|
|
2765
|
+
header.innerHTML = `
|
|
2766
|
+
<div>
|
|
2767
|
+
<h2 class="consent-dialog-title" id="consent-dialog-title-id">${modalTitle}</h2>
|
|
2768
|
+
<p style="margin: 0.2rem 0 0 0; font-size: 0.84rem; color: var(--consent-muted); font-weight: 400;">
|
|
2769
|
+
${modalSubtitle}
|
|
2770
|
+
</p>
|
|
2771
|
+
</div>
|
|
2772
|
+
<button type="button" class="consent-dialog-close" aria-label="${closeLabel}">
|
|
2773
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round">
|
|
2774
|
+
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
2775
|
+
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
2776
|
+
</svg>
|
|
2777
|
+
</button>
|
|
2778
|
+
`;
|
|
2779
|
+
const body = document.createElement("div");
|
|
2780
|
+
body.className = "consent-dialog-body";
|
|
2781
|
+
const categoryChoices = __spreadValues({}, currentChoices);
|
|
2782
|
+
for (const [catId, catConfig] of Object.entries(config.categories)) {
|
|
2783
|
+
const catRow = document.createElement("div");
|
|
2784
|
+
catRow.className = "consent-category-row";
|
|
2785
|
+
const isRequired = catConfig.required === true;
|
|
2786
|
+
const isChecked = isRequired ? true : (_c = categoryChoices[catId]) != null ? _c : false;
|
|
2787
|
+
const associatedServices = config.services ? Object.entries(config.services).filter(([_, srv]) => srv.category === catId) : [];
|
|
2788
|
+
let servicesHtml = "";
|
|
2789
|
+
if (associatedServices.length > 0) {
|
|
2790
|
+
servicesHtml = `
|
|
2791
|
+
<div class="consent-category-services" id="cat-services-${catId}" style="display: none; margin-top: 0.75rem; padding-top: 0.6rem; border-top: 1px dashed var(--consent-divider); font-size: 0.82rem;">
|
|
2792
|
+
<div style="font-weight: 600; color: var(--consent-muted); margin-bottom: 0.4rem; font-size: 0.75rem; letter-spacing: 0.02em;">
|
|
2793
|
+
${servicesLabel} (${associatedServices.length})
|
|
2794
|
+
</div>
|
|
2795
|
+
<div style="display: flex; flex-direction: column; gap: 0.3rem;">
|
|
2796
|
+
${associatedServices.map(
|
|
2797
|
+
([srvId, srv]) => `
|
|
2798
|
+
<div style="display: flex; justify-content: space-between; align-items: center; padding: 0.2rem 0;">
|
|
2799
|
+
<span style="font-weight: 500;">${sanitizeHtml(srv.label || srvId)}</span>
|
|
2800
|
+
<span style="color: var(--consent-muted); font-size: 0.78rem;">${sanitizeHtml(srv.provider || thirdParty)}</span>
|
|
2801
|
+
</div>
|
|
2802
|
+
`
|
|
2803
|
+
).join("")}
|
|
2804
|
+
</div>
|
|
2805
|
+
</div>
|
|
2806
|
+
`;
|
|
2807
|
+
}
|
|
2808
|
+
catRow.innerHTML = `
|
|
2809
|
+
<div class="consent-category-header">
|
|
2810
|
+
<div style="flex: 1; padding-right: 1rem;">
|
|
2811
|
+
<div style="display: flex; align-items: center; gap: 0.4rem;">
|
|
2812
|
+
<span class="consent-category-name">${sanitizeHtml(catConfig.label)}</span>
|
|
2813
|
+
${isRequired ? `<span class="consent-badge consent-badge-required">${requiredBadge}</span>` : `<span class="consent-badge consent-badge-optional">${optionalBadge}</span>`}
|
|
2814
|
+
</div>
|
|
2815
|
+
<p class="consent-category-desc">${sanitizeHtml(catConfig.description)}</p>
|
|
2816
|
+
${associatedServices.length > 0 ? `<button type="button" class="consent-toggle-services-btn" data-target="cat-services-${catId}">
|
|
2817
|
+
${viewServices} (${associatedServices.length}) \u25BE
|
|
2818
|
+
</button>` : ""}
|
|
2819
|
+
</div>
|
|
2820
|
+
<label class="consent-toggle">
|
|
2821
|
+
<input type="checkbox" id="cat-toggle-${catId}" ${isChecked ? "checked" : ""} ${isRequired ? "disabled" : ""}>
|
|
2822
|
+
<span class="consent-toggle-slider"></span>
|
|
2823
|
+
</label>
|
|
2824
|
+
</div>
|
|
2825
|
+
${servicesHtml}
|
|
2826
|
+
`;
|
|
2827
|
+
if (!isRequired) {
|
|
2828
|
+
const checkbox = catRow.querySelector(
|
|
2829
|
+
`#cat-toggle-${catId}`
|
|
2830
|
+
);
|
|
2831
|
+
checkbox == null ? void 0 : checkbox.addEventListener("change", (e) => {
|
|
2832
|
+
categoryChoices[catId] = e.target.checked;
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2835
|
+
const toggleServicesBtn = catRow.querySelector(
|
|
2836
|
+
".consent-toggle-services-btn"
|
|
2837
|
+
);
|
|
2838
|
+
if (toggleServicesBtn) {
|
|
2839
|
+
toggleServicesBtn.addEventListener("click", () => {
|
|
2840
|
+
const targetId = toggleServicesBtn.getAttribute("data-target");
|
|
2841
|
+
if (targetId) {
|
|
2842
|
+
const targetEl = catRow.querySelector(`#${targetId}`);
|
|
2843
|
+
if (targetEl) {
|
|
2844
|
+
const isHidden = targetEl.style.display === "none";
|
|
2845
|
+
targetEl.style.display = isHidden ? "block" : "none";
|
|
2846
|
+
toggleServicesBtn.textContent = isHidden ? `${hideServices} (${associatedServices.length}) \u25B4` : `${viewServices} (${associatedServices.length}) \u25BE`;
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
});
|
|
2850
|
+
}
|
|
2851
|
+
body.appendChild(catRow);
|
|
2852
|
+
}
|
|
2853
|
+
const footer = document.createElement("div");
|
|
2854
|
+
footer.className = "consent-dialog-footer";
|
|
2855
|
+
footer.innerHTML = `
|
|
2856
|
+
<button type="button" class="consent-btn consent-btn-reject" id="consent-pref-reject">
|
|
2857
|
+
${rejectAllText}
|
|
2858
|
+
</button>
|
|
2859
|
+
<div style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
|
|
2860
|
+
<button type="button" class="consent-btn consent-btn-configure" id="consent-pref-accept">
|
|
2861
|
+
${acceptAllText}
|
|
2862
|
+
</button>
|
|
2863
|
+
<button type="button" class="consent-btn consent-btn-accept" id="consent-pref-save">
|
|
2864
|
+
${saveText}
|
|
2865
|
+
</button>
|
|
2866
|
+
</div>
|
|
2867
|
+
`;
|
|
2868
|
+
dialog.appendChild(header);
|
|
2869
|
+
dialog.appendChild(body);
|
|
2870
|
+
dialog.appendChild(footer);
|
|
2871
|
+
backdrop.appendChild(dialog);
|
|
2872
|
+
(_d = header.querySelector(".consent-dialog-close")) == null ? void 0 : _d.addEventListener("click", () => {
|
|
2873
|
+
handlers.onClose();
|
|
2874
|
+
this.close();
|
|
2875
|
+
});
|
|
2876
|
+
backdrop.addEventListener("click", (e) => {
|
|
2877
|
+
if (e.target === backdrop) {
|
|
2878
|
+
handlers.onClose();
|
|
2879
|
+
this.close();
|
|
2880
|
+
}
|
|
2881
|
+
});
|
|
2882
|
+
(_e = footer.querySelector("#consent-pref-reject")) == null ? void 0 : _e.addEventListener("click", () => {
|
|
2883
|
+
handlers.onRejectAll();
|
|
2884
|
+
this.close();
|
|
2885
|
+
});
|
|
2886
|
+
(_f = footer.querySelector("#consent-pref-accept")) == null ? void 0 : _f.addEventListener("click", () => {
|
|
2887
|
+
handlers.onAcceptAll();
|
|
2888
|
+
this.close();
|
|
2889
|
+
});
|
|
2890
|
+
(_g = footer.querySelector("#consent-pref-save")) == null ? void 0 : _g.addEventListener("click", () => {
|
|
2891
|
+
handlers.onSave(categoryChoices);
|
|
2892
|
+
this.close();
|
|
2893
|
+
});
|
|
2894
|
+
const handleKeyDown = (e) => {
|
|
2895
|
+
if (e.key === "Escape") {
|
|
2896
|
+
handlers.onClose();
|
|
2897
|
+
this.close();
|
|
2898
|
+
return;
|
|
2899
|
+
}
|
|
2900
|
+
if (e.key === "Tab") {
|
|
2901
|
+
const focusableElements = dialog.querySelectorAll(
|
|
2902
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
2903
|
+
);
|
|
2904
|
+
const firstElement = focusableElements[0];
|
|
2905
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
2906
|
+
if (e.shiftKey) {
|
|
2907
|
+
if (document.activeElement === firstElement) {
|
|
2908
|
+
lastElement == null ? void 0 : lastElement.focus();
|
|
2909
|
+
e.preventDefault();
|
|
2910
|
+
}
|
|
2911
|
+
} else {
|
|
2912
|
+
if (document.activeElement === lastElement) {
|
|
2913
|
+
firstElement == null ? void 0 : firstElement.focus();
|
|
2914
|
+
e.preventDefault();
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
};
|
|
2919
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
2920
|
+
this._keyListener = handleKeyDown;
|
|
2921
|
+
document.body.appendChild(backdrop);
|
|
2922
|
+
this.backdrop = backdrop;
|
|
2923
|
+
const firstFocusable = dialog.querySelector("button");
|
|
2924
|
+
firstFocusable == null ? void 0 : firstFocusable.focus();
|
|
2925
|
+
}
|
|
2926
|
+
getIsOpen() {
|
|
2927
|
+
return this.backdrop !== null;
|
|
2928
|
+
}
|
|
2929
|
+
close() {
|
|
2930
|
+
if (this._keyListener) {
|
|
2931
|
+
document.removeEventListener("keydown", this._keyListener);
|
|
2932
|
+
delete this._keyListener;
|
|
2933
|
+
}
|
|
2934
|
+
if (this.backdrop && this.backdrop.parentNode) {
|
|
2935
|
+
this.backdrop.parentNode.removeChild(this.backdrop);
|
|
2936
|
+
this.backdrop = null;
|
|
2937
|
+
}
|
|
2938
|
+
if (this.lastFocusedElement && typeof this.lastFocusedElement.focus === "function") {
|
|
2939
|
+
this.lastFocusedElement.focus();
|
|
2940
|
+
this.lastFocusedElement = null;
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
};
|
|
2944
|
+
|
|
2945
|
+
// src/core/consent-engine.ts
|
|
2946
|
+
var ConsentEngine = class {
|
|
2947
|
+
constructor() {
|
|
2948
|
+
this.stateManager = new StateManager();
|
|
2949
|
+
this.eventBus = new EventBus();
|
|
2950
|
+
this.blockerRegistry = new BlockerRegistry();
|
|
2951
|
+
this.banner = new ConsentBanner();
|
|
2952
|
+
this.preferencesModal = new PreferencesModal();
|
|
2953
|
+
this.floatingBadge = new FloatingBadge();
|
|
2954
|
+
this.i18n = new I18nEngine();
|
|
2955
|
+
this.stopLocaleSync = null;
|
|
2956
|
+
this.initPromise = null;
|
|
2957
|
+
this.resolveReady = null;
|
|
2958
|
+
this.readyPromise = new Promise((resolve) => {
|
|
2959
|
+
this.resolveReady = resolve;
|
|
2960
|
+
});
|
|
2961
|
+
}
|
|
2962
|
+
async init(configInput) {
|
|
2963
|
+
if (this.initPromise) return this.initPromise;
|
|
2964
|
+
this.initPromise = (async () => {
|
|
2965
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
2966
|
+
let config;
|
|
2967
|
+
if (typeof configInput === "string") {
|
|
2968
|
+
const response = await fetch(configInput);
|
|
2969
|
+
if (!response.ok) {
|
|
2970
|
+
throw new Error(
|
|
2971
|
+
`[ConsentSDK] Failed to fetch consent configuration from '${configInput}' (HTTP ${response.status})`
|
|
2972
|
+
);
|
|
2973
|
+
}
|
|
2974
|
+
config = await response.json();
|
|
2975
|
+
} else {
|
|
2976
|
+
config = configInput;
|
|
2977
|
+
}
|
|
2978
|
+
config = resolveConfigPresets(config);
|
|
2979
|
+
validateConfig(config);
|
|
2980
|
+
const initialLocale = this.i18n.detectParentLocale(config);
|
|
2981
|
+
this.i18n.setLocale(initialLocale);
|
|
2982
|
+
(_a = this.stopLocaleSync) == null ? void 0 : _a.call(this);
|
|
2983
|
+
this.stopLocaleSync = this.i18n.startParentSync(config, (newLocale) => {
|
|
2984
|
+
this.setLocale(newLocale);
|
|
2985
|
+
});
|
|
2986
|
+
const cookieName = ((_b = config.storage) == null ? void 0 : _b.name) || "site_consent";
|
|
2987
|
+
const rawReceipt = ((_c = config.storage) == null ? void 0 : _c.type) === "memory" ? MemoryStore.get(cookieName) : CookieStore.get(cookieName);
|
|
2988
|
+
const savedReceipt = rawReceipt ? parseReceipt(rawReceipt) : null;
|
|
2989
|
+
const evalResult = evaluatePolicy(config, savedReceipt);
|
|
2990
|
+
const isGpcDetected = detectGpcSignal();
|
|
2991
|
+
const gpcConfig = resolveGpcConfig(config);
|
|
2992
|
+
const isGpcActive = isGpcDetected && gpcConfig.enabled && gpcConfig.respectSignal;
|
|
2993
|
+
let activeReceipt = evalResult.isValid ? savedReceipt : null;
|
|
2994
|
+
let activeChoices = evalResult.choices;
|
|
2995
|
+
let autoAppliedGpc = false;
|
|
2996
|
+
if (!evalResult.isValid && isGpcActive) {
|
|
2997
|
+
if (gpcConfig.mode !== "notice-only") {
|
|
2998
|
+
activeChoices = applyGpcChoices(config);
|
|
2999
|
+
activeReceipt = createReceipt(
|
|
3000
|
+
config.policyVersion,
|
|
3001
|
+
activeChoices,
|
|
3002
|
+
"gpc",
|
|
3003
|
+
null,
|
|
3004
|
+
(_d = config.security) == null ? void 0 : _d.secretKey
|
|
3005
|
+
);
|
|
3006
|
+
activeReceipt.gpc = true;
|
|
3007
|
+
autoAppliedGpc = true;
|
|
3008
|
+
const receiptJson = JSON.stringify(activeReceipt);
|
|
3009
|
+
if (((_e = config.storage) == null ? void 0 : _e.type) === "memory") {
|
|
3010
|
+
MemoryStore.set(cookieName, receiptJson);
|
|
3011
|
+
} else {
|
|
3012
|
+
CookieStore.set(cookieName, receiptJson, {
|
|
3013
|
+
path: ((_f = config.storage) == null ? void 0 : _f.path) || "/",
|
|
3014
|
+
maxAgeDays: (_h = (_g = config.consent) == null ? void 0 : _g.maxAgeDays) != null ? _h : 365,
|
|
3015
|
+
sameSite: ((_i = config.storage) == null ? void 0 : _i.sameSite) || "Lax",
|
|
3016
|
+
secure: (_j = config.storage) == null ? void 0 : _j.secure,
|
|
3017
|
+
domain: (_k = config.storage) == null ? void 0 : _k.domain
|
|
3018
|
+
});
|
|
3019
|
+
}
|
|
3020
|
+
for (const [catId, allowed] of Object.entries(activeChoices)) {
|
|
3021
|
+
if (!allowed && catId !== "necessary") {
|
|
3022
|
+
StorageCleaner.purgeCategory(config, catId);
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
this.stateManager.init(config, activeChoices, activeReceipt, isGpcDetected);
|
|
3028
|
+
this.stateManager.setLocale(initialLocale);
|
|
3029
|
+
if (isGpcDetected) {
|
|
3030
|
+
this.eventBus.emit("gpc:detected", {
|
|
3031
|
+
signal: true,
|
|
3032
|
+
autoApplied: autoAppliedGpc,
|
|
3033
|
+
choices: activeChoices
|
|
3034
|
+
});
|
|
3035
|
+
this.dispatchDomEvent("solvenza:gpc", {
|
|
3036
|
+
signal: true,
|
|
3037
|
+
autoApplied: autoAppliedGpc,
|
|
3038
|
+
choices: activeChoices
|
|
3039
|
+
});
|
|
3040
|
+
}
|
|
3041
|
+
GoogleConsentAdapter.initDefault();
|
|
3042
|
+
if (evalResult.isValid || autoAppliedGpc) {
|
|
3043
|
+
GoogleConsentAdapter.update(activeChoices);
|
|
3044
|
+
this.dispatchDomEvent("solvenza:restored", { state: this.getConsent() });
|
|
3045
|
+
}
|
|
3046
|
+
this.blockerRegistry.init(
|
|
3047
|
+
(cat) => this.has(cat),
|
|
3048
|
+
(srv) => this.hasService(srv),
|
|
3049
|
+
{
|
|
3050
|
+
onAllowClick: (category) => {
|
|
3051
|
+
const current = this.stateManager.getChoices();
|
|
3052
|
+
current[category] = true;
|
|
3053
|
+
this.setPreferences(current);
|
|
3054
|
+
}
|
|
3055
|
+
},
|
|
3056
|
+
(serviceId, category) => {
|
|
3057
|
+
this.eventBus.emit("service:loaded", { serviceId, category });
|
|
3058
|
+
}
|
|
3059
|
+
);
|
|
3060
|
+
this.setupGlobalRevocationTrigger();
|
|
3061
|
+
const resolvedConfig = this.getResolvedConfig() || config;
|
|
3062
|
+
const badgeConfig = this.resolveFloatingBadgeConfig(resolvedConfig);
|
|
3063
|
+
if (badgeConfig.enabled) {
|
|
3064
|
+
this.floatingBadge.render(resolvedConfig, {
|
|
3065
|
+
onClick: () => this.openPreferences()
|
|
3066
|
+
});
|
|
3067
|
+
if (evalResult.isValid || autoAppliedGpc || badgeConfig.visibility === "always") {
|
|
3068
|
+
this.showFloatingBadge();
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
(_l = this.resolveReady) == null ? void 0 : _l.call(this);
|
|
3072
|
+
this.eventBus.emit("ready", { state: this.getConsent() });
|
|
3073
|
+
if (!evalResult.isValid && !autoAppliedGpc) {
|
|
3074
|
+
this.showBanner();
|
|
3075
|
+
}
|
|
3076
|
+
})();
|
|
3077
|
+
return this.initPromise;
|
|
3078
|
+
}
|
|
3079
|
+
async ready() {
|
|
3080
|
+
return this.readyPromise;
|
|
3081
|
+
}
|
|
3082
|
+
getConsent() {
|
|
3083
|
+
return this.stateManager.getState();
|
|
3084
|
+
}
|
|
3085
|
+
has(category) {
|
|
3086
|
+
return this.stateManager.hasCategory(category);
|
|
3087
|
+
}
|
|
3088
|
+
hasService(serviceId) {
|
|
3089
|
+
return this.stateManager.hasService(serviceId);
|
|
3090
|
+
}
|
|
3091
|
+
acceptAll() {
|
|
3092
|
+
const config = this.stateManager.getConfig();
|
|
3093
|
+
if (!config) return;
|
|
3094
|
+
const choices = {};
|
|
3095
|
+
for (const catId of Object.keys(config.categories)) {
|
|
3096
|
+
choices[catId] = true;
|
|
3097
|
+
}
|
|
3098
|
+
this.saveChoices(choices, "banner");
|
|
3099
|
+
this.eventBus.emit("consent:accepted", {
|
|
3100
|
+
choices,
|
|
3101
|
+
receipt: this.getReceipt()
|
|
3102
|
+
});
|
|
3103
|
+
}
|
|
3104
|
+
rejectAll() {
|
|
3105
|
+
const config = this.stateManager.getConfig();
|
|
3106
|
+
if (!config) return;
|
|
3107
|
+
const choices = {};
|
|
3108
|
+
for (const [catId, catConfig] of Object.entries(config.categories)) {
|
|
3109
|
+
choices[catId] = catConfig.required === true;
|
|
3110
|
+
}
|
|
3111
|
+
this.saveChoices(choices, "banner");
|
|
3112
|
+
this.eventBus.emit("consent:rejected", {
|
|
3113
|
+
choices,
|
|
3114
|
+
receipt: this.getReceipt()
|
|
3115
|
+
});
|
|
3116
|
+
}
|
|
3117
|
+
setPreferences(choices) {
|
|
3118
|
+
const config = this.stateManager.getConfig();
|
|
3119
|
+
if (!config) return;
|
|
3120
|
+
const sanitizedChoices = __spreadValues({}, choices);
|
|
3121
|
+
sanitizedChoices["necessary"] = true;
|
|
3122
|
+
this.saveChoices(sanitizedChoices, "preferences");
|
|
3123
|
+
}
|
|
3124
|
+
openPreferences() {
|
|
3125
|
+
const config = this.getResolvedConfig();
|
|
3126
|
+
if (!config) return;
|
|
3127
|
+
this.hideFloatingBadge();
|
|
3128
|
+
this.preferencesModal.render(config, this.stateManager.getChoices(), {
|
|
3129
|
+
onSave: (choices) => {
|
|
3130
|
+
this.setPreferences(choices);
|
|
3131
|
+
this.restoreFloatingBadgeIfNeeded();
|
|
3132
|
+
},
|
|
3133
|
+
onAcceptAll: () => {
|
|
3134
|
+
this.acceptAll();
|
|
3135
|
+
this.restoreFloatingBadgeIfNeeded();
|
|
3136
|
+
},
|
|
3137
|
+
onRejectAll: () => {
|
|
3138
|
+
this.rejectAll();
|
|
3139
|
+
this.restoreFloatingBadgeIfNeeded();
|
|
3140
|
+
},
|
|
3141
|
+
onClose: () => {
|
|
3142
|
+
this.restoreFloatingBadgeIfNeeded();
|
|
3143
|
+
this.eventBus.emit("preferences:closed", void 0);
|
|
3144
|
+
}
|
|
3145
|
+
});
|
|
3146
|
+
this.eventBus.emit("preferences:opened", void 0);
|
|
3147
|
+
}
|
|
3148
|
+
closePreferences() {
|
|
3149
|
+
this.preferencesModal.close();
|
|
3150
|
+
this.restoreFloatingBadgeIfNeeded();
|
|
3151
|
+
this.eventBus.emit("preferences:closed", void 0);
|
|
3152
|
+
}
|
|
3153
|
+
getLocale() {
|
|
3154
|
+
return this.i18n.getLocale();
|
|
3155
|
+
}
|
|
3156
|
+
setLocale(locale) {
|
|
3157
|
+
const previousLocale = this.i18n.getLocale();
|
|
3158
|
+
if (locale.toLowerCase() === previousLocale.toLowerCase()) return;
|
|
3159
|
+
this.i18n.setLocale(locale);
|
|
3160
|
+
this.stateManager.setLocale(locale);
|
|
3161
|
+
const config = this.getResolvedConfig();
|
|
3162
|
+
if (!config) return;
|
|
3163
|
+
if (this.banner.getIsVisible()) {
|
|
3164
|
+
this.banner.render(config, {
|
|
3165
|
+
onAcceptAll: () => this.acceptAll(),
|
|
3166
|
+
onRejectAll: () => this.rejectAll(),
|
|
3167
|
+
onConfigure: () => this.openPreferences()
|
|
3168
|
+
});
|
|
3169
|
+
}
|
|
3170
|
+
if (this.preferencesModal.getIsOpen()) {
|
|
3171
|
+
this.preferencesModal.render(config, this.stateManager.getChoices(), {
|
|
3172
|
+
onSave: (choices) => {
|
|
3173
|
+
this.setPreferences(choices);
|
|
3174
|
+
this.restoreFloatingBadgeIfNeeded();
|
|
3175
|
+
},
|
|
3176
|
+
onAcceptAll: () => {
|
|
3177
|
+
this.acceptAll();
|
|
3178
|
+
this.restoreFloatingBadgeIfNeeded();
|
|
3179
|
+
},
|
|
3180
|
+
onRejectAll: () => {
|
|
3181
|
+
this.rejectAll();
|
|
3182
|
+
this.restoreFloatingBadgeIfNeeded();
|
|
3183
|
+
},
|
|
3184
|
+
onClose: () => {
|
|
3185
|
+
this.restoreFloatingBadgeIfNeeded();
|
|
3186
|
+
this.eventBus.emit("preferences:closed", void 0);
|
|
3187
|
+
}
|
|
3188
|
+
});
|
|
3189
|
+
}
|
|
3190
|
+
const badgeConfig = this.resolveFloatingBadgeConfig(config);
|
|
3191
|
+
if (badgeConfig.enabled) {
|
|
3192
|
+
const wasVisible = this.floatingBadge.getIsVisible();
|
|
3193
|
+
this.floatingBadge.render(config, {
|
|
3194
|
+
onClick: () => this.openPreferences()
|
|
3195
|
+
});
|
|
3196
|
+
if (wasVisible) {
|
|
3197
|
+
this.floatingBadge.show();
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
this.eventBus.emit("locale:changed", { locale, previousLocale });
|
|
3201
|
+
this.dispatchDomEvent("solvenza:locale:changed", { locale, previousLocale });
|
|
3202
|
+
}
|
|
3203
|
+
/**
|
|
3204
|
+
* Synchronize active locale with parent application i18n state.
|
|
3205
|
+
*/
|
|
3206
|
+
syncLocale(locale) {
|
|
3207
|
+
this.setLocale(locale);
|
|
3208
|
+
}
|
|
3209
|
+
/**
|
|
3210
|
+
* Check if Global Privacy Control (GPC) or Do Not Track (DNT) signal is active.
|
|
3211
|
+
*/
|
|
3212
|
+
isGpcActive() {
|
|
3213
|
+
return detectGpcSignal();
|
|
3214
|
+
}
|
|
3215
|
+
restoreFloatingBadgeIfNeeded() {
|
|
3216
|
+
const config = this.getResolvedConfig();
|
|
3217
|
+
if (!config) return;
|
|
3218
|
+
const badgeConfig = this.resolveFloatingBadgeConfig(config);
|
|
3219
|
+
if (!badgeConfig.enabled) return;
|
|
3220
|
+
this.showFloatingBadge();
|
|
3221
|
+
}
|
|
3222
|
+
getResolvedConfig() {
|
|
3223
|
+
const rawConfig = this.stateManager.getConfig();
|
|
3224
|
+
if (!rawConfig) return null;
|
|
3225
|
+
return this.i18n.resolveConfig(rawConfig, this.getLocale());
|
|
3226
|
+
}
|
|
3227
|
+
withdraw() {
|
|
3228
|
+
var _a, _b, _c, _d;
|
|
3229
|
+
const previousChoices = this.stateManager.getChoices();
|
|
3230
|
+
const config = this.stateManager.getConfig();
|
|
3231
|
+
if (!config) return;
|
|
3232
|
+
const cookieName = ((_a = config.storage) == null ? void 0 : _a.name) || "site_consent";
|
|
3233
|
+
if (((_b = config.storage) == null ? void 0 : _b.type) === "memory") {
|
|
3234
|
+
MemoryStore.remove(cookieName);
|
|
3235
|
+
} else {
|
|
3236
|
+
CookieStore.remove(cookieName, ((_c = config.storage) == null ? void 0 : _c.path) || "/", (_d = config.storage) == null ? void 0 : _d.domain);
|
|
3237
|
+
}
|
|
3238
|
+
for (const catId of Object.keys(config.categories)) {
|
|
3239
|
+
if (catId !== "necessary") {
|
|
3240
|
+
this.purgeCategory(catId);
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
this.stateManager.clearChoices();
|
|
3244
|
+
GoogleConsentAdapter.update(this.stateManager.getChoices());
|
|
3245
|
+
this.eventBus.emit("consent:withdrawn", { previousChoices });
|
|
3246
|
+
this.dispatchDomEvent("solvenza:updated", { choices: this.stateManager.getChoices() });
|
|
3247
|
+
const badgeConfig = this.resolveFloatingBadgeConfig(this.getResolvedConfig() || config);
|
|
3248
|
+
if (badgeConfig.enabled && badgeConfig.visibility !== "always") {
|
|
3249
|
+
this.hideFloatingBadge();
|
|
3250
|
+
}
|
|
3251
|
+
this.showBanner();
|
|
3252
|
+
}
|
|
3253
|
+
purgeCategory(category) {
|
|
3254
|
+
const config = this.stateManager.getConfig();
|
|
3255
|
+
if (!config) {
|
|
3256
|
+
return {
|
|
3257
|
+
category,
|
|
3258
|
+
purgedCookies: [],
|
|
3259
|
+
purgedLocalStorage: [],
|
|
3260
|
+
purgedSessionStorage: []
|
|
3261
|
+
};
|
|
3262
|
+
}
|
|
3263
|
+
const report = StorageCleaner.purgeCategory(config, category);
|
|
3264
|
+
this.eventBus.emit("storage:purged", { category, report });
|
|
3265
|
+
this.dispatchDomEvent("solvenza:storage:purged", { category, report });
|
|
3266
|
+
return report;
|
|
3267
|
+
}
|
|
3268
|
+
purgeStorage(categoryOrService) {
|
|
3269
|
+
var _a, _b;
|
|
3270
|
+
const config = this.stateManager.getConfig();
|
|
3271
|
+
if (!config) return [];
|
|
3272
|
+
if (categoryOrService) {
|
|
3273
|
+
if ((_a = config.categories) == null ? void 0 : _a[categoryOrService]) {
|
|
3274
|
+
return [this.purgeCategory(categoryOrService)];
|
|
3275
|
+
}
|
|
3276
|
+
const service = (_b = config.services) == null ? void 0 : _b[categoryOrService];
|
|
3277
|
+
if (service == null ? void 0 : service.category) {
|
|
3278
|
+
return [this.purgeCategory(service.category)];
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
const reports = [];
|
|
3282
|
+
const currentChoices = this.stateManager.getChoices();
|
|
3283
|
+
for (const [catId, allowed] of Object.entries(currentChoices)) {
|
|
3284
|
+
if (!allowed && catId !== "necessary") {
|
|
3285
|
+
reports.push(this.purgeCategory(catId));
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
return reports;
|
|
3289
|
+
}
|
|
3290
|
+
showFloatingBadge() {
|
|
3291
|
+
const config = this.getResolvedConfig();
|
|
3292
|
+
if (!config) return;
|
|
3293
|
+
const badgeConfig = this.resolveFloatingBadgeConfig(config);
|
|
3294
|
+
if (!badgeConfig.enabled) return;
|
|
3295
|
+
const el = this.floatingBadge.getElement();
|
|
3296
|
+
if (!el || !el.parentNode) {
|
|
3297
|
+
this.floatingBadge.render(config, {
|
|
3298
|
+
onClick: () => this.openPreferences()
|
|
3299
|
+
});
|
|
3300
|
+
}
|
|
3301
|
+
this.floatingBadge.show();
|
|
3302
|
+
this.eventBus.emit("floating-badge:shown", void 0);
|
|
3303
|
+
this.dispatchDomEvent("solvenza:badge:shown", void 0);
|
|
3304
|
+
}
|
|
3305
|
+
hideFloatingBadge() {
|
|
3306
|
+
this.floatingBadge.hide();
|
|
3307
|
+
this.eventBus.emit("floating-badge:hidden", void 0);
|
|
3308
|
+
this.dispatchDomEvent("solvenza:badge:hidden", void 0);
|
|
3309
|
+
}
|
|
3310
|
+
when(categoryOrService, callback) {
|
|
3311
|
+
return CustomServiceAdapter.createWhen(
|
|
3312
|
+
this.eventBus,
|
|
3313
|
+
(cat) => this.has(cat),
|
|
3314
|
+
(srv) => this.hasService(srv)
|
|
3315
|
+
)(categoryOrService, callback);
|
|
3316
|
+
}
|
|
3317
|
+
on(event, handler) {
|
|
3318
|
+
return this.eventBus.on(event, handler);
|
|
3319
|
+
}
|
|
3320
|
+
getReceipt() {
|
|
3321
|
+
return this.stateManager.getReceipt();
|
|
3322
|
+
}
|
|
3323
|
+
rescan() {
|
|
3324
|
+
const config = this.stateManager.getConfig();
|
|
3325
|
+
if (!config) {
|
|
3326
|
+
throw new Error("[ConsentSDK] SDK not initialized.");
|
|
3327
|
+
}
|
|
3328
|
+
const isGiven = !!this.getReceipt();
|
|
3329
|
+
return ResourceScanner.runDiagnostic(config, isGiven);
|
|
3330
|
+
}
|
|
3331
|
+
mountPolicy(targetContainer, options) {
|
|
3332
|
+
const config = this.getResolvedConfig() || this.stateManager.getConfig();
|
|
3333
|
+
if (!config) {
|
|
3334
|
+
throw new Error("[ConsentSDK] SDK not initialized.");
|
|
3335
|
+
}
|
|
3336
|
+
const container = typeof targetContainer === "string" ? document.querySelector(targetContainer) : targetContainer;
|
|
3337
|
+
if (container) {
|
|
3338
|
+
container.innerHTML = PolicyGenerator.renderCookiePolicy(config, options);
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
renderPolicyHtml(options) {
|
|
3342
|
+
const config = this.getResolvedConfig() || this.stateManager.getConfig();
|
|
3343
|
+
if (!config) {
|
|
3344
|
+
throw new Error("[ConsentSDK] SDK not initialized.");
|
|
3345
|
+
}
|
|
3346
|
+
return PolicyGenerator.renderCookiePolicy(config, options);
|
|
3347
|
+
}
|
|
3348
|
+
mountLegalNotice(targetContainer, options) {
|
|
3349
|
+
const config = this.getResolvedConfig() || this.stateManager.getConfig();
|
|
3350
|
+
if (!config) {
|
|
3351
|
+
throw new Error("[ConsentSDK] SDK not initialized.");
|
|
3352
|
+
}
|
|
3353
|
+
const container = typeof targetContainer === "string" ? document.querySelector(targetContainer) : targetContainer;
|
|
3354
|
+
if (container) {
|
|
3355
|
+
container.innerHTML = PolicyGenerator.renderLegalNotice(config, options);
|
|
3356
|
+
}
|
|
3357
|
+
}
|
|
3358
|
+
renderLegalNoticeHtml(options) {
|
|
3359
|
+
const config = this.getResolvedConfig() || this.stateManager.getConfig();
|
|
3360
|
+
if (!config) {
|
|
3361
|
+
throw new Error("[ConsentSDK] SDK not initialized.");
|
|
3362
|
+
}
|
|
3363
|
+
return PolicyGenerator.renderLegalNotice(config, options);
|
|
3364
|
+
}
|
|
3365
|
+
mountPrivacyPolicy(targetContainer, options) {
|
|
3366
|
+
const config = this.getResolvedConfig() || this.stateManager.getConfig();
|
|
3367
|
+
if (!config) {
|
|
3368
|
+
throw new Error("[ConsentSDK] SDK not initialized.");
|
|
3369
|
+
}
|
|
3370
|
+
const container = typeof targetContainer === "string" ? document.querySelector(targetContainer) : targetContainer;
|
|
3371
|
+
if (container) {
|
|
3372
|
+
container.innerHTML = PolicyGenerator.renderPrivacyPolicy(config, options);
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
3375
|
+
renderPrivacyPolicyHtml(options) {
|
|
3376
|
+
const config = this.getResolvedConfig() || this.stateManager.getConfig();
|
|
3377
|
+
if (!config) {
|
|
3378
|
+
throw new Error("[ConsentSDK] SDK not initialized.");
|
|
3379
|
+
}
|
|
3380
|
+
return PolicyGenerator.renderPrivacyPolicy(config, options);
|
|
3381
|
+
}
|
|
3382
|
+
saveChoices(choices, source) {
|
|
3383
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
3384
|
+
const config = this.stateManager.getConfig();
|
|
3385
|
+
if (!config) return;
|
|
3386
|
+
const receipt = createReceipt(
|
|
3387
|
+
config.policyVersion,
|
|
3388
|
+
choices,
|
|
3389
|
+
source,
|
|
3390
|
+
this.stateManager.getReceipt(),
|
|
3391
|
+
(_a = config.security) == null ? void 0 : _a.secretKey
|
|
3392
|
+
);
|
|
3393
|
+
if (this.isGpcActive()) {
|
|
3394
|
+
receipt.gpc = true;
|
|
3395
|
+
}
|
|
3396
|
+
this.stateManager.updateChoices(receipt);
|
|
3397
|
+
for (const [catId, isAllowed] of Object.entries(choices)) {
|
|
3398
|
+
if (!isAllowed) {
|
|
3399
|
+
this.purgeCategory(catId);
|
|
3400
|
+
}
|
|
3401
|
+
}
|
|
3402
|
+
const cookieName = ((_b = config.storage) == null ? void 0 : _b.name) || "site_consent";
|
|
3403
|
+
const receiptJson = JSON.stringify(receipt);
|
|
3404
|
+
if (((_c = config.storage) == null ? void 0 : _c.type) === "memory") {
|
|
3405
|
+
MemoryStore.set(cookieName, receiptJson);
|
|
3406
|
+
} else {
|
|
3407
|
+
CookieStore.set(cookieName, receiptJson, {
|
|
3408
|
+
path: ((_d = config.storage) == null ? void 0 : _d.path) || "/",
|
|
3409
|
+
maxAgeDays: (_f = (_e = config.consent) == null ? void 0 : _e.maxAgeDays) != null ? _f : 365,
|
|
3410
|
+
sameSite: ((_g = config.storage) == null ? void 0 : _g.sameSite) || "Lax",
|
|
3411
|
+
secure: (_h = config.storage) == null ? void 0 : _h.secure,
|
|
3412
|
+
domain: (_i = config.storage) == null ? void 0 : _i.domain
|
|
3413
|
+
});
|
|
3414
|
+
}
|
|
3415
|
+
GoogleConsentAdapter.update(choices);
|
|
3416
|
+
this.blockerRegistry.init(
|
|
3417
|
+
(cat) => this.has(cat),
|
|
3418
|
+
(srv) => this.hasService(srv)
|
|
3419
|
+
);
|
|
3420
|
+
if (((_j = config.logging) == null ? void 0 : _j.enabled) && config.logging.endpoint) {
|
|
3421
|
+
try {
|
|
3422
|
+
fetch(config.logging.endpoint, {
|
|
3423
|
+
method: "POST",
|
|
3424
|
+
headers: { "Content-Type": "application/json" },
|
|
3425
|
+
body: receiptJson
|
|
3426
|
+
}).catch(() => {
|
|
3427
|
+
});
|
|
3428
|
+
} catch (e) {
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
this.banner.remove();
|
|
3432
|
+
const resolvedConfig = this.getResolvedConfig() || config;
|
|
3433
|
+
const badgeConfig = this.resolveFloatingBadgeConfig(resolvedConfig);
|
|
3434
|
+
if (badgeConfig.enabled) {
|
|
3435
|
+
this.showFloatingBadge();
|
|
3436
|
+
}
|
|
3437
|
+
this.eventBus.emit("consent:changed", { choices, receipt });
|
|
3438
|
+
this.dispatchDomEvent("solvenza:updated", { choices, receipt });
|
|
3439
|
+
}
|
|
3440
|
+
showBanner() {
|
|
3441
|
+
const config = this.getResolvedConfig() || this.stateManager.getConfig();
|
|
3442
|
+
if (!config) return;
|
|
3443
|
+
const badgeConfig = this.resolveFloatingBadgeConfig(config);
|
|
3444
|
+
if (badgeConfig.enabled && badgeConfig.visibility !== "always") {
|
|
3445
|
+
this.hideFloatingBadge();
|
|
3446
|
+
}
|
|
3447
|
+
this.banner.render(config, {
|
|
3448
|
+
onAcceptAll: () => this.acceptAll(),
|
|
3449
|
+
onRejectAll: () => this.rejectAll(),
|
|
3450
|
+
onConfigure: () => this.openPreferences()
|
|
3451
|
+
});
|
|
3452
|
+
this.eventBus.emit("banner:shown", void 0);
|
|
3453
|
+
this.dispatchDomEvent("solvenza:show", void 0);
|
|
3454
|
+
}
|
|
3455
|
+
setupGlobalRevocationTrigger() {
|
|
3456
|
+
if (typeof document === "undefined") return;
|
|
3457
|
+
document.addEventListener("click", (e) => {
|
|
3458
|
+
const target = e.target;
|
|
3459
|
+
if (target == null ? void 0 : target.closest("[data-consent-open]")) {
|
|
3460
|
+
e.preventDefault();
|
|
3461
|
+
this.openPreferences();
|
|
3462
|
+
}
|
|
3463
|
+
});
|
|
3464
|
+
document.addEventListener("solvenza:show", () => {
|
|
3465
|
+
this.showBanner();
|
|
3466
|
+
});
|
|
3467
|
+
document.addEventListener("solvenza:preferences", () => {
|
|
3468
|
+
this.openPreferences();
|
|
3469
|
+
});
|
|
3470
|
+
document.addEventListener("solvenza:badge:show", () => {
|
|
3471
|
+
this.showFloatingBadge();
|
|
3472
|
+
});
|
|
3473
|
+
document.addEventListener("solvenza:badge:hide", () => {
|
|
3474
|
+
this.hideFloatingBadge();
|
|
3475
|
+
});
|
|
3476
|
+
document.addEventListener("solvenza:locale", (e) => {
|
|
3477
|
+
var _a;
|
|
3478
|
+
if ((_a = e == null ? void 0 : e.detail) == null ? void 0 : _a.locale) {
|
|
3479
|
+
this.setLocale(e.detail.locale);
|
|
3480
|
+
}
|
|
3481
|
+
});
|
|
3482
|
+
}
|
|
3483
|
+
resolveFloatingBadgeConfig(config) {
|
|
3484
|
+
var _a;
|
|
3485
|
+
const raw = (_a = config == null ? void 0 : config.ui) == null ? void 0 : _a.floatingBadge;
|
|
3486
|
+
if (raw === false) {
|
|
3487
|
+
return { enabled: false };
|
|
3488
|
+
}
|
|
3489
|
+
if (raw === true || raw === void 0) {
|
|
3490
|
+
return {
|
|
3491
|
+
enabled: true,
|
|
3492
|
+
position: "bottom-left",
|
|
3493
|
+
icon: "cookie",
|
|
3494
|
+
visibility: "after-consent"
|
|
3495
|
+
};
|
|
3496
|
+
}
|
|
3497
|
+
if (typeof raw === "object" && raw !== null) {
|
|
3498
|
+
return {
|
|
3499
|
+
enabled: raw.enabled !== false,
|
|
3500
|
+
position: raw.position || "bottom-left",
|
|
3501
|
+
label: raw.label,
|
|
3502
|
+
ariaLabel: raw.ariaLabel,
|
|
3503
|
+
showLabel: raw.showLabel,
|
|
3504
|
+
icon: raw.icon || "cookie",
|
|
3505
|
+
visibility: raw.visibility || "after-consent"
|
|
3506
|
+
};
|
|
3507
|
+
}
|
|
3508
|
+
return { enabled: true, position: "bottom-left", icon: "cookie", visibility: "after-consent" };
|
|
3509
|
+
}
|
|
3510
|
+
dispatchDomEvent(name, detail) {
|
|
3511
|
+
if (typeof document === "undefined") return;
|
|
3512
|
+
try {
|
|
3513
|
+
const customEvent = new CustomEvent(name, { detail, bubbles: true });
|
|
3514
|
+
document.dispatchEvent(customEvent);
|
|
3515
|
+
} catch (e) {
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3518
|
+
};
|
|
3519
|
+
var globalScope = typeof window !== "undefined" ? window : globalThis;
|
|
3520
|
+
if (!globalScope.__ConsentSDK_Instance__) {
|
|
3521
|
+
globalScope.__ConsentSDK_Instance__ = new ConsentEngine();
|
|
3522
|
+
}
|
|
3523
|
+
var Consent = globalScope.__ConsentSDK_Instance__;
|
|
3524
|
+
|
|
3525
|
+
export {
|
|
3526
|
+
ConfigValidationError,
|
|
3527
|
+
validateConfig,
|
|
3528
|
+
computeReceiptSignature,
|
|
3529
|
+
verifyReceiptIntegrity,
|
|
3530
|
+
sanitizeHtml,
|
|
3531
|
+
createReceipt,
|
|
3532
|
+
parseReceipt,
|
|
3533
|
+
isReceiptExpired,
|
|
3534
|
+
CookieStorageProvider,
|
|
3535
|
+
CookieStore,
|
|
3536
|
+
MemoryStorageProvider,
|
|
3537
|
+
MemoryStore,
|
|
3538
|
+
StorageCleaner,
|
|
3539
|
+
ScriptResourceBlocker,
|
|
3540
|
+
ScriptGate,
|
|
3541
|
+
IframeResourceBlocker,
|
|
3542
|
+
IframeGate,
|
|
3543
|
+
ImageResourceBlocker,
|
|
3544
|
+
ResourceGate,
|
|
3545
|
+
GoogleConsentAdapter,
|
|
3546
|
+
FloatingBadge,
|
|
3547
|
+
ResourceScanner,
|
|
3548
|
+
PolicyGenerator,
|
|
3549
|
+
BUILTIN_TRANSLATIONS,
|
|
3550
|
+
I18nEngine,
|
|
3551
|
+
detectGpcSignal,
|
|
3552
|
+
resolveGpcConfig,
|
|
3553
|
+
applyGpcChoices,
|
|
3554
|
+
ConsentEngine,
|
|
3555
|
+
Consent
|
|
3556
|
+
};
|
|
3557
|
+
//# sourceMappingURL=chunk-C5QPXHTH.js.map
|