keksi 0.1.2 → 0.1.4
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 +6 -3
- package/dist/cli.js +27 -1
- package/dist/glob.d.ts +2 -0
- package/dist/i18n.d.ts +2 -1
- package/dist/index.js +72 -30
- package/dist/playwright.js +34 -4
- package/dist/render.d.ts +1 -1
- package/dist/runtime.d.ts +3 -0
- package/dist/scan.js +27 -1
- package/dist/sveltekit.js +72 -30
- package/dist/types.d.ts +14 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ texts in English, Finnish and Swedish.
|
|
|
6
6
|
keksi loads the trackers itself, and only after consent. You don't add their script tags: you put their ids in the
|
|
7
7
|
config, so there's nothing to block.
|
|
8
8
|
|
|
9
|
-
- Google Analytics 4, Google Ads, Meta Pixel and YouTube. The short list is deliberate.
|
|
9
|
+
- Google Analytics 4, Google Ads, Meta Pixel, PostHog and YouTube. The short list is deliberate.
|
|
10
10
|
- About 5 KB gzipped, inline. No extra requests, no dependencies.
|
|
11
11
|
- Rendered on the server. Works on static pages, with no layout shift.
|
|
12
12
|
- The cookie policy table is generated from the same data.
|
|
@@ -25,6 +25,7 @@ export default defineConfig({
|
|
|
25
25
|
ga: 'G-XXXXXXX',
|
|
26
26
|
googleAds: 'AW-123456789',
|
|
27
27
|
meta: '1234567890',
|
|
28
|
+
posthog: { key: 'phc_XXXX' }, // EU cloud by default; set host for US or a proxy
|
|
28
29
|
youtube: true,
|
|
29
30
|
policyUrl: '/evasteet',
|
|
30
31
|
});
|
|
@@ -82,16 +83,18 @@ string.
|
|
|
82
83
|
|
|
83
84
|
## Tracking events
|
|
84
85
|
|
|
85
|
-
`gtag()
|
|
86
|
+
`gtag()`, `fbq()` and `posthog` are always defined, so you can call them anytime:
|
|
86
87
|
|
|
87
88
|
```ts
|
|
88
89
|
gtag('event', 'generate_lead');
|
|
89
90
|
fbq('track', 'Lead');
|
|
91
|
+
posthog.capture('signup');
|
|
90
92
|
```
|
|
91
93
|
|
|
92
94
|
- Calls made before consent are queued. They're sent only if the visitor consents on that page.
|
|
93
95
|
- Google gets Consent Mode v2 signals (basic mode).
|
|
94
|
-
- GA4
|
|
96
|
+
- GA4, Meta Pixel and PostHog track single-page navigation themselves.
|
|
97
|
+
- PostHog counts as analytics. With the US host, the settings say data is transferred to the US.
|
|
95
98
|
|
|
96
99
|
## YouTube
|
|
97
100
|
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
6
6
|
|
|
7
|
+
// src/glob.ts
|
|
8
|
+
var glob = (pattern, name) => new RegExp(`^${pattern.split("*").map((s) => s.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*")}$`).test(name);
|
|
9
|
+
|
|
7
10
|
// src/providers.ts
|
|
8
11
|
var GOOGLE = { company: "Google Ireland Limited", privacyPolicy: "https://policies.google.com/privacy", transfer: "dpf" };
|
|
9
12
|
var META = { company: "Meta Platforms Ireland Limited", privacyPolicy: "https://www.facebook.com/privacy/policy/", transfer: "dpf" };
|
|
@@ -64,6 +67,29 @@ var providers = [
|
|
|
64
67
|
],
|
|
65
68
|
hosts: ["facebook.net", "facebook.com"]
|
|
66
69
|
},
|
|
70
|
+
{
|
|
71
|
+
id: "posthog",
|
|
72
|
+
name: "PostHog",
|
|
73
|
+
company: "PostHog Inc.",
|
|
74
|
+
privacyPolicy: "https://posthog.com/privacy",
|
|
75
|
+
category: "analytics",
|
|
76
|
+
purpose: {
|
|
77
|
+
fi: "Tuoteanalytiikka: sivujen katselut, klikkaukset ja k\xE4ytt\xF6tavat. Voi tallentaa istuntoja, jos istuntotallenteet ovat k\xE4yt\xF6ss\xE4.",
|
|
78
|
+
sv: "Produktanalys: sidvisningar, klick och anv\xE4ndningsm\xF6nster. Kan spela in sessioner om sessionsinspelning \xE4r aktiverad.",
|
|
79
|
+
en: "Product analytics: page views, clicks and usage patterns. Can record sessions if session replay is enabled."
|
|
80
|
+
},
|
|
81
|
+
cookies: [
|
|
82
|
+
{ name: "ph_*_posthog", duration: "1y" },
|
|
83
|
+
{ name: "ph_*_posthog_cpm", duration: "1y" },
|
|
84
|
+
{ name: "ph_*_posthog", duration: "persistent", storage: "localStorage" },
|
|
85
|
+
{ name: "ph_*_posthog__flags", duration: "persistent", storage: "localStorage" },
|
|
86
|
+
{ name: "ph_*_posthog", duration: "session", storage: "sessionStorage" },
|
|
87
|
+
{ name: "ph_*_primary_window_exists", duration: "session", storage: "sessionStorage" },
|
|
88
|
+
{ name: "ph_*_session_registered_properties", duration: "session", storage: "sessionStorage" },
|
|
89
|
+
{ name: "ph_*_window_id", duration: "session", storage: "sessionStorage" }
|
|
90
|
+
],
|
|
91
|
+
hosts: ["posthog.com"]
|
|
92
|
+
},
|
|
67
93
|
{
|
|
68
94
|
id: "youtube",
|
|
69
95
|
name: "YouTube",
|
|
@@ -119,7 +145,7 @@ var purposes = {
|
|
|
119
145
|
};
|
|
120
146
|
var trackerNames = providers.flatMap((p) => p.cookies.flatMap((c) => c.name.split(", ")));
|
|
121
147
|
var trackerHosts = providers.flatMap((p) => p.hosts);
|
|
122
|
-
var matches = (patterns, name) => patterns.some((p) =>
|
|
148
|
+
var matches = (patterns, name) => patterns.some((p) => glob(p, name));
|
|
123
149
|
function purposeOf(name) {
|
|
124
150
|
const k = Object.keys(purposes).find((p) => matches([p], name));
|
|
125
151
|
return k ? purposes[k] : void 0;
|
package/dist/glob.d.ts
ADDED
package/dist/i18n.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Locale } from './types.js';
|
|
|
2
2
|
declare const fi: {
|
|
3
3
|
title: string;
|
|
4
4
|
intro: string;
|
|
5
|
-
|
|
5
|
+
change: string;
|
|
6
6
|
more: string;
|
|
7
7
|
accept: string;
|
|
8
8
|
reject: string;
|
|
@@ -22,6 +22,7 @@ declare const fi: {
|
|
|
22
22
|
consentPurpose: string;
|
|
23
23
|
privacyPolicy: string;
|
|
24
24
|
dpf: string;
|
|
25
|
+
us: string;
|
|
25
26
|
name: string;
|
|
26
27
|
duration: string;
|
|
27
28
|
purpose: string;
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
var fi = {
|
|
3
3
|
title: "Ev\xE4steet",
|
|
4
4
|
intro: "K\xE4yt\xE4mme v\xE4ltt\xE4m\xE4tt\xF6mien ev\xE4steiden lis\xE4ksi seuraavia palveluita vain, jos annat siihen luvan:",
|
|
5
|
-
|
|
5
|
+
change: "Voit muuttaa valintaasi milloin tahansa.",
|
|
6
6
|
more: "Lue lis\xE4\xE4",
|
|
7
7
|
accept: "Hyv\xE4ksy kaikki",
|
|
8
8
|
reject: "Hylk\xE4\xE4 kaikki",
|
|
@@ -22,6 +22,7 @@ var fi = {
|
|
|
22
22
|
consentPurpose: "Tallentaa ev\xE4stevalintasi.",
|
|
23
23
|
privacyPolicy: "Tietosuojaseloste",
|
|
24
24
|
dpf: "Tietoja voidaan siirt\xE4\xE4 Yhdysvaltoihin EU:n ja Yhdysvaltojen v\xE4lisen tietosuojakehyksen (DPF) perusteella.",
|
|
25
|
+
us: "Tietoja siirret\xE4\xE4n Yhdysvaltoihin.",
|
|
25
26
|
name: "Nimi",
|
|
26
27
|
duration: "Kesto",
|
|
27
28
|
purpose: "Tarkoitus",
|
|
@@ -39,7 +40,7 @@ var fi = {
|
|
|
39
40
|
var sv = {
|
|
40
41
|
title: "Kakor",
|
|
41
42
|
intro: "Ut\xF6ver n\xF6dv\xE4ndiga kakor anv\xE4nder vi f\xF6ljande tj\xE4nster endast om du ger ditt samtycke:",
|
|
42
|
-
|
|
43
|
+
change: "Du kan \xE4ndra ditt val n\xE4r som helst.",
|
|
43
44
|
more: "L\xE4s mer",
|
|
44
45
|
accept: "Godk\xE4nn alla",
|
|
45
46
|
reject: "Avvisa alla",
|
|
@@ -59,6 +60,7 @@ var sv = {
|
|
|
59
60
|
consentPurpose: "Sparar ditt val av kakor.",
|
|
60
61
|
privacyPolicy: "Dataskyddsbeskrivning",
|
|
61
62
|
dpf: "Uppgifter kan \xF6verf\xF6ras till USA med st\xF6d av ramverket f\xF6r dataskydd mellan EU och USA (DPF).",
|
|
63
|
+
us: "Uppgifter \xF6verf\xF6rs till USA.",
|
|
62
64
|
name: "Namn",
|
|
63
65
|
duration: "Varaktighet",
|
|
64
66
|
purpose: "Syfte",
|
|
@@ -76,7 +78,7 @@ var sv = {
|
|
|
76
78
|
var en = {
|
|
77
79
|
title: "Cookies",
|
|
78
80
|
intro: "Besides necessary cookies, we use the following services only if you allow it:",
|
|
79
|
-
|
|
81
|
+
change: "You can change your choice at any time.",
|
|
80
82
|
more: "Read more",
|
|
81
83
|
accept: "Accept all",
|
|
82
84
|
reject: "Reject all",
|
|
@@ -96,6 +98,7 @@ var en = {
|
|
|
96
98
|
consentPurpose: "Stores your cookie choice.",
|
|
97
99
|
privacyPolicy: "Privacy policy",
|
|
98
100
|
dpf: "Data may be transferred to the United States under the EU\u2013US Data Privacy Framework (DPF).",
|
|
101
|
+
us: "Data is transferred to the United States.",
|
|
99
102
|
name: "Name",
|
|
100
103
|
duration: "Duration",
|
|
101
104
|
purpose: "Purpose",
|
|
@@ -172,6 +175,29 @@ var providers = [
|
|
|
172
175
|
],
|
|
173
176
|
hosts: ["facebook.net", "facebook.com"]
|
|
174
177
|
},
|
|
178
|
+
{
|
|
179
|
+
id: "posthog",
|
|
180
|
+
name: "PostHog",
|
|
181
|
+
company: "PostHog Inc.",
|
|
182
|
+
privacyPolicy: "https://posthog.com/privacy",
|
|
183
|
+
category: "analytics",
|
|
184
|
+
purpose: {
|
|
185
|
+
fi: "Tuoteanalytiikka: sivujen katselut, klikkaukset ja k\xE4ytt\xF6tavat. Voi tallentaa istuntoja, jos istuntotallenteet ovat k\xE4yt\xF6ss\xE4.",
|
|
186
|
+
sv: "Produktanalys: sidvisningar, klick och anv\xE4ndningsm\xF6nster. Kan spela in sessioner om sessionsinspelning \xE4r aktiverad.",
|
|
187
|
+
en: "Product analytics: page views, clicks and usage patterns. Can record sessions if session replay is enabled."
|
|
188
|
+
},
|
|
189
|
+
cookies: [
|
|
190
|
+
{ name: "ph_*_posthog", duration: "1y" },
|
|
191
|
+
{ name: "ph_*_posthog_cpm", duration: "1y" },
|
|
192
|
+
{ name: "ph_*_posthog", duration: "persistent", storage: "localStorage" },
|
|
193
|
+
{ name: "ph_*_posthog__flags", duration: "persistent", storage: "localStorage" },
|
|
194
|
+
{ name: "ph_*_posthog", duration: "session", storage: "sessionStorage" },
|
|
195
|
+
{ name: "ph_*_primary_window_exists", duration: "session", storage: "sessionStorage" },
|
|
196
|
+
{ name: "ph_*_session_registered_properties", duration: "session", storage: "sessionStorage" },
|
|
197
|
+
{ name: "ph_*_window_id", duration: "session", storage: "sessionStorage" }
|
|
198
|
+
],
|
|
199
|
+
hosts: ["posthog.com"]
|
|
200
|
+
},
|
|
175
201
|
{
|
|
176
202
|
id: "youtube",
|
|
177
203
|
name: "YouTube",
|
|
@@ -200,8 +226,11 @@ function defineConfig(config) {
|
|
|
200
226
|
return config;
|
|
201
227
|
}
|
|
202
228
|
var list = (v) => v === void 0 ? [] : Array.isArray(v) ? v : [v];
|
|
229
|
+
var posthogHost = (config) => (config.posthog?.host ?? "https://eu.i.posthog.com").replace(/\/$/, "");
|
|
203
230
|
function activeProviders(config) {
|
|
204
|
-
return providers.filter(
|
|
231
|
+
return providers.filter(
|
|
232
|
+
(p) => p.id === "youtube" ? config.youtube : p.id === "posthog" ? config.posthog?.key : list(config[p.id]).length
|
|
233
|
+
).map((p) => p.id === "posthog" && /\/\/us\./.test(posthogHost(config)) ? { ...p, transfer: "us" } : p);
|
|
205
234
|
}
|
|
206
235
|
function categoriesOf(config) {
|
|
207
236
|
return ["analytics", "marketing"].filter((c) => activeProviders(config).some((p) => p.category === c));
|
|
@@ -226,6 +255,7 @@ function runtimeData(config, locale) {
|
|
|
226
255
|
ga: list(config.ga),
|
|
227
256
|
ads: list(config.googleAds),
|
|
228
257
|
meta: list(config.meta),
|
|
258
|
+
ph: config.posthog?.key ? { key: config.posthog.key, host: posthogHost(config) } : void 0,
|
|
229
259
|
yt: !!config.youtube,
|
|
230
260
|
del: { analytics: del("analytics"), marketing: del("marketing") },
|
|
231
261
|
t: { ytText: t.ytText, ytButton: t.ytButton, ytOpen: t.ytOpen }
|
|
@@ -234,34 +264,46 @@ function runtimeData(config, locale) {
|
|
|
234
264
|
|
|
235
265
|
// src/styles.ts
|
|
236
266
|
var css = [
|
|
237
|
-
":where(.keksi,.keksi-
|
|
238
|
-
":where(.keksi
|
|
239
|
-
":where(.keksi){position:fixed;z-index:2147483000;inset:auto 1rem 1rem;max-width:34rem;max-height:calc(100vh - 2rem);
|
|
240
|
-
":where(.keksi,.keksi-
|
|
241
|
-
":where(.keksi)
|
|
242
|
-
":where(.keksi
|
|
243
|
-
":where(.keksi
|
|
244
|
-
":where(.keksi-
|
|
245
|
-
":where(.keksi-
|
|
267
|
+
":where(.keksi,.keksi-policy){--_bg:var(--keksi-bg,#fff);--_fg:var(--keksi-fg,#1b1b1b);--_line:var(--keksi-line,#e3e3e3);--_soft:color-mix(in srgb,var(--_fg) 4%,var(--_bg));--_r:var(--keksi-radius,8px)}",
|
|
268
|
+
":where(.keksi){box-sizing:border-box;background:var(--_bg);color:var(--_fg);font:inherit;font-size:14px;line-height:1.5;border:1px solid var(--_line);border-radius:var(--_r);box-shadow:0 8px 32px #0000002e}",
|
|
269
|
+
":where(.keksi){position:fixed;z-index:2147483000;inset:auto 1rem 1rem;display:flex;flex-direction:column;max-width:34rem;max-height:calc(100vh - 2rem);margin-inline:auto;overflow:hidden}",
|
|
270
|
+
":where(.keksi,.keksi-main,.keksi-settings)[hidden]{display:none}",
|
|
271
|
+
":where(.keksi-main,.keksi-settings){display:flex;flex-direction:column;min-height:0;margin:0}",
|
|
272
|
+
":where(.keksi:has(.keksi-settings:not([hidden]))){max-width:40rem}",
|
|
273
|
+
":where(.keksi-settings .keksi-body){padding-block:0}",
|
|
274
|
+
":where(.keksi-head){display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.875rem 1.25rem;border-bottom:1px solid var(--_line)}",
|
|
275
|
+
":where(.keksi-head) :where(h2){font-size:1rem;font-weight:600;line-height:1.3;margin:0}",
|
|
276
|
+
":where(.keksi-body){padding:1rem 1.25rem;overflow:auto}",
|
|
277
|
+
":where(.keksi) :where(.keksi-body>*){margin:0 0 .75rem}",
|
|
278
|
+
":where(.keksi) :where(.keksi-body>:last-child){margin-bottom:0}",
|
|
279
|
+
":where(.keksi) :where(ul){list-style:none;padding:0}",
|
|
280
|
+
":where(.keksi) :where(strong){font-weight:600}",
|
|
281
|
+
":where(.keksi) :where(a){color:inherit}",
|
|
282
|
+
".keksi a{text-decoration:underline}",
|
|
283
|
+
":where(.keksi-actions){display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:.5rem;padding:.75rem 1.25rem;background:var(--_soft)}",
|
|
284
|
+
":where(.keksi-btn){min-width:7.5rem;font:inherit;font-weight:600;padding:.5rem 1rem;border:1px solid var(--_fg);border-radius:calc(var(--_r) - 2px);background:var(--_bg);color:var(--_fg);cursor:pointer}",
|
|
246
285
|
":where(.keksi-btn:hover){background:var(--_fg);color:var(--_bg)}",
|
|
247
|
-
":where(.keksi
|
|
248
|
-
":where(.keksi-
|
|
249
|
-
":where(.keksi-
|
|
250
|
-
":where(.keksi-
|
|
251
|
-
":where(.keksi-
|
|
252
|
-
":where(.keksi-
|
|
253
|
-
":where(.keksi-cat){
|
|
254
|
-
":where(.keksi-cat-head) :where(h2,h3,h4,h5){font-size:1rem;margin:0}",
|
|
286
|
+
":where(.keksi-text){margin-right:auto;padding:.5rem .25rem;font:inherit;border:0;background:none;color:inherit;text-decoration:underline;text-underline-offset:2px;cursor:pointer}",
|
|
287
|
+
"@media (max-width:30rem){:where(.keksi-btn){flex:1 1 auto}}",
|
|
288
|
+
":where(.keksi,keksi-youtube) :where(button,a,input,summary):focus-visible{outline:3px solid var(--keksi-focus,#2563eb);outline-offset:2px}",
|
|
289
|
+
":where(.keksi-close){font:inherit;font-size:1.375rem;line-height:1;margin:-.25rem -.5rem;padding:.25rem .5rem;border:0;background:none;color:inherit;cursor:pointer}",
|
|
290
|
+
":where(.keksi-cat){padding:1rem 0;border-top:1px solid var(--_line)}",
|
|
291
|
+
":where(.keksi-body>.keksi-cat:first-child){border-top:0}",
|
|
292
|
+
":where(.keksi-cat-head){display:flex;align-items:center;justify-content:space-between;gap:1rem}",
|
|
293
|
+
":where(.keksi-cat-head) :where(h2,h3,h4,h5){font-size:1rem;font-weight:600;margin:0}",
|
|
294
|
+
":where(.keksi-cat-head) :where(span){font-size:.8125rem;opacity:.7}",
|
|
255
295
|
":where(.keksi-cat) :where(p){margin:.25rem 0}",
|
|
256
|
-
":where(.keksi-cat input[role=switch]){appearance:none;position:relative;flex:none;width:2.
|
|
257
|
-
':where(.keksi-cat input[role=switch])::before{content:"";position:absolute;top:.
|
|
296
|
+
":where(.keksi-cat input[role=switch]){appearance:none;position:relative;flex:none;width:2.5rem;height:1.375rem;margin:0;border-radius:1rem;background:color-mix(in srgb,var(--_fg) 22%,var(--_bg));cursor:pointer}",
|
|
297
|
+
':where(.keksi-cat input[role=switch])::before{content:"";position:absolute;top:.1875rem;left:.1875rem;width:1rem;height:1rem;border-radius:50%;background:#fff;transition:transform .15s}',
|
|
258
298
|
":where(.keksi-cat input[role=switch]:checked){background:var(--_fg)}",
|
|
259
|
-
":where(.keksi-cat input[role=switch]:checked)::before{transform:translateX(1.
|
|
299
|
+
":where(.keksi-cat input[role=switch]:checked)::before{transform:translateX(1.125rem)}",
|
|
260
300
|
":where(.keksi-cat summary){margin-top:.5rem;font-weight:600;cursor:pointer}",
|
|
261
|
-
":where(.keksi-provider){margin:.75rem 0}",
|
|
262
|
-
":where(.keksi-provider)
|
|
263
|
-
":where(.keksi-
|
|
264
|
-
":where(.keksi-cat
|
|
301
|
+
":where(.keksi-provider){margin:.75rem 0;padding:.75rem .875rem;border-radius:calc(var(--_r) - 2px);background:var(--_soft)}",
|
|
302
|
+
":where(.keksi-policy .keksi-provider){padding:0;background:none}",
|
|
303
|
+
":where(.keksi-provider) :where(h3,h4,h5,h6){font-size:.9375rem;font-weight:600;margin:0}",
|
|
304
|
+
":where(.keksi-cat table){width:100%;border-collapse:collapse;font-size:.8125rem;margin-top:.5rem}",
|
|
305
|
+
":where(.keksi-cat) :where(th,td){padding:.3rem .75rem .3rem 0;border-bottom:1px solid var(--_line);text-align:left;vertical-align:top}",
|
|
306
|
+
":where(.keksi-cat) :where(th){font-weight:600}",
|
|
265
307
|
":where(keksi-youtube){display:block;aspect-ratio:16/9;background:#111;color:#fff}",
|
|
266
308
|
":where(keksi-youtube iframe){display:block;width:100%;height:100%;border:0}",
|
|
267
309
|
":where(.keksi-yt){display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.75rem;height:100%;padding:1rem;box-sizing:border-box;text-align:center}",
|
|
@@ -282,7 +324,7 @@ function renderKeksi(config, { lang = "fi", nonce } = {}) {
|
|
|
282
324
|
const btn = (action, label, type = "button") => `<button type="${type}" class="keksi-btn" data-keksi="${action}">${e(label)}</button>`;
|
|
283
325
|
const uses = cats.map((c) => `<li><strong>${e(t[c])}:</strong> ${e(active.filter((p) => p.category === c).map((p) => p.name).join(", "))}</li>`).join("");
|
|
284
326
|
const json = JSON.stringify(runtimeData(config, lang)).replace(/</g, "\\u003c");
|
|
285
|
-
return `<style${n}>${css}</style><section id="keksi-banner" class="keksi" aria-labelledby="keksi-title" hidden><h2 id="keksi-title">${e(t.title)}</h2><p>${e(t.intro)}</p><ul>${uses}</ul><p>${e(t.
|
|
327
|
+
return `<style${n}>${css}</style><section id="keksi-banner" class="keksi" aria-labelledby="keksi-title" hidden><div class="keksi-main"><div class="keksi-head"><h2 id="keksi-title">${e(t.title)}</h2></div><div class="keksi-body"><p>${e(t.intro)}</p><ul>${uses}</ul><p>${e(t.change)}${policy ? ` <a href="${e(policy)}">${e(t.more)}</a>` : ""}</p></div><div class="keksi-actions"><button type="button" class="keksi-text" data-keksi-open>${e(t.settings)}</button>${btn("reject", t.reject)}${btn("accept", t.accept)}</div></div><form id="keksi-settings" class="keksi-settings" aria-labelledby="keksi-settings-title" hidden><div class="keksi-head"><h2 id="keksi-settings-title">${e(t.settingsTitle)}</h2><button type="button" class="keksi-close" data-keksi="close" aria-label="${e(t.close)}">\xD7</button></div><div class="keksi-body">${sections(config, lang, 3, true)}</div><div class="keksi-actions">${btn("save", t.save)}${btn("reject", t.reject)}${btn("accept", t.accept)}</div></form></section><script type="application/json" id="keksi-data">${json}</script><script${n}>${'"use strict";(()=>{var M=(o,s)=>new RegExp(`^${o.split("*").map(i=>i.replace(/[.+?^${}()|[\\]\\\\]/g,"\\\\$&")).join(".*")}$`).test(s);var $=["analytics","marketing"],C={analytics:!1,marketing:!1};function p(...o){return arguments}function j(o,s=window){if(typeof s.keksi?.sync=="function")return s.keksi.sync(),s.keksi;let i=s.document,f=e=>i.getElementById(e),u=q(),_=!1,A=!1,S=!1,v=new Set,L=p("consent","default",{ad_storage:"denied",ad_user_data:"denied",ad_personalization:"denied",analytics_storage:"denied"});if(o.ga.length||o.ads.length){let e=s.dataLayer??(s.dataLayer=[]);s.gtag=function(){e.push(arguments)},e.push(L)}if(o.meta.length){let e=function(){e.callMethod?e.callMethod.apply(e,[...arguments]):e.queue.push(arguments)};Object.assign(e,{push:e,loaded:!0,version:"2.0",queue:[]}),s.fbq=s._fbq=e}if(o.ph){let e=Object.assign([],{_i:[],people:[]});for(let n of"capture identify alias reset register register_once unregister group setPersonProperties getFeatureFlag isFeatureEnabled onFeatureFlags reloadFeatureFlags get_distinct_id startSessionRecording stopSessionRecording captureException on".split(" "))e[n]=(...t)=>e.push([n,...t]);s.posthog=e}function q(){let e=i.cookie.split("; ").find(r=>r.startsWith(`${o.name}=`))?.slice(o.name.length+1),[n,,t,a]=e?.split(".")??[];return n!==o.v||!t||!a?null:{id:t,c:{analytics:a.includes("a"),marketing:a.includes("m")}}}function F(e){let n=u?.id??Array.from(s.crypto.getRandomValues(new Uint8Array(8)),a=>a.toString(16).padStart(2,"0")).join(""),t=(e.analytics?"a":"")+(e.marketing?"m":"")||"-";return i.cookie=`${o.name}=${o.v}.${Date.now().toString(36)}.${n}.${t}; Path=/; Max-Age=${o.days*86400}; SameSite=Lax`+(o.domain?`; Domain=${o.domain}`:"")+(s.location.protocol==="https:"?"; Secure":""),{id:n,c:e}}function E(e){let n=i.createElement("script");return n.async=!0,n.src=e,i.head.append(n),n}function w(e){let n=[...e.analytics?o.ga:[],...e.marketing?o.ads:[]].filter(t=>!v.has(t));if(n.length){let t=e.marketing?"granted":"denied",a=[p("consent","update",{analytics_storage:e.analytics?"granted":"denied",ad_storage:t,ad_user_data:t,ad_personalization:t})];v.size||a.push(p("js",new Date));for(let r of n)a.push(p("config",r)),v.add(r);if(_)for(let r of a)s.dataLayer.push(r);else{let r=s.dataLayer;r.splice(r.indexOf(L)+1,0,...a),E(`https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(n[0])}`),_=!0}}if(e.marketing&&o.meta.length&&!A&&(A=!0,s.fbq.queue.unshift(...o.meta.map(t=>p("init",t)),p("track","PageView")),E("https://connect.facebook.net/en_US/fbevents.js")),e.analytics&&o.ph&&!S&&(S=!0,s.posthog._i.push([o.ph.key,{api_host:o.ph.host,defaults:"2026-08-30"},"posthog"]),E(`${o.ph.host.replace(".i.posthog.com","-assets.i.posthog.com")}/static/array.js`).crossOrigin="anonymous"),e.marketing)for(let t of i.querySelectorAll("keksi-youtube"))t.show(!0)}function H(e){let n=e.flatMap(c=>o.del[c]),t=c=>n.some(l=>M(l,c)),a=s.location.hostname.split("."),r=["",...a.map((c,l)=>`; Domain=${a.slice(l).join(".")}`)];for(let c of i.cookie.split("; ")){let l=c.split("=")[0];if(t(l))for(let b of r)i.cookie=`${l}=; Max-Age=0; Path=/${b}`}try{for(let c of[s.localStorage,s.sessionStorage])for(let l of Object.keys(c))t(l)&&c.removeItem(l)}catch{}}function g(e){let n=u?.c;u=F(e),y(),i.dispatchEvent(new s.CustomEvent("keksi:change",{detail:{...e}})),H($.filter(t=>!e[t])),$.some(t=>n?.[t]&&!e[t])?s.location.reload():w(e)}function h(e=!1,n=!u||e){let t=f("keksi-banner"),a=f("keksi-settings");!t||!a||(t.hidden=!n,a.hidden=!e,t.firstElementChild.hidden=e)}function y(){h()}function x(){let e=f("keksi-settings");for(let n of $){let t=e?.querySelector(`[name=${n}]`);t&&(t.checked=!!u?.c[n])}h(!0),e?.querySelector(".keksi-close")?.focus()}i.addEventListener("click",e=>{let n=e.target.closest?.("[data-keksi],[data-keksi-open]");if(!n)return;if(n.hasAttribute("data-keksi-open"))return e.preventDefault(),x();let t=n.getAttribute("data-keksi");if(t==="close")h();else if(t==="accept")g({analytics:!0,marketing:!0});else if(t==="reject")g(C);else if(t==="save"){let a=r=>!!f("keksi-settings")?.querySelector(`[name=${r}]`)?.checked;g({analytics:a("analytics"),marketing:a("marketing")})}}),i.addEventListener("keydown",e=>{e.key==="Escape"&&f("keksi-settings")?.hidden===!1&&h()}),i.addEventListener("astro:after-swap",y);class I extends s.HTMLElement{connectedCallback(){this.show(!!u?.c.marketing)}show(n,t=!1){let a=encodeURIComponent(this.getAttribute("video")??""),r=this.getAttribute("label");if(n){if(this.querySelector("iframe"))return;let d=i.createElement("iframe");d.src=`https://www.youtube-nocookie.com/embed/${a}${t?"?autoplay=1":""}`,d.title=r??"YouTube",d.allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",d.allowFullscreen=!0,d.referrerPolicy="strict-origin-when-cross-origin",this.replaceChildren(d),t&&d.focus();return}let c=i.createElement("div");c.className="keksi-yt";let l=this.getAttribute("poster");l&&(c.style.background=`linear-gradient(#000a,#000a),center/cover url(${JSON.stringify(l)})`);let b=i.createElement("p");b.textContent=o.t.ytText;let m=i.createElement("button");m.type="button",m.textContent=o.t.ytButton,r&&m.setAttribute("aria-label",`${o.t.ytButton}: ${r}`),m.onclick=()=>this.show(!0,!0);let k=i.createElement("a");k.href=`https://www.youtube.com/watch?v=${a}`,k.target="_blank",k.rel="noopener",k.textContent=o.t.ytOpen,c.append(b,m,k),this.replaceChildren(c)}}o.yt&&!s.customElements.get("keksi-youtube")&&s.customElements.define("keksi-youtube",I);let T={get consent(){return u?{...u.c}:null},open:x,save:e=>g({...u?.c??C,...e}),acceptAll:()=>g({analytics:!0,marketing:!0}),rejectAll:()=>g(C),sync:y};return s.keksi=T,y(),u&&w(u.c),T}j(JSON.parse(document.getElementById("keksi-data").textContent));})();'}</script>`;
|
|
286
328
|
}
|
|
287
329
|
function cookieTable(config, { lang = "fi", headingLevel = 2 } = {}) {
|
|
288
330
|
return `<div class="keksi-policy">${sections(config, lang, headingLevel, false)}</div>`;
|
|
@@ -303,7 +345,7 @@ function sections(config, lang, h, dialog) {
|
|
|
303
345
|
const items = c === "necessary" ? [site] : active.filter((p) => p.category === c).map((p) => ({ ...p, purpose: p.purpose[lang] }));
|
|
304
346
|
const head = c === "necessary" ? `<h${h}>${e(t.necessary)}</h${h}><span>${e(t.alwaysOn)}</span>` : dialog ? `<h${h}><label for="keksi-c-${c}">${e(t[c])}</label></h${h}><input type="checkbox" role="switch" id="keksi-c-${c}" name="${c}" aria-describedby="keksi-i-${c}">` : `<h${h}>${e(t[c])}</h${h}>`;
|
|
305
347
|
const list2 = items.map(
|
|
306
|
-
(p) => `<div class="keksi-provider"><h${h + 1}>${e(p.name)}</h${h + 1}>${p.purpose ? `<p>${e(p.purpose)}</p>` : ""}` + (p.company && p.privacyPolicy ? `<p>${e(p.company)} \xB7 <a href="${e(p.privacyPolicy)}" target="_blank" rel="noopener">${e(t.privacyPolicy)}</a></p>` + (p.transfer ? `<p>${e(t.
|
|
348
|
+
(p) => `<div class="keksi-provider"><h${h + 1}>${e(p.name)}</h${h + 1}>${p.purpose ? `<p>${e(p.purpose)}</p>` : ""}` + (p.company && p.privacyPolicy ? `<p>${e(p.company)} \xB7 <a href="${e(p.privacyPolicy)}" target="_blank" rel="noopener">${e(t.privacyPolicy)}</a></p>` + (p.transfer ? `<p>${e(t[p.transfer])}</p>` : "") : "") + table(p.cookies, t) + `</div>`
|
|
307
349
|
).join("");
|
|
308
350
|
const body = dialog ? `<details><summary>${e(t.providers)} (${items.length})</summary>${list2}</details>` : list2;
|
|
309
351
|
return `<section class="keksi-cat"><div class="keksi-cat-head">${head}</div><p id="keksi-i-${c}">${e(t[`${c}Info`])}</p>${body}</section>`;
|
package/dist/playwright.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
var fi = {
|
|
3
3
|
title: "Ev\xE4steet",
|
|
4
4
|
intro: "K\xE4yt\xE4mme v\xE4ltt\xE4m\xE4tt\xF6mien ev\xE4steiden lis\xE4ksi seuraavia palveluita vain, jos annat siihen luvan:",
|
|
5
|
-
|
|
5
|
+
change: "Voit muuttaa valintaasi milloin tahansa.",
|
|
6
6
|
more: "Lue lis\xE4\xE4",
|
|
7
7
|
accept: "Hyv\xE4ksy kaikki",
|
|
8
8
|
reject: "Hylk\xE4\xE4 kaikki",
|
|
@@ -22,6 +22,7 @@ var fi = {
|
|
|
22
22
|
consentPurpose: "Tallentaa ev\xE4stevalintasi.",
|
|
23
23
|
privacyPolicy: "Tietosuojaseloste",
|
|
24
24
|
dpf: "Tietoja voidaan siirt\xE4\xE4 Yhdysvaltoihin EU:n ja Yhdysvaltojen v\xE4lisen tietosuojakehyksen (DPF) perusteella.",
|
|
25
|
+
us: "Tietoja siirret\xE4\xE4n Yhdysvaltoihin.",
|
|
25
26
|
name: "Nimi",
|
|
26
27
|
duration: "Kesto",
|
|
27
28
|
purpose: "Tarkoitus",
|
|
@@ -39,7 +40,7 @@ var fi = {
|
|
|
39
40
|
var sv = {
|
|
40
41
|
title: "Kakor",
|
|
41
42
|
intro: "Ut\xF6ver n\xF6dv\xE4ndiga kakor anv\xE4nder vi f\xF6ljande tj\xE4nster endast om du ger ditt samtycke:",
|
|
42
|
-
|
|
43
|
+
change: "Du kan \xE4ndra ditt val n\xE4r som helst.",
|
|
43
44
|
more: "L\xE4s mer",
|
|
44
45
|
accept: "Godk\xE4nn alla",
|
|
45
46
|
reject: "Avvisa alla",
|
|
@@ -59,6 +60,7 @@ var sv = {
|
|
|
59
60
|
consentPurpose: "Sparar ditt val av kakor.",
|
|
60
61
|
privacyPolicy: "Dataskyddsbeskrivning",
|
|
61
62
|
dpf: "Uppgifter kan \xF6verf\xF6ras till USA med st\xF6d av ramverket f\xF6r dataskydd mellan EU och USA (DPF).",
|
|
63
|
+
us: "Uppgifter \xF6verf\xF6rs till USA.",
|
|
62
64
|
name: "Namn",
|
|
63
65
|
duration: "Varaktighet",
|
|
64
66
|
purpose: "Syfte",
|
|
@@ -76,7 +78,7 @@ var sv = {
|
|
|
76
78
|
var en = {
|
|
77
79
|
title: "Cookies",
|
|
78
80
|
intro: "Besides necessary cookies, we use the following services only if you allow it:",
|
|
79
|
-
|
|
81
|
+
change: "You can change your choice at any time.",
|
|
80
82
|
more: "Read more",
|
|
81
83
|
accept: "Accept all",
|
|
82
84
|
reject: "Reject all",
|
|
@@ -96,6 +98,7 @@ var en = {
|
|
|
96
98
|
consentPurpose: "Stores your cookie choice.",
|
|
97
99
|
privacyPolicy: "Privacy policy",
|
|
98
100
|
dpf: "Data may be transferred to the United States under the EU\u2013US Data Privacy Framework (DPF).",
|
|
101
|
+
us: "Data is transferred to the United States.",
|
|
99
102
|
name: "Name",
|
|
100
103
|
duration: "Duration",
|
|
101
104
|
purpose: "Purpose",
|
|
@@ -172,6 +175,29 @@ var providers = [
|
|
|
172
175
|
],
|
|
173
176
|
hosts: ["facebook.net", "facebook.com"]
|
|
174
177
|
},
|
|
178
|
+
{
|
|
179
|
+
id: "posthog",
|
|
180
|
+
name: "PostHog",
|
|
181
|
+
company: "PostHog Inc.",
|
|
182
|
+
privacyPolicy: "https://posthog.com/privacy",
|
|
183
|
+
category: "analytics",
|
|
184
|
+
purpose: {
|
|
185
|
+
fi: "Tuoteanalytiikka: sivujen katselut, klikkaukset ja k\xE4ytt\xF6tavat. Voi tallentaa istuntoja, jos istuntotallenteet ovat k\xE4yt\xF6ss\xE4.",
|
|
186
|
+
sv: "Produktanalys: sidvisningar, klick och anv\xE4ndningsm\xF6nster. Kan spela in sessioner om sessionsinspelning \xE4r aktiverad.",
|
|
187
|
+
en: "Product analytics: page views, clicks and usage patterns. Can record sessions if session replay is enabled."
|
|
188
|
+
},
|
|
189
|
+
cookies: [
|
|
190
|
+
{ name: "ph_*_posthog", duration: "1y" },
|
|
191
|
+
{ name: "ph_*_posthog_cpm", duration: "1y" },
|
|
192
|
+
{ name: "ph_*_posthog", duration: "persistent", storage: "localStorage" },
|
|
193
|
+
{ name: "ph_*_posthog__flags", duration: "persistent", storage: "localStorage" },
|
|
194
|
+
{ name: "ph_*_posthog", duration: "session", storage: "sessionStorage" },
|
|
195
|
+
{ name: "ph_*_primary_window_exists", duration: "session", storage: "sessionStorage" },
|
|
196
|
+
{ name: "ph_*_session_registered_properties", duration: "session", storage: "sessionStorage" },
|
|
197
|
+
{ name: "ph_*_window_id", duration: "session", storage: "sessionStorage" }
|
|
198
|
+
],
|
|
199
|
+
hosts: ["posthog.com"]
|
|
200
|
+
},
|
|
175
201
|
{
|
|
176
202
|
id: "youtube",
|
|
177
203
|
name: "YouTube",
|
|
@@ -197,8 +223,11 @@ var providers = [
|
|
|
197
223
|
|
|
198
224
|
// src/config.ts
|
|
199
225
|
var list = (v) => v === void 0 ? [] : Array.isArray(v) ? v : [v];
|
|
226
|
+
var posthogHost = (config) => (config.posthog?.host ?? "https://eu.i.posthog.com").replace(/\/$/, "");
|
|
200
227
|
function activeProviders(config) {
|
|
201
|
-
return providers.filter(
|
|
228
|
+
return providers.filter(
|
|
229
|
+
(p) => p.id === "youtube" ? config.youtube : p.id === "posthog" ? config.posthog?.key : list(config[p.id]).length
|
|
230
|
+
).map((p) => p.id === "posthog" && /\/\/us\./.test(posthogHost(config)) ? { ...p, transfer: "us" } : p);
|
|
202
231
|
}
|
|
203
232
|
function textsFor(config, locale) {
|
|
204
233
|
return { ...texts[locale], ...config.texts?.[locale] };
|
|
@@ -220,6 +249,7 @@ function runtimeData(config, locale) {
|
|
|
220
249
|
ga: list(config.ga),
|
|
221
250
|
ads: list(config.googleAds),
|
|
222
251
|
meta: list(config.meta),
|
|
252
|
+
ph: config.posthog?.key ? { key: config.posthog.key, host: posthogHost(config) } : void 0,
|
|
223
253
|
yt: !!config.youtube,
|
|
224
254
|
del: { analytics: del("analytics"), marketing: del("marketing") },
|
|
225
255
|
t: { ytText: t.ytText, ytButton: t.ytButton, ytOpen: t.ytOpen }
|
package/dist/render.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface RenderOptions {
|
|
|
5
5
|
/** For a CSP with nonces; applied to the inline `<style>` and `<script>`. */
|
|
6
6
|
nonce?: string;
|
|
7
7
|
}
|
|
8
|
-
/** Banner
|
|
8
|
+
/** Banner with its settings view, and the runtime. Place it right after `<body>`. Empty when no provider needs consent. */
|
|
9
9
|
export declare function renderKeksi(config: KeksiConfig, { lang, nonce }?: RenderOptions): string;
|
|
10
10
|
export interface CookieTableOptions {
|
|
11
11
|
lang?: Locale;
|
package/dist/runtime.d.ts
CHANGED
|
@@ -21,6 +21,9 @@ declare global {
|
|
|
21
21
|
gtag?: (...a: unknown[]) => void;
|
|
22
22
|
fbq?: Queue;
|
|
23
23
|
_fbq?: Queue;
|
|
24
|
+
posthog?: unknown[] & Record<string, unknown> & {
|
|
25
|
+
_i: unknown[];
|
|
26
|
+
};
|
|
24
27
|
}
|
|
25
28
|
}
|
|
26
29
|
export declare function boot(d: RuntimeData, w?: Window & typeof globalThis): KeksiApi;
|
package/dist/scan.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/glob.ts
|
|
2
|
+
var glob = (pattern, name) => new RegExp(`^${pattern.split("*").map((s) => s.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*")}$`).test(name);
|
|
3
|
+
|
|
1
4
|
// src/providers.ts
|
|
2
5
|
var GOOGLE = { company: "Google Ireland Limited", privacyPolicy: "https://policies.google.com/privacy", transfer: "dpf" };
|
|
3
6
|
var META = { company: "Meta Platforms Ireland Limited", privacyPolicy: "https://www.facebook.com/privacy/policy/", transfer: "dpf" };
|
|
@@ -58,6 +61,29 @@ var providers = [
|
|
|
58
61
|
],
|
|
59
62
|
hosts: ["facebook.net", "facebook.com"]
|
|
60
63
|
},
|
|
64
|
+
{
|
|
65
|
+
id: "posthog",
|
|
66
|
+
name: "PostHog",
|
|
67
|
+
company: "PostHog Inc.",
|
|
68
|
+
privacyPolicy: "https://posthog.com/privacy",
|
|
69
|
+
category: "analytics",
|
|
70
|
+
purpose: {
|
|
71
|
+
fi: "Tuoteanalytiikka: sivujen katselut, klikkaukset ja k\xE4ytt\xF6tavat. Voi tallentaa istuntoja, jos istuntotallenteet ovat k\xE4yt\xF6ss\xE4.",
|
|
72
|
+
sv: "Produktanalys: sidvisningar, klick och anv\xE4ndningsm\xF6nster. Kan spela in sessioner om sessionsinspelning \xE4r aktiverad.",
|
|
73
|
+
en: "Product analytics: page views, clicks and usage patterns. Can record sessions if session replay is enabled."
|
|
74
|
+
},
|
|
75
|
+
cookies: [
|
|
76
|
+
{ name: "ph_*_posthog", duration: "1y" },
|
|
77
|
+
{ name: "ph_*_posthog_cpm", duration: "1y" },
|
|
78
|
+
{ name: "ph_*_posthog", duration: "persistent", storage: "localStorage" },
|
|
79
|
+
{ name: "ph_*_posthog__flags", duration: "persistent", storage: "localStorage" },
|
|
80
|
+
{ name: "ph_*_posthog", duration: "session", storage: "sessionStorage" },
|
|
81
|
+
{ name: "ph_*_primary_window_exists", duration: "session", storage: "sessionStorage" },
|
|
82
|
+
{ name: "ph_*_session_registered_properties", duration: "session", storage: "sessionStorage" },
|
|
83
|
+
{ name: "ph_*_window_id", duration: "session", storage: "sessionStorage" }
|
|
84
|
+
],
|
|
85
|
+
hosts: ["posthog.com"]
|
|
86
|
+
},
|
|
61
87
|
{
|
|
62
88
|
id: "youtube",
|
|
63
89
|
name: "YouTube",
|
|
@@ -113,7 +139,7 @@ var purposes = {
|
|
|
113
139
|
};
|
|
114
140
|
var trackerNames = providers.flatMap((p) => p.cookies.flatMap((c) => c.name.split(", ")));
|
|
115
141
|
var trackerHosts = providers.flatMap((p) => p.hosts);
|
|
116
|
-
var matches = (patterns, name) => patterns.some((p) =>
|
|
142
|
+
var matches = (patterns, name) => patterns.some((p) => glob(p, name));
|
|
117
143
|
function purposeOf(name) {
|
|
118
144
|
const k = Object.keys(purposes).find((p) => matches([p], name));
|
|
119
145
|
return k ? purposes[k] : void 0;
|
package/dist/sveltekit.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
var fi = {
|
|
3
3
|
title: "Ev\xE4steet",
|
|
4
4
|
intro: "K\xE4yt\xE4mme v\xE4ltt\xE4m\xE4tt\xF6mien ev\xE4steiden lis\xE4ksi seuraavia palveluita vain, jos annat siihen luvan:",
|
|
5
|
-
|
|
5
|
+
change: "Voit muuttaa valintaasi milloin tahansa.",
|
|
6
6
|
more: "Lue lis\xE4\xE4",
|
|
7
7
|
accept: "Hyv\xE4ksy kaikki",
|
|
8
8
|
reject: "Hylk\xE4\xE4 kaikki",
|
|
@@ -22,6 +22,7 @@ var fi = {
|
|
|
22
22
|
consentPurpose: "Tallentaa ev\xE4stevalintasi.",
|
|
23
23
|
privacyPolicy: "Tietosuojaseloste",
|
|
24
24
|
dpf: "Tietoja voidaan siirt\xE4\xE4 Yhdysvaltoihin EU:n ja Yhdysvaltojen v\xE4lisen tietosuojakehyksen (DPF) perusteella.",
|
|
25
|
+
us: "Tietoja siirret\xE4\xE4n Yhdysvaltoihin.",
|
|
25
26
|
name: "Nimi",
|
|
26
27
|
duration: "Kesto",
|
|
27
28
|
purpose: "Tarkoitus",
|
|
@@ -39,7 +40,7 @@ var fi = {
|
|
|
39
40
|
var sv = {
|
|
40
41
|
title: "Kakor",
|
|
41
42
|
intro: "Ut\xF6ver n\xF6dv\xE4ndiga kakor anv\xE4nder vi f\xF6ljande tj\xE4nster endast om du ger ditt samtycke:",
|
|
42
|
-
|
|
43
|
+
change: "Du kan \xE4ndra ditt val n\xE4r som helst.",
|
|
43
44
|
more: "L\xE4s mer",
|
|
44
45
|
accept: "Godk\xE4nn alla",
|
|
45
46
|
reject: "Avvisa alla",
|
|
@@ -59,6 +60,7 @@ var sv = {
|
|
|
59
60
|
consentPurpose: "Sparar ditt val av kakor.",
|
|
60
61
|
privacyPolicy: "Dataskyddsbeskrivning",
|
|
61
62
|
dpf: "Uppgifter kan \xF6verf\xF6ras till USA med st\xF6d av ramverket f\xF6r dataskydd mellan EU och USA (DPF).",
|
|
63
|
+
us: "Uppgifter \xF6verf\xF6rs till USA.",
|
|
62
64
|
name: "Namn",
|
|
63
65
|
duration: "Varaktighet",
|
|
64
66
|
purpose: "Syfte",
|
|
@@ -76,7 +78,7 @@ var sv = {
|
|
|
76
78
|
var en = {
|
|
77
79
|
title: "Cookies",
|
|
78
80
|
intro: "Besides necessary cookies, we use the following services only if you allow it:",
|
|
79
|
-
|
|
81
|
+
change: "You can change your choice at any time.",
|
|
80
82
|
more: "Read more",
|
|
81
83
|
accept: "Accept all",
|
|
82
84
|
reject: "Reject all",
|
|
@@ -96,6 +98,7 @@ var en = {
|
|
|
96
98
|
consentPurpose: "Stores your cookie choice.",
|
|
97
99
|
privacyPolicy: "Privacy policy",
|
|
98
100
|
dpf: "Data may be transferred to the United States under the EU\u2013US Data Privacy Framework (DPF).",
|
|
101
|
+
us: "Data is transferred to the United States.",
|
|
99
102
|
name: "Name",
|
|
100
103
|
duration: "Duration",
|
|
101
104
|
purpose: "Purpose",
|
|
@@ -172,6 +175,29 @@ var providers = [
|
|
|
172
175
|
],
|
|
173
176
|
hosts: ["facebook.net", "facebook.com"]
|
|
174
177
|
},
|
|
178
|
+
{
|
|
179
|
+
id: "posthog",
|
|
180
|
+
name: "PostHog",
|
|
181
|
+
company: "PostHog Inc.",
|
|
182
|
+
privacyPolicy: "https://posthog.com/privacy",
|
|
183
|
+
category: "analytics",
|
|
184
|
+
purpose: {
|
|
185
|
+
fi: "Tuoteanalytiikka: sivujen katselut, klikkaukset ja k\xE4ytt\xF6tavat. Voi tallentaa istuntoja, jos istuntotallenteet ovat k\xE4yt\xF6ss\xE4.",
|
|
186
|
+
sv: "Produktanalys: sidvisningar, klick och anv\xE4ndningsm\xF6nster. Kan spela in sessioner om sessionsinspelning \xE4r aktiverad.",
|
|
187
|
+
en: "Product analytics: page views, clicks and usage patterns. Can record sessions if session replay is enabled."
|
|
188
|
+
},
|
|
189
|
+
cookies: [
|
|
190
|
+
{ name: "ph_*_posthog", duration: "1y" },
|
|
191
|
+
{ name: "ph_*_posthog_cpm", duration: "1y" },
|
|
192
|
+
{ name: "ph_*_posthog", duration: "persistent", storage: "localStorage" },
|
|
193
|
+
{ name: "ph_*_posthog__flags", duration: "persistent", storage: "localStorage" },
|
|
194
|
+
{ name: "ph_*_posthog", duration: "session", storage: "sessionStorage" },
|
|
195
|
+
{ name: "ph_*_primary_window_exists", duration: "session", storage: "sessionStorage" },
|
|
196
|
+
{ name: "ph_*_session_registered_properties", duration: "session", storage: "sessionStorage" },
|
|
197
|
+
{ name: "ph_*_window_id", duration: "session", storage: "sessionStorage" }
|
|
198
|
+
],
|
|
199
|
+
hosts: ["posthog.com"]
|
|
200
|
+
},
|
|
175
201
|
{
|
|
176
202
|
id: "youtube",
|
|
177
203
|
name: "YouTube",
|
|
@@ -197,8 +223,11 @@ var providers = [
|
|
|
197
223
|
|
|
198
224
|
// src/config.ts
|
|
199
225
|
var list = (v) => v === void 0 ? [] : Array.isArray(v) ? v : [v];
|
|
226
|
+
var posthogHost = (config) => (config.posthog?.host ?? "https://eu.i.posthog.com").replace(/\/$/, "");
|
|
200
227
|
function activeProviders(config) {
|
|
201
|
-
return providers.filter(
|
|
228
|
+
return providers.filter(
|
|
229
|
+
(p) => p.id === "youtube" ? config.youtube : p.id === "posthog" ? config.posthog?.key : list(config[p.id]).length
|
|
230
|
+
).map((p) => p.id === "posthog" && /\/\/us\./.test(posthogHost(config)) ? { ...p, transfer: "us" } : p);
|
|
202
231
|
}
|
|
203
232
|
function categoriesOf(config) {
|
|
204
233
|
return ["analytics", "marketing"].filter((c) => activeProviders(config).some((p) => p.category === c));
|
|
@@ -223,6 +252,7 @@ function runtimeData(config, locale) {
|
|
|
223
252
|
ga: list(config.ga),
|
|
224
253
|
ads: list(config.googleAds),
|
|
225
254
|
meta: list(config.meta),
|
|
255
|
+
ph: config.posthog?.key ? { key: config.posthog.key, host: posthogHost(config) } : void 0,
|
|
226
256
|
yt: !!config.youtube,
|
|
227
257
|
del: { analytics: del("analytics"), marketing: del("marketing") },
|
|
228
258
|
t: { ytText: t.ytText, ytButton: t.ytButton, ytOpen: t.ytOpen }
|
|
@@ -231,34 +261,46 @@ function runtimeData(config, locale) {
|
|
|
231
261
|
|
|
232
262
|
// src/styles.ts
|
|
233
263
|
var css = [
|
|
234
|
-
":where(.keksi,.keksi-
|
|
235
|
-
":where(.keksi
|
|
236
|
-
":where(.keksi){position:fixed;z-index:2147483000;inset:auto 1rem 1rem;max-width:34rem;max-height:calc(100vh - 2rem);
|
|
237
|
-
":where(.keksi,.keksi-
|
|
238
|
-
":where(.keksi)
|
|
239
|
-
":where(.keksi
|
|
240
|
-
":where(.keksi
|
|
241
|
-
":where(.keksi-
|
|
242
|
-
":where(.keksi-
|
|
264
|
+
":where(.keksi,.keksi-policy){--_bg:var(--keksi-bg,#fff);--_fg:var(--keksi-fg,#1b1b1b);--_line:var(--keksi-line,#e3e3e3);--_soft:color-mix(in srgb,var(--_fg) 4%,var(--_bg));--_r:var(--keksi-radius,8px)}",
|
|
265
|
+
":where(.keksi){box-sizing:border-box;background:var(--_bg);color:var(--_fg);font:inherit;font-size:14px;line-height:1.5;border:1px solid var(--_line);border-radius:var(--_r);box-shadow:0 8px 32px #0000002e}",
|
|
266
|
+
":where(.keksi){position:fixed;z-index:2147483000;inset:auto 1rem 1rem;display:flex;flex-direction:column;max-width:34rem;max-height:calc(100vh - 2rem);margin-inline:auto;overflow:hidden}",
|
|
267
|
+
":where(.keksi,.keksi-main,.keksi-settings)[hidden]{display:none}",
|
|
268
|
+
":where(.keksi-main,.keksi-settings){display:flex;flex-direction:column;min-height:0;margin:0}",
|
|
269
|
+
":where(.keksi:has(.keksi-settings:not([hidden]))){max-width:40rem}",
|
|
270
|
+
":where(.keksi-settings .keksi-body){padding-block:0}",
|
|
271
|
+
":where(.keksi-head){display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.875rem 1.25rem;border-bottom:1px solid var(--_line)}",
|
|
272
|
+
":where(.keksi-head) :where(h2){font-size:1rem;font-weight:600;line-height:1.3;margin:0}",
|
|
273
|
+
":where(.keksi-body){padding:1rem 1.25rem;overflow:auto}",
|
|
274
|
+
":where(.keksi) :where(.keksi-body>*){margin:0 0 .75rem}",
|
|
275
|
+
":where(.keksi) :where(.keksi-body>:last-child){margin-bottom:0}",
|
|
276
|
+
":where(.keksi) :where(ul){list-style:none;padding:0}",
|
|
277
|
+
":where(.keksi) :where(strong){font-weight:600}",
|
|
278
|
+
":where(.keksi) :where(a){color:inherit}",
|
|
279
|
+
".keksi a{text-decoration:underline}",
|
|
280
|
+
":where(.keksi-actions){display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:.5rem;padding:.75rem 1.25rem;background:var(--_soft)}",
|
|
281
|
+
":where(.keksi-btn){min-width:7.5rem;font:inherit;font-weight:600;padding:.5rem 1rem;border:1px solid var(--_fg);border-radius:calc(var(--_r) - 2px);background:var(--_bg);color:var(--_fg);cursor:pointer}",
|
|
243
282
|
":where(.keksi-btn:hover){background:var(--_fg);color:var(--_bg)}",
|
|
244
|
-
":where(.keksi
|
|
245
|
-
":where(.keksi-
|
|
246
|
-
":where(.keksi-
|
|
247
|
-
":where(.keksi-
|
|
248
|
-
":where(.keksi-
|
|
249
|
-
":where(.keksi-
|
|
250
|
-
":where(.keksi-cat){
|
|
251
|
-
":where(.keksi-cat-head) :where(h2,h3,h4,h5){font-size:1rem;margin:0}",
|
|
283
|
+
":where(.keksi-text){margin-right:auto;padding:.5rem .25rem;font:inherit;border:0;background:none;color:inherit;text-decoration:underline;text-underline-offset:2px;cursor:pointer}",
|
|
284
|
+
"@media (max-width:30rem){:where(.keksi-btn){flex:1 1 auto}}",
|
|
285
|
+
":where(.keksi,keksi-youtube) :where(button,a,input,summary):focus-visible{outline:3px solid var(--keksi-focus,#2563eb);outline-offset:2px}",
|
|
286
|
+
":where(.keksi-close){font:inherit;font-size:1.375rem;line-height:1;margin:-.25rem -.5rem;padding:.25rem .5rem;border:0;background:none;color:inherit;cursor:pointer}",
|
|
287
|
+
":where(.keksi-cat){padding:1rem 0;border-top:1px solid var(--_line)}",
|
|
288
|
+
":where(.keksi-body>.keksi-cat:first-child){border-top:0}",
|
|
289
|
+
":where(.keksi-cat-head){display:flex;align-items:center;justify-content:space-between;gap:1rem}",
|
|
290
|
+
":where(.keksi-cat-head) :where(h2,h3,h4,h5){font-size:1rem;font-weight:600;margin:0}",
|
|
291
|
+
":where(.keksi-cat-head) :where(span){font-size:.8125rem;opacity:.7}",
|
|
252
292
|
":where(.keksi-cat) :where(p){margin:.25rem 0}",
|
|
253
|
-
":where(.keksi-cat input[role=switch]){appearance:none;position:relative;flex:none;width:2.
|
|
254
|
-
':where(.keksi-cat input[role=switch])::before{content:"";position:absolute;top:.
|
|
293
|
+
":where(.keksi-cat input[role=switch]){appearance:none;position:relative;flex:none;width:2.5rem;height:1.375rem;margin:0;border-radius:1rem;background:color-mix(in srgb,var(--_fg) 22%,var(--_bg));cursor:pointer}",
|
|
294
|
+
':where(.keksi-cat input[role=switch])::before{content:"";position:absolute;top:.1875rem;left:.1875rem;width:1rem;height:1rem;border-radius:50%;background:#fff;transition:transform .15s}',
|
|
255
295
|
":where(.keksi-cat input[role=switch]:checked){background:var(--_fg)}",
|
|
256
|
-
":where(.keksi-cat input[role=switch]:checked)::before{transform:translateX(1.
|
|
296
|
+
":where(.keksi-cat input[role=switch]:checked)::before{transform:translateX(1.125rem)}",
|
|
257
297
|
":where(.keksi-cat summary){margin-top:.5rem;font-weight:600;cursor:pointer}",
|
|
258
|
-
":where(.keksi-provider){margin:.75rem 0}",
|
|
259
|
-
":where(.keksi-provider)
|
|
260
|
-
":where(.keksi-
|
|
261
|
-
":where(.keksi-cat
|
|
298
|
+
":where(.keksi-provider){margin:.75rem 0;padding:.75rem .875rem;border-radius:calc(var(--_r) - 2px);background:var(--_soft)}",
|
|
299
|
+
":where(.keksi-policy .keksi-provider){padding:0;background:none}",
|
|
300
|
+
":where(.keksi-provider) :where(h3,h4,h5,h6){font-size:.9375rem;font-weight:600;margin:0}",
|
|
301
|
+
":where(.keksi-cat table){width:100%;border-collapse:collapse;font-size:.8125rem;margin-top:.5rem}",
|
|
302
|
+
":where(.keksi-cat) :where(th,td){padding:.3rem .75rem .3rem 0;border-bottom:1px solid var(--_line);text-align:left;vertical-align:top}",
|
|
303
|
+
":where(.keksi-cat) :where(th){font-weight:600}",
|
|
262
304
|
":where(keksi-youtube){display:block;aspect-ratio:16/9;background:#111;color:#fff}",
|
|
263
305
|
":where(keksi-youtube iframe){display:block;width:100%;height:100%;border:0}",
|
|
264
306
|
":where(.keksi-yt){display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.75rem;height:100%;padding:1rem;box-sizing:border-box;text-align:center}",
|
|
@@ -279,7 +321,7 @@ function renderKeksi(config, { lang = "fi", nonce } = {}) {
|
|
|
279
321
|
const btn = (action, label, type = "button") => `<button type="${type}" class="keksi-btn" data-keksi="${action}">${e(label)}</button>`;
|
|
280
322
|
const uses = cats.map((c) => `<li><strong>${e(t[c])}:</strong> ${e(active.filter((p) => p.category === c).map((p) => p.name).join(", "))}</li>`).join("");
|
|
281
323
|
const json = JSON.stringify(runtimeData(config, lang)).replace(/</g, "\\u003c");
|
|
282
|
-
return `<style${n}>${css}</style><section id="keksi-banner" class="keksi" aria-labelledby="keksi-title" hidden><h2 id="keksi-title">${e(t.title)}</h2><p>${e(t.intro)}</p><ul>${uses}</ul><p>${e(t.
|
|
324
|
+
return `<style${n}>${css}</style><section id="keksi-banner" class="keksi" aria-labelledby="keksi-title" hidden><div class="keksi-main"><div class="keksi-head"><h2 id="keksi-title">${e(t.title)}</h2></div><div class="keksi-body"><p>${e(t.intro)}</p><ul>${uses}</ul><p>${e(t.change)}${policy ? ` <a href="${e(policy)}">${e(t.more)}</a>` : ""}</p></div><div class="keksi-actions"><button type="button" class="keksi-text" data-keksi-open>${e(t.settings)}</button>${btn("reject", t.reject)}${btn("accept", t.accept)}</div></div><form id="keksi-settings" class="keksi-settings" aria-labelledby="keksi-settings-title" hidden><div class="keksi-head"><h2 id="keksi-settings-title">${e(t.settingsTitle)}</h2><button type="button" class="keksi-close" data-keksi="close" aria-label="${e(t.close)}">\xD7</button></div><div class="keksi-body">${sections(config, lang, 3, true)}</div><div class="keksi-actions">${btn("save", t.save)}${btn("reject", t.reject)}${btn("accept", t.accept)}</div></form></section><script type="application/json" id="keksi-data">${json}</script><script${n}>${'"use strict";(()=>{var M=(o,s)=>new RegExp(`^${o.split("*").map(i=>i.replace(/[.+?^${}()|[\\]\\\\]/g,"\\\\$&")).join(".*")}$`).test(s);var $=["analytics","marketing"],C={analytics:!1,marketing:!1};function p(...o){return arguments}function j(o,s=window){if(typeof s.keksi?.sync=="function")return s.keksi.sync(),s.keksi;let i=s.document,f=e=>i.getElementById(e),u=q(),_=!1,A=!1,S=!1,v=new Set,L=p("consent","default",{ad_storage:"denied",ad_user_data:"denied",ad_personalization:"denied",analytics_storage:"denied"});if(o.ga.length||o.ads.length){let e=s.dataLayer??(s.dataLayer=[]);s.gtag=function(){e.push(arguments)},e.push(L)}if(o.meta.length){let e=function(){e.callMethod?e.callMethod.apply(e,[...arguments]):e.queue.push(arguments)};Object.assign(e,{push:e,loaded:!0,version:"2.0",queue:[]}),s.fbq=s._fbq=e}if(o.ph){let e=Object.assign([],{_i:[],people:[]});for(let n of"capture identify alias reset register register_once unregister group setPersonProperties getFeatureFlag isFeatureEnabled onFeatureFlags reloadFeatureFlags get_distinct_id startSessionRecording stopSessionRecording captureException on".split(" "))e[n]=(...t)=>e.push([n,...t]);s.posthog=e}function q(){let e=i.cookie.split("; ").find(r=>r.startsWith(`${o.name}=`))?.slice(o.name.length+1),[n,,t,a]=e?.split(".")??[];return n!==o.v||!t||!a?null:{id:t,c:{analytics:a.includes("a"),marketing:a.includes("m")}}}function F(e){let n=u?.id??Array.from(s.crypto.getRandomValues(new Uint8Array(8)),a=>a.toString(16).padStart(2,"0")).join(""),t=(e.analytics?"a":"")+(e.marketing?"m":"")||"-";return i.cookie=`${o.name}=${o.v}.${Date.now().toString(36)}.${n}.${t}; Path=/; Max-Age=${o.days*86400}; SameSite=Lax`+(o.domain?`; Domain=${o.domain}`:"")+(s.location.protocol==="https:"?"; Secure":""),{id:n,c:e}}function E(e){let n=i.createElement("script");return n.async=!0,n.src=e,i.head.append(n),n}function w(e){let n=[...e.analytics?o.ga:[],...e.marketing?o.ads:[]].filter(t=>!v.has(t));if(n.length){let t=e.marketing?"granted":"denied",a=[p("consent","update",{analytics_storage:e.analytics?"granted":"denied",ad_storage:t,ad_user_data:t,ad_personalization:t})];v.size||a.push(p("js",new Date));for(let r of n)a.push(p("config",r)),v.add(r);if(_)for(let r of a)s.dataLayer.push(r);else{let r=s.dataLayer;r.splice(r.indexOf(L)+1,0,...a),E(`https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(n[0])}`),_=!0}}if(e.marketing&&o.meta.length&&!A&&(A=!0,s.fbq.queue.unshift(...o.meta.map(t=>p("init",t)),p("track","PageView")),E("https://connect.facebook.net/en_US/fbevents.js")),e.analytics&&o.ph&&!S&&(S=!0,s.posthog._i.push([o.ph.key,{api_host:o.ph.host,defaults:"2026-08-30"},"posthog"]),E(`${o.ph.host.replace(".i.posthog.com","-assets.i.posthog.com")}/static/array.js`).crossOrigin="anonymous"),e.marketing)for(let t of i.querySelectorAll("keksi-youtube"))t.show(!0)}function H(e){let n=e.flatMap(c=>o.del[c]),t=c=>n.some(l=>M(l,c)),a=s.location.hostname.split("."),r=["",...a.map((c,l)=>`; Domain=${a.slice(l).join(".")}`)];for(let c of i.cookie.split("; ")){let l=c.split("=")[0];if(t(l))for(let b of r)i.cookie=`${l}=; Max-Age=0; Path=/${b}`}try{for(let c of[s.localStorage,s.sessionStorage])for(let l of Object.keys(c))t(l)&&c.removeItem(l)}catch{}}function g(e){let n=u?.c;u=F(e),y(),i.dispatchEvent(new s.CustomEvent("keksi:change",{detail:{...e}})),H($.filter(t=>!e[t])),$.some(t=>n?.[t]&&!e[t])?s.location.reload():w(e)}function h(e=!1,n=!u||e){let t=f("keksi-banner"),a=f("keksi-settings");!t||!a||(t.hidden=!n,a.hidden=!e,t.firstElementChild.hidden=e)}function y(){h()}function x(){let e=f("keksi-settings");for(let n of $){let t=e?.querySelector(`[name=${n}]`);t&&(t.checked=!!u?.c[n])}h(!0),e?.querySelector(".keksi-close")?.focus()}i.addEventListener("click",e=>{let n=e.target.closest?.("[data-keksi],[data-keksi-open]");if(!n)return;if(n.hasAttribute("data-keksi-open"))return e.preventDefault(),x();let t=n.getAttribute("data-keksi");if(t==="close")h();else if(t==="accept")g({analytics:!0,marketing:!0});else if(t==="reject")g(C);else if(t==="save"){let a=r=>!!f("keksi-settings")?.querySelector(`[name=${r}]`)?.checked;g({analytics:a("analytics"),marketing:a("marketing")})}}),i.addEventListener("keydown",e=>{e.key==="Escape"&&f("keksi-settings")?.hidden===!1&&h()}),i.addEventListener("astro:after-swap",y);class I extends s.HTMLElement{connectedCallback(){this.show(!!u?.c.marketing)}show(n,t=!1){let a=encodeURIComponent(this.getAttribute("video")??""),r=this.getAttribute("label");if(n){if(this.querySelector("iframe"))return;let d=i.createElement("iframe");d.src=`https://www.youtube-nocookie.com/embed/${a}${t?"?autoplay=1":""}`,d.title=r??"YouTube",d.allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share",d.allowFullscreen=!0,d.referrerPolicy="strict-origin-when-cross-origin",this.replaceChildren(d),t&&d.focus();return}let c=i.createElement("div");c.className="keksi-yt";let l=this.getAttribute("poster");l&&(c.style.background=`linear-gradient(#000a,#000a),center/cover url(${JSON.stringify(l)})`);let b=i.createElement("p");b.textContent=o.t.ytText;let m=i.createElement("button");m.type="button",m.textContent=o.t.ytButton,r&&m.setAttribute("aria-label",`${o.t.ytButton}: ${r}`),m.onclick=()=>this.show(!0,!0);let k=i.createElement("a");k.href=`https://www.youtube.com/watch?v=${a}`,k.target="_blank",k.rel="noopener",k.textContent=o.t.ytOpen,c.append(b,m,k),this.replaceChildren(c)}}o.yt&&!s.customElements.get("keksi-youtube")&&s.customElements.define("keksi-youtube",I);let T={get consent(){return u?{...u.c}:null},open:x,save:e=>g({...u?.c??C,...e}),acceptAll:()=>g({analytics:!0,marketing:!0}),rejectAll:()=>g(C),sync:y};return s.keksi=T,y(),u&&w(u.c),T}j(JSON.parse(document.getElementById("keksi-data").textContent));})();'}</script>`;
|
|
283
325
|
}
|
|
284
326
|
function sections(config, lang, h, dialog) {
|
|
285
327
|
const t = textsFor(config, lang);
|
|
@@ -297,7 +339,7 @@ function sections(config, lang, h, dialog) {
|
|
|
297
339
|
const items = c === "necessary" ? [site] : active.filter((p) => p.category === c).map((p) => ({ ...p, purpose: p.purpose[lang] }));
|
|
298
340
|
const head = c === "necessary" ? `<h${h}>${e(t.necessary)}</h${h}><span>${e(t.alwaysOn)}</span>` : dialog ? `<h${h}><label for="keksi-c-${c}">${e(t[c])}</label></h${h}><input type="checkbox" role="switch" id="keksi-c-${c}" name="${c}" aria-describedby="keksi-i-${c}">` : `<h${h}>${e(t[c])}</h${h}>`;
|
|
299
341
|
const list2 = items.map(
|
|
300
|
-
(p) => `<div class="keksi-provider"><h${h + 1}>${e(p.name)}</h${h + 1}>${p.purpose ? `<p>${e(p.purpose)}</p>` : ""}` + (p.company && p.privacyPolicy ? `<p>${e(p.company)} \xB7 <a href="${e(p.privacyPolicy)}" target="_blank" rel="noopener">${e(t.privacyPolicy)}</a></p>` + (p.transfer ? `<p>${e(t.
|
|
342
|
+
(p) => `<div class="keksi-provider"><h${h + 1}>${e(p.name)}</h${h + 1}>${p.purpose ? `<p>${e(p.purpose)}</p>` : ""}` + (p.company && p.privacyPolicy ? `<p>${e(p.company)} \xB7 <a href="${e(p.privacyPolicy)}" target="_blank" rel="noopener">${e(t.privacyPolicy)}</a></p>` + (p.transfer ? `<p>${e(t[p.transfer])}</p>` : "") : "") + table(p.cookies, t) + `</div>`
|
|
301
343
|
).join("");
|
|
302
344
|
const body = dialog ? `<details><summary>${e(t.providers)} (${items.length})</summary>${list2}</details>` : list2;
|
|
303
345
|
return `<section class="keksi-cat"><div class="keksi-cat-head">${head}</div><p id="keksi-i-${c}">${e(t[`${c}Info`])}</p>${body}</section>`;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Texts } from './i18n.js';
|
|
2
2
|
export type Locale = 'fi' | 'sv' | 'en';
|
|
3
3
|
export type Category = 'analytics' | 'marketing';
|
|
4
|
-
export type ProviderId = 'ga' | 'googleAds' | 'meta' | 'youtube';
|
|
4
|
+
export type ProviderId = 'ga' | 'googleAds' | 'meta' | 'posthog' | 'youtube';
|
|
5
5
|
export type Consent = Record<Category, boolean>;
|
|
6
6
|
export interface KeksiConfig {
|
|
7
7
|
/** GA4 measurement ids, e.g. `G-XXXXXXX`. */
|
|
@@ -10,6 +10,11 @@ export interface KeksiConfig {
|
|
|
10
10
|
googleAds?: string | string[];
|
|
11
11
|
/** Meta Pixel ids. */
|
|
12
12
|
meta?: string | string[];
|
|
13
|
+
/** PostHog project token and ingestion host (default `https://eu.i.posthog.com`). */
|
|
14
|
+
posthog?: {
|
|
15
|
+
key: string;
|
|
16
|
+
host?: string;
|
|
17
|
+
};
|
|
13
18
|
/** Enables `<keksi-youtube>` embeds. */
|
|
14
19
|
youtube?: boolean;
|
|
15
20
|
/** Cookie policy page, linked from the banner. */
|
|
@@ -40,7 +45,7 @@ export interface CookieInfo {
|
|
|
40
45
|
duration: Duration;
|
|
41
46
|
/** Third-party host that sets it. Omitted = set on this site, deleted on withdrawal. */
|
|
42
47
|
host?: string;
|
|
43
|
-
storage?: 'localStorage';
|
|
48
|
+
storage?: 'localStorage' | 'sessionStorage';
|
|
44
49
|
}
|
|
45
50
|
export interface Provider {
|
|
46
51
|
id: ProviderId;
|
|
@@ -49,8 +54,8 @@ export interface Provider {
|
|
|
49
54
|
company: string;
|
|
50
55
|
privacyPolicy: string;
|
|
51
56
|
purpose: Record<Locale, string>;
|
|
52
|
-
/** Transfer outside the EU
|
|
53
|
-
transfer?: 'dpf';
|
|
57
|
+
/** Transfer outside the EU: under the DPF, or to the US with no basis stated. */
|
|
58
|
+
transfer?: 'dpf' | 'us';
|
|
54
59
|
cookies: CookieInfo[];
|
|
55
60
|
/** Hosts it contacts, for the Playwright check. */
|
|
56
61
|
hosts: string[];
|
|
@@ -64,8 +69,12 @@ export interface RuntimeData {
|
|
|
64
69
|
ga: string[];
|
|
65
70
|
ads: string[];
|
|
66
71
|
meta: string[];
|
|
72
|
+
ph?: {
|
|
73
|
+
key: string;
|
|
74
|
+
host: string;
|
|
75
|
+
};
|
|
67
76
|
yt: boolean;
|
|
68
|
-
/** First-party cookie and
|
|
77
|
+
/** First-party cookie and storage names per category, `*` = wildcard. */
|
|
69
78
|
del: Record<Category, string[]>;
|
|
70
79
|
t: Pick<Texts, 'ytText' | 'ytButton' | 'ytOpen'>;
|
|
71
80
|
}
|