keksi 0.1.4 → 0.1.5
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 +7 -3
- package/dist/config.d.ts +3 -0
- package/dist/index.js +29 -17
- package/dist/playwright.js +18 -10
- package/dist/sveltekit.js +29 -17
- package/dist/types.d.ts +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -102,9 +102,13 @@ posthog.capture('signup');
|
|
|
102
102
|
<keksi-youtube video="aqz-KE-bpKQ" label="Esittelyvideo" poster="/img/poster.jpg"></keksi-youtube>
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
-
- Shows a placeholder
|
|
106
|
-
-
|
|
107
|
-
-
|
|
105
|
+
- Shows a YouTube-style placeholder. Clicking play is the consent: it's remembered, and all videos load from
|
|
106
|
+
`youtube-nocookie.com` from then on.
|
|
107
|
+
- Marketing consent in the banner covers YouTube too, and turning it off withdraws YouTube.
|
|
108
|
+
- With YouTube as the only provider, the banner doesn't show by itself. The `data-keksi-open` link still opens the
|
|
109
|
+
settings for withdrawing.
|
|
110
|
+
- `poster` is optional and should be self-hosted. YouTube's thumbnails come from `i.ytimg.com`, which would contact
|
|
111
|
+
Google before consent.
|
|
108
112
|
|
|
109
113
|
## API
|
|
110
114
|
|
package/dist/config.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ import type { Category, KeksiConfig, Locale, Provider, RuntimeData } from './typ
|
|
|
3
3
|
export declare function defineConfig(config: KeksiConfig): KeksiConfig;
|
|
4
4
|
export declare function activeProviders(config: KeksiConfig): Provider[];
|
|
5
5
|
export declare function categoriesOf(config: KeksiConfig): Category[];
|
|
6
|
+
/** False when YouTube is the only provider: a click on a video is the consent, so the banner doesn't show by itself. */
|
|
7
|
+
export declare function needsBanner(config: KeksiConfig): boolean;
|
|
8
|
+
export declare function policyOf(config: KeksiConfig, locale: Locale): string | undefined;
|
|
6
9
|
export declare function textsFor(config: KeksiConfig, locale: Locale): Texts;
|
|
7
10
|
/** Changes when a provider is added or removed, which asks everyone again. Ids and texts can change freely. */
|
|
8
11
|
export declare function version(config: KeksiConfig): string;
|
package/dist/index.js
CHANGED
|
@@ -33,9 +33,9 @@ var fi = {
|
|
|
33
33
|
mo: "kuukausi|kuukautta",
|
|
34
34
|
d: "p\xE4iv\xE4|p\xE4iv\xE4\xE4",
|
|
35
35
|
min: "minuutti|minuuttia",
|
|
36
|
-
ytText: "
|
|
37
|
-
ytButton: "
|
|
38
|
-
ytOpen: "
|
|
36
|
+
ytText: "Toistamalla videon hyv\xE4ksyt YouTuben ev\xE4steet.",
|
|
37
|
+
ytButton: "Toista video",
|
|
38
|
+
ytOpen: "Katso YouTubessa"
|
|
39
39
|
};
|
|
40
40
|
var sv = {
|
|
41
41
|
title: "Kakor",
|
|
@@ -71,9 +71,9 @@ var sv = {
|
|
|
71
71
|
mo: "m\xE5nad|m\xE5nader",
|
|
72
72
|
d: "dag|dagar",
|
|
73
73
|
min: "minut|minuter",
|
|
74
|
-
ytText: "
|
|
75
|
-
ytButton: "
|
|
76
|
-
ytOpen: "
|
|
74
|
+
ytText: "Genom att spela upp videon godk\xE4nner du kakor fr\xE5n YouTube.",
|
|
75
|
+
ytButton: "Spela upp video",
|
|
76
|
+
ytOpen: "Titta p\xE5 YouTube"
|
|
77
77
|
};
|
|
78
78
|
var en = {
|
|
79
79
|
title: "Cookies",
|
|
@@ -109,9 +109,9 @@ var en = {
|
|
|
109
109
|
mo: "month|months",
|
|
110
110
|
d: "day|days",
|
|
111
111
|
min: "minute|minutes",
|
|
112
|
-
ytText: "
|
|
113
|
-
ytButton: "
|
|
114
|
-
ytOpen: "
|
|
112
|
+
ytText: "By playing the video, you accept cookies from YouTube.",
|
|
113
|
+
ytButton: "Play video",
|
|
114
|
+
ytOpen: "Watch on YouTube"
|
|
115
115
|
};
|
|
116
116
|
var texts = { fi, sv, en };
|
|
117
117
|
|
|
@@ -235,6 +235,12 @@ function activeProviders(config) {
|
|
|
235
235
|
function categoriesOf(config) {
|
|
236
236
|
return ["analytics", "marketing"].filter((c) => activeProviders(config).some((p) => p.category === c));
|
|
237
237
|
}
|
|
238
|
+
function needsBanner(config) {
|
|
239
|
+
return activeProviders(config).some((p) => p.id !== "youtube");
|
|
240
|
+
}
|
|
241
|
+
function policyOf(config, locale) {
|
|
242
|
+
return typeof config.policyUrl === "string" ? config.policyUrl : config.policyUrl?.[locale];
|
|
243
|
+
}
|
|
238
244
|
function textsFor(config, locale) {
|
|
239
245
|
return { ...texts[locale], ...config.texts?.[locale] };
|
|
240
246
|
}
|
|
@@ -257,8 +263,10 @@ function runtimeData(config, locale) {
|
|
|
257
263
|
meta: list(config.meta),
|
|
258
264
|
ph: config.posthog?.key ? { key: config.posthog.key, host: posthogHost(config) } : void 0,
|
|
259
265
|
yt: !!config.youtube,
|
|
266
|
+
banner: needsBanner(config),
|
|
267
|
+
policy: policyOf(config, locale),
|
|
260
268
|
del: { analytics: del("analytics"), marketing: del("marketing") },
|
|
261
|
-
t: { ytText: t.ytText, ytButton: t.ytButton, ytOpen: t.ytOpen }
|
|
269
|
+
t: { ytText: t.ytText, ytButton: t.ytButton, ytOpen: t.ytOpen, more: t.more }
|
|
262
270
|
};
|
|
263
271
|
}
|
|
264
272
|
|
|
@@ -304,12 +312,16 @@ var css = [
|
|
|
304
312
|
":where(.keksi-cat table){width:100%;border-collapse:collapse;font-size:.8125rem;margin-top:.5rem}",
|
|
305
313
|
":where(.keksi-cat) :where(th,td){padding:.3rem .75rem .3rem 0;border-bottom:1px solid var(--_line);text-align:left;vertical-align:top}",
|
|
306
314
|
":where(.keksi-cat) :where(th){font-weight:600}",
|
|
307
|
-
":where(keksi-youtube){display:block;aspect-ratio:16/9;background:#
|
|
315
|
+
":where(keksi-youtube){position:relative;display:block;aspect-ratio:16/9;overflow:hidden;border-radius:12px;background:#0f0f0f;color:#fff;font-family:Roboto,Arial,sans-serif}",
|
|
308
316
|
":where(keksi-youtube iframe){display:block;width:100%;height:100%;border:0}",
|
|
309
|
-
":where(.keksi-yt){
|
|
310
|
-
":where(.keksi-yt) :
|
|
311
|
-
":where(.keksi-yt) :where(
|
|
312
|
-
":where(.keksi-yt
|
|
317
|
+
":where(keksi-youtube[label]:has(.keksi-yt))::before{content:attr(label);position:absolute;inset:0 0 auto;z-index:1;padding:1rem 1.25rem 2.5rem;font-size:1.125rem;font-weight:500;background:linear-gradient(#000b,#0000);pointer-events:none}",
|
|
318
|
+
":where(.keksi-yt){position:relative;display:grid;grid-template-rows:1fr auto auto;gap:.25rem;height:100%;padding:0 1.25rem 1rem;box-sizing:border-box;font-size:.875rem;line-height:1.45;background:linear-gradient(#0000 55%,#000c),center/cover var(--keksi-poster,none)}",
|
|
319
|
+
":where(.keksi-yt) :where(p){margin:0}",
|
|
320
|
+
":where(.keksi-yt) :where(p:first-of-type){grid-row:2}",
|
|
321
|
+
":where(.keksi-yt) :where(p:last-of-type){grid-row:3;color:#fffc}",
|
|
322
|
+
":where(.keksi-yt) :where(a){color:inherit;text-decoration:underline;text-underline-offset:2px}",
|
|
323
|
+
`:where(.keksi-yt button){position:absolute;inset:0;width:68px;height:48px;margin:auto;padding:0;border:0;font-size:0;color:transparent;background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 68 48'%3E%3Cpath d='M66.52 7.74c-.78-2.93-2.49-5.41-5.42-6.19C55.79.13 34 0 34 0S12.21.13 6.9 1.55c-2.93.78-4.63 3.26-5.42 6.19C.06 13.05 0 24 0 24s.06 10.95 1.48 16.26c.78 2.93 2.49 5.41 5.42 6.19C12.21 47.87 34 48 34 48s21.79-.13 27.1-1.55c2.93-.78 4.64-3.26 5.42-6.19C67.94 34.95 68 24 68 24s-.06-10.95-1.48-16.26z' fill='%23f00'/%3E%3Cpath d='M45 24 27 14v20' fill='%23fff'/%3E%3C/svg%3E") center/contain no-repeat;cursor:pointer;transition:transform .15s}`,
|
|
324
|
+
":where(.keksi-yt button:hover){transform:scale(1.08)}"
|
|
313
325
|
].join("");
|
|
314
326
|
|
|
315
327
|
// src/render.ts
|
|
@@ -319,12 +331,12 @@ function renderKeksi(config, { lang = "fi", nonce } = {}) {
|
|
|
319
331
|
if (!cats.length) return "";
|
|
320
332
|
const t = textsFor(config, lang);
|
|
321
333
|
const active = activeProviders(config);
|
|
322
|
-
const policy =
|
|
334
|
+
const policy = policyOf(config, lang);
|
|
323
335
|
const n = nonce ? ` nonce="${e(nonce)}"` : "";
|
|
324
336
|
const btn = (action, label, type = "button") => `<button type="${type}" class="keksi-btn" data-keksi="${action}">${e(label)}</button>`;
|
|
325
337
|
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("");
|
|
326
338
|
const json = JSON.stringify(runtimeData(config, lang)).replace(/</g, "\\u003c");
|
|
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
|
|
339
|
+
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 F=(n,s)=>new RegExp(`^${n.split("*").map(i=>i.replace(/[.+?^${}()|[\\]\\\\]/g,"\\\\$&")).join(".*")}$`).test(s);var _=["analytics","marketing"],v={analytics:!1,marketing:!1};function f(...n){return arguments}function H(n,s=window){if(typeof s.keksi?.sync=="function")return s.keksi.sync(),s.keksi;let i=s.document,g=e=>i.getElementById(e),c=I(),A=!1,w=!1,L=!1,E=new Set,x=f("consent","default",{ad_storage:"denied",ad_user_data:"denied",ad_personalization:"denied",analytics_storage:"denied"});if(n.ga.length||n.ads.length){let e=s.dataLayer??(s.dataLayer=[]);s.gtag=function(){e.push(arguments)},e.push(x)}if(n.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(n.ph){let e=Object.assign([],{_i:[],people:[]});for(let o of"capture identify alias reset register register_once unregister group setPersonProperties getFeatureFlag isFeatureEnabled onFeatureFlags reloadFeatureFlags get_distinct_id startSessionRecording stopSessionRecording captureException on".split(" "))e[o]=(...t)=>e.push([o,...t]);s.posthog=e}function I(){let e=i.cookie.split("; ").find(l=>l.startsWith(`${n.name}=`))?.slice(n.name.length+1),[o,,t,a]=e?.split(".")??[];if(o!==n.v||!t||!a)return null;let r={analytics:a.includes("a"),marketing:a.includes("m")};return{id:t,c:r,yt:r.marketing||a.includes("y"),done:!a.includes("u")}}function T(e,o=e.marketing,t=!0){let a=c?.id??Array.from(s.crypto.getRandomValues(new Uint8Array(8)),l=>l.toString(16).padStart(2,"0")).join(""),r=(t?"":"u")+(e.analytics?"a":"")+(e.marketing?"m":"")+(o&&!e.marketing?"y":"")||"-";return i.cookie=`${n.name}=${n.v}.${Date.now().toString(36)}.${a}.${r}; Path=/; Max-Age=${n.days*86400}; SameSite=Lax`+(n.domain?`; Domain=${n.domain}`:"")+(s.location.protocol==="https:"?"; Secure":""),{id:a,c:e,yt:o,done:t}}function $(e){let o=i.createElement("script");return o.async=!0,o.src=e,i.head.append(o),o}function C(e){let o=[...e.analytics?n.ga:[],...e.marketing?n.ads:[]].filter(t=>!E.has(t));if(o.length){let t=e.marketing?"granted":"denied",a=[f("consent","update",{analytics_storage:e.analytics?"granted":"denied",ad_storage:t,ad_user_data:t,ad_personalization:t})];E.size||a.push(f("js",new Date));for(let r of o)a.push(f("config",r)),E.add(r);if(A)for(let r of a)s.dataLayer.push(r);else{let r=s.dataLayer;r.splice(r.indexOf(x)+1,0,...a),$(`https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(o[0])}`),A=!0}}if(e.marketing&&n.meta.length&&!w&&(w=!0,s.fbq.queue.unshift(...n.meta.map(t=>f("init",t)),f("track","PageView")),$("https://connect.facebook.net/en_US/fbevents.js")),e.analytics&&n.ph&&!L&&(L=!0,s.posthog._i.push([n.ph.key,{api_host:n.ph.host,defaults:"2026-08-30"},"posthog"]),$(`${n.ph.host.replace(".i.posthog.com","-assets.i.posthog.com")}/static/array.js`).crossOrigin="anonymous"),c?.yt)for(let t of i.querySelectorAll("keksi-youtube"))t.show(!0)}function O(){c?.yt||(c=T(c?.c??v,!0,c?.done??!1)),C(c.c)}function R(e){let o=e.flatMap(l=>n.del[l]),t=l=>o.some(u=>F(u,l)),a=s.location.hostname.split("."),r=["",...a.map((l,u)=>`; Domain=${a.slice(u).join(".")}`)];for(let l of i.cookie.split("; ")){let u=l.split("=")[0];if(t(u))for(let b of r)i.cookie=`${u}=; Max-Age=0; Path=/${b}`}try{for(let l of[s.localStorage,s.sessionStorage])for(let u of Object.keys(l))t(u)&&l.removeItem(u)}catch{}}function p(e){let o=c;c=T(e),h(),i.dispatchEvent(new s.CustomEvent("keksi:change",{detail:{...e}})),R(_.filter(t=>!e[t])),_.some(t=>o?.c[t]&&!e[t])||o?.yt&&!c.yt?s.location.reload():C(e)}function y(e=!1,o=n.banner&&!c?.done||e){let t=g("keksi-banner"),a=g("keksi-settings");!t||!a||(t.hidden=!o,a.hidden=!e,t.firstElementChild.hidden=e)}function h(){y()}function M(){let e=g("keksi-settings");for(let o of _){let t=e?.querySelector(`[name=${o}]`);t&&(t.checked=!!c?.c[o])}y(!0),e?.querySelector(".keksi-close")?.focus()}i.addEventListener("click",e=>{let o=e.target.closest?.("[data-keksi],[data-keksi-open]");if(!o)return;if(o.hasAttribute("data-keksi-open"))return e.preventDefault(),M();let t=o.getAttribute("data-keksi");if(t==="close")y();else if(t==="accept")p({analytics:!0,marketing:!0});else if(t==="reject")p(v);else if(t==="save"){let a=r=>!!g("keksi-settings")?.querySelector(`[name=${r}]`)?.checked;p({analytics:a("analytics"),marketing:a("marketing")})}}),i.addEventListener("keydown",e=>{e.key==="Escape"&&g("keksi-settings")?.hidden===!1&&y()}),i.addEventListener("astro:after-swap",h);class D extends s.HTMLElement{connectedCallback(){this.show(!!c?.yt)}show(o,t=!1){let a=encodeURIComponent(this.getAttribute("video")??""),r=this.getAttribute("label");if(o){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 l=i.createElement("div");l.className="keksi-yt";let u=this.getAttribute("poster");u&&l.style.setProperty("--keksi-poster",`url(${JSON.stringify(u)})`);let b=i.createElement("p");b.textContent=n.t.ytText;let m=i.createElement("button");m.type="button",m.textContent=n.t.ytButton,r&&m.setAttribute("aria-label",`${n.t.ytButton}: ${r}`),m.onclick=()=>{this.show(!0,!0),O()};let S=i.createElement("p"),q=(d,N,P=!1)=>{let k=i.createElement("a");k.href=d,k.textContent=N,P&&(k.target="_blank",k.rel="noopener"),S.append(S.childNodes.length?" \\xB7 ":"",k)};q(`https://www.youtube.com/watch?v=${a}`,n.t.ytOpen,!0),n.policy&&q(n.policy,n.t.more),l.append(b,m,S),this.replaceChildren(l)}}n.yt&&!s.customElements.get("keksi-youtube")&&s.customElements.define("keksi-youtube",D);let j={get consent(){return c?.done?{...c.c}:null},open:M,save:e=>p({...c?.c??v,...e}),acceptAll:()=>p({analytics:!0,marketing:!0}),rejectAll:()=>p(v),sync:h};return s.keksi=j,h(),c&&C(c.c),j}H(JSON.parse(document.getElementById("keksi-data").textContent));})();'}</script>`;
|
|
328
340
|
}
|
|
329
341
|
function cookieTable(config, { lang = "fi", headingLevel = 2 } = {}) {
|
|
330
342
|
return `<div class="keksi-policy">${sections(config, lang, headingLevel, false)}</div>`;
|
package/dist/playwright.js
CHANGED
|
@@ -33,9 +33,9 @@ var fi = {
|
|
|
33
33
|
mo: "kuukausi|kuukautta",
|
|
34
34
|
d: "p\xE4iv\xE4|p\xE4iv\xE4\xE4",
|
|
35
35
|
min: "minuutti|minuuttia",
|
|
36
|
-
ytText: "
|
|
37
|
-
ytButton: "
|
|
38
|
-
ytOpen: "
|
|
36
|
+
ytText: "Toistamalla videon hyv\xE4ksyt YouTuben ev\xE4steet.",
|
|
37
|
+
ytButton: "Toista video",
|
|
38
|
+
ytOpen: "Katso YouTubessa"
|
|
39
39
|
};
|
|
40
40
|
var sv = {
|
|
41
41
|
title: "Kakor",
|
|
@@ -71,9 +71,9 @@ var sv = {
|
|
|
71
71
|
mo: "m\xE5nad|m\xE5nader",
|
|
72
72
|
d: "dag|dagar",
|
|
73
73
|
min: "minut|minuter",
|
|
74
|
-
ytText: "
|
|
75
|
-
ytButton: "
|
|
76
|
-
ytOpen: "
|
|
74
|
+
ytText: "Genom att spela upp videon godk\xE4nner du kakor fr\xE5n YouTube.",
|
|
75
|
+
ytButton: "Spela upp video",
|
|
76
|
+
ytOpen: "Titta p\xE5 YouTube"
|
|
77
77
|
};
|
|
78
78
|
var en = {
|
|
79
79
|
title: "Cookies",
|
|
@@ -109,9 +109,9 @@ var en = {
|
|
|
109
109
|
mo: "month|months",
|
|
110
110
|
d: "day|days",
|
|
111
111
|
min: "minute|minutes",
|
|
112
|
-
ytText: "
|
|
113
|
-
ytButton: "
|
|
114
|
-
ytOpen: "
|
|
112
|
+
ytText: "By playing the video, you accept cookies from YouTube.",
|
|
113
|
+
ytButton: "Play video",
|
|
114
|
+
ytOpen: "Watch on YouTube"
|
|
115
115
|
};
|
|
116
116
|
var texts = { fi, sv, en };
|
|
117
117
|
|
|
@@ -229,6 +229,12 @@ function activeProviders(config) {
|
|
|
229
229
|
(p) => p.id === "youtube" ? config.youtube : p.id === "posthog" ? config.posthog?.key : list(config[p.id]).length
|
|
230
230
|
).map((p) => p.id === "posthog" && /\/\/us\./.test(posthogHost(config)) ? { ...p, transfer: "us" } : p);
|
|
231
231
|
}
|
|
232
|
+
function needsBanner(config) {
|
|
233
|
+
return activeProviders(config).some((p) => p.id !== "youtube");
|
|
234
|
+
}
|
|
235
|
+
function policyOf(config, locale) {
|
|
236
|
+
return typeof config.policyUrl === "string" ? config.policyUrl : config.policyUrl?.[locale];
|
|
237
|
+
}
|
|
232
238
|
function textsFor(config, locale) {
|
|
233
239
|
return { ...texts[locale], ...config.texts?.[locale] };
|
|
234
240
|
}
|
|
@@ -251,8 +257,10 @@ function runtimeData(config, locale) {
|
|
|
251
257
|
meta: list(config.meta),
|
|
252
258
|
ph: config.posthog?.key ? { key: config.posthog.key, host: posthogHost(config) } : void 0,
|
|
253
259
|
yt: !!config.youtube,
|
|
260
|
+
banner: needsBanner(config),
|
|
261
|
+
policy: policyOf(config, locale),
|
|
254
262
|
del: { analytics: del("analytics"), marketing: del("marketing") },
|
|
255
|
-
t: { ytText: t.ytText, ytButton: t.ytButton, ytOpen: t.ytOpen }
|
|
263
|
+
t: { ytText: t.ytText, ytButton: t.ytButton, ytOpen: t.ytOpen, more: t.more }
|
|
256
264
|
};
|
|
257
265
|
}
|
|
258
266
|
|
package/dist/sveltekit.js
CHANGED
|
@@ -33,9 +33,9 @@ var fi = {
|
|
|
33
33
|
mo: "kuukausi|kuukautta",
|
|
34
34
|
d: "p\xE4iv\xE4|p\xE4iv\xE4\xE4",
|
|
35
35
|
min: "minuutti|minuuttia",
|
|
36
|
-
ytText: "
|
|
37
|
-
ytButton: "
|
|
38
|
-
ytOpen: "
|
|
36
|
+
ytText: "Toistamalla videon hyv\xE4ksyt YouTuben ev\xE4steet.",
|
|
37
|
+
ytButton: "Toista video",
|
|
38
|
+
ytOpen: "Katso YouTubessa"
|
|
39
39
|
};
|
|
40
40
|
var sv = {
|
|
41
41
|
title: "Kakor",
|
|
@@ -71,9 +71,9 @@ var sv = {
|
|
|
71
71
|
mo: "m\xE5nad|m\xE5nader",
|
|
72
72
|
d: "dag|dagar",
|
|
73
73
|
min: "minut|minuter",
|
|
74
|
-
ytText: "
|
|
75
|
-
ytButton: "
|
|
76
|
-
ytOpen: "
|
|
74
|
+
ytText: "Genom att spela upp videon godk\xE4nner du kakor fr\xE5n YouTube.",
|
|
75
|
+
ytButton: "Spela upp video",
|
|
76
|
+
ytOpen: "Titta p\xE5 YouTube"
|
|
77
77
|
};
|
|
78
78
|
var en = {
|
|
79
79
|
title: "Cookies",
|
|
@@ -109,9 +109,9 @@ var en = {
|
|
|
109
109
|
mo: "month|months",
|
|
110
110
|
d: "day|days",
|
|
111
111
|
min: "minute|minutes",
|
|
112
|
-
ytText: "
|
|
113
|
-
ytButton: "
|
|
114
|
-
ytOpen: "
|
|
112
|
+
ytText: "By playing the video, you accept cookies from YouTube.",
|
|
113
|
+
ytButton: "Play video",
|
|
114
|
+
ytOpen: "Watch on YouTube"
|
|
115
115
|
};
|
|
116
116
|
var texts = { fi, sv, en };
|
|
117
117
|
|
|
@@ -232,6 +232,12 @@ function activeProviders(config) {
|
|
|
232
232
|
function categoriesOf(config) {
|
|
233
233
|
return ["analytics", "marketing"].filter((c) => activeProviders(config).some((p) => p.category === c));
|
|
234
234
|
}
|
|
235
|
+
function needsBanner(config) {
|
|
236
|
+
return activeProviders(config).some((p) => p.id !== "youtube");
|
|
237
|
+
}
|
|
238
|
+
function policyOf(config, locale) {
|
|
239
|
+
return typeof config.policyUrl === "string" ? config.policyUrl : config.policyUrl?.[locale];
|
|
240
|
+
}
|
|
235
241
|
function textsFor(config, locale) {
|
|
236
242
|
return { ...texts[locale], ...config.texts?.[locale] };
|
|
237
243
|
}
|
|
@@ -254,8 +260,10 @@ function runtimeData(config, locale) {
|
|
|
254
260
|
meta: list(config.meta),
|
|
255
261
|
ph: config.posthog?.key ? { key: config.posthog.key, host: posthogHost(config) } : void 0,
|
|
256
262
|
yt: !!config.youtube,
|
|
263
|
+
banner: needsBanner(config),
|
|
264
|
+
policy: policyOf(config, locale),
|
|
257
265
|
del: { analytics: del("analytics"), marketing: del("marketing") },
|
|
258
|
-
t: { ytText: t.ytText, ytButton: t.ytButton, ytOpen: t.ytOpen }
|
|
266
|
+
t: { ytText: t.ytText, ytButton: t.ytButton, ytOpen: t.ytOpen, more: t.more }
|
|
259
267
|
};
|
|
260
268
|
}
|
|
261
269
|
|
|
@@ -301,12 +309,16 @@ var css = [
|
|
|
301
309
|
":where(.keksi-cat table){width:100%;border-collapse:collapse;font-size:.8125rem;margin-top:.5rem}",
|
|
302
310
|
":where(.keksi-cat) :where(th,td){padding:.3rem .75rem .3rem 0;border-bottom:1px solid var(--_line);text-align:left;vertical-align:top}",
|
|
303
311
|
":where(.keksi-cat) :where(th){font-weight:600}",
|
|
304
|
-
":where(keksi-youtube){display:block;aspect-ratio:16/9;background:#
|
|
312
|
+
":where(keksi-youtube){position:relative;display:block;aspect-ratio:16/9;overflow:hidden;border-radius:12px;background:#0f0f0f;color:#fff;font-family:Roboto,Arial,sans-serif}",
|
|
305
313
|
":where(keksi-youtube iframe){display:block;width:100%;height:100%;border:0}",
|
|
306
|
-
":where(.keksi-yt){
|
|
307
|
-
":where(.keksi-yt) :
|
|
308
|
-
":where(.keksi-yt) :where(
|
|
309
|
-
":where(.keksi-yt
|
|
314
|
+
":where(keksi-youtube[label]:has(.keksi-yt))::before{content:attr(label);position:absolute;inset:0 0 auto;z-index:1;padding:1rem 1.25rem 2.5rem;font-size:1.125rem;font-weight:500;background:linear-gradient(#000b,#0000);pointer-events:none}",
|
|
315
|
+
":where(.keksi-yt){position:relative;display:grid;grid-template-rows:1fr auto auto;gap:.25rem;height:100%;padding:0 1.25rem 1rem;box-sizing:border-box;font-size:.875rem;line-height:1.45;background:linear-gradient(#0000 55%,#000c),center/cover var(--keksi-poster,none)}",
|
|
316
|
+
":where(.keksi-yt) :where(p){margin:0}",
|
|
317
|
+
":where(.keksi-yt) :where(p:first-of-type){grid-row:2}",
|
|
318
|
+
":where(.keksi-yt) :where(p:last-of-type){grid-row:3;color:#fffc}",
|
|
319
|
+
":where(.keksi-yt) :where(a){color:inherit;text-decoration:underline;text-underline-offset:2px}",
|
|
320
|
+
`:where(.keksi-yt button){position:absolute;inset:0;width:68px;height:48px;margin:auto;padding:0;border:0;font-size:0;color:transparent;background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 68 48'%3E%3Cpath d='M66.52 7.74c-.78-2.93-2.49-5.41-5.42-6.19C55.79.13 34 0 34 0S12.21.13 6.9 1.55c-2.93.78-4.63 3.26-5.42 6.19C.06 13.05 0 24 0 24s.06 10.95 1.48 16.26c.78 2.93 2.49 5.41 5.42 6.19C12.21 47.87 34 48 34 48s21.79-.13 27.1-1.55c2.93-.78 4.64-3.26 5.42-6.19C67.94 34.95 68 24 68 24s-.06-10.95-1.48-16.26z' fill='%23f00'/%3E%3Cpath d='M45 24 27 14v20' fill='%23fff'/%3E%3C/svg%3E") center/contain no-repeat;cursor:pointer;transition:transform .15s}`,
|
|
321
|
+
":where(.keksi-yt button:hover){transform:scale(1.08)}"
|
|
310
322
|
].join("");
|
|
311
323
|
|
|
312
324
|
// src/render.ts
|
|
@@ -316,12 +328,12 @@ function renderKeksi(config, { lang = "fi", nonce } = {}) {
|
|
|
316
328
|
if (!cats.length) return "";
|
|
317
329
|
const t = textsFor(config, lang);
|
|
318
330
|
const active = activeProviders(config);
|
|
319
|
-
const policy =
|
|
331
|
+
const policy = policyOf(config, lang);
|
|
320
332
|
const n = nonce ? ` nonce="${e(nonce)}"` : "";
|
|
321
333
|
const btn = (action, label, type = "button") => `<button type="${type}" class="keksi-btn" data-keksi="${action}">${e(label)}</button>`;
|
|
322
334
|
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("");
|
|
323
335
|
const json = JSON.stringify(runtimeData(config, lang)).replace(/</g, "\\u003c");
|
|
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
|
|
336
|
+
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 F=(n,s)=>new RegExp(`^${n.split("*").map(i=>i.replace(/[.+?^${}()|[\\]\\\\]/g,"\\\\$&")).join(".*")}$`).test(s);var _=["analytics","marketing"],v={analytics:!1,marketing:!1};function f(...n){return arguments}function H(n,s=window){if(typeof s.keksi?.sync=="function")return s.keksi.sync(),s.keksi;let i=s.document,g=e=>i.getElementById(e),c=I(),A=!1,w=!1,L=!1,E=new Set,x=f("consent","default",{ad_storage:"denied",ad_user_data:"denied",ad_personalization:"denied",analytics_storage:"denied"});if(n.ga.length||n.ads.length){let e=s.dataLayer??(s.dataLayer=[]);s.gtag=function(){e.push(arguments)},e.push(x)}if(n.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(n.ph){let e=Object.assign([],{_i:[],people:[]});for(let o of"capture identify alias reset register register_once unregister group setPersonProperties getFeatureFlag isFeatureEnabled onFeatureFlags reloadFeatureFlags get_distinct_id startSessionRecording stopSessionRecording captureException on".split(" "))e[o]=(...t)=>e.push([o,...t]);s.posthog=e}function I(){let e=i.cookie.split("; ").find(l=>l.startsWith(`${n.name}=`))?.slice(n.name.length+1),[o,,t,a]=e?.split(".")??[];if(o!==n.v||!t||!a)return null;let r={analytics:a.includes("a"),marketing:a.includes("m")};return{id:t,c:r,yt:r.marketing||a.includes("y"),done:!a.includes("u")}}function T(e,o=e.marketing,t=!0){let a=c?.id??Array.from(s.crypto.getRandomValues(new Uint8Array(8)),l=>l.toString(16).padStart(2,"0")).join(""),r=(t?"":"u")+(e.analytics?"a":"")+(e.marketing?"m":"")+(o&&!e.marketing?"y":"")||"-";return i.cookie=`${n.name}=${n.v}.${Date.now().toString(36)}.${a}.${r}; Path=/; Max-Age=${n.days*86400}; SameSite=Lax`+(n.domain?`; Domain=${n.domain}`:"")+(s.location.protocol==="https:"?"; Secure":""),{id:a,c:e,yt:o,done:t}}function $(e){let o=i.createElement("script");return o.async=!0,o.src=e,i.head.append(o),o}function C(e){let o=[...e.analytics?n.ga:[],...e.marketing?n.ads:[]].filter(t=>!E.has(t));if(o.length){let t=e.marketing?"granted":"denied",a=[f("consent","update",{analytics_storage:e.analytics?"granted":"denied",ad_storage:t,ad_user_data:t,ad_personalization:t})];E.size||a.push(f("js",new Date));for(let r of o)a.push(f("config",r)),E.add(r);if(A)for(let r of a)s.dataLayer.push(r);else{let r=s.dataLayer;r.splice(r.indexOf(x)+1,0,...a),$(`https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(o[0])}`),A=!0}}if(e.marketing&&n.meta.length&&!w&&(w=!0,s.fbq.queue.unshift(...n.meta.map(t=>f("init",t)),f("track","PageView")),$("https://connect.facebook.net/en_US/fbevents.js")),e.analytics&&n.ph&&!L&&(L=!0,s.posthog._i.push([n.ph.key,{api_host:n.ph.host,defaults:"2026-08-30"},"posthog"]),$(`${n.ph.host.replace(".i.posthog.com","-assets.i.posthog.com")}/static/array.js`).crossOrigin="anonymous"),c?.yt)for(let t of i.querySelectorAll("keksi-youtube"))t.show(!0)}function O(){c?.yt||(c=T(c?.c??v,!0,c?.done??!1)),C(c.c)}function R(e){let o=e.flatMap(l=>n.del[l]),t=l=>o.some(u=>F(u,l)),a=s.location.hostname.split("."),r=["",...a.map((l,u)=>`; Domain=${a.slice(u).join(".")}`)];for(let l of i.cookie.split("; ")){let u=l.split("=")[0];if(t(u))for(let b of r)i.cookie=`${u}=; Max-Age=0; Path=/${b}`}try{for(let l of[s.localStorage,s.sessionStorage])for(let u of Object.keys(l))t(u)&&l.removeItem(u)}catch{}}function p(e){let o=c;c=T(e),h(),i.dispatchEvent(new s.CustomEvent("keksi:change",{detail:{...e}})),R(_.filter(t=>!e[t])),_.some(t=>o?.c[t]&&!e[t])||o?.yt&&!c.yt?s.location.reload():C(e)}function y(e=!1,o=n.banner&&!c?.done||e){let t=g("keksi-banner"),a=g("keksi-settings");!t||!a||(t.hidden=!o,a.hidden=!e,t.firstElementChild.hidden=e)}function h(){y()}function M(){let e=g("keksi-settings");for(let o of _){let t=e?.querySelector(`[name=${o}]`);t&&(t.checked=!!c?.c[o])}y(!0),e?.querySelector(".keksi-close")?.focus()}i.addEventListener("click",e=>{let o=e.target.closest?.("[data-keksi],[data-keksi-open]");if(!o)return;if(o.hasAttribute("data-keksi-open"))return e.preventDefault(),M();let t=o.getAttribute("data-keksi");if(t==="close")y();else if(t==="accept")p({analytics:!0,marketing:!0});else if(t==="reject")p(v);else if(t==="save"){let a=r=>!!g("keksi-settings")?.querySelector(`[name=${r}]`)?.checked;p({analytics:a("analytics"),marketing:a("marketing")})}}),i.addEventListener("keydown",e=>{e.key==="Escape"&&g("keksi-settings")?.hidden===!1&&y()}),i.addEventListener("astro:after-swap",h);class D extends s.HTMLElement{connectedCallback(){this.show(!!c?.yt)}show(o,t=!1){let a=encodeURIComponent(this.getAttribute("video")??""),r=this.getAttribute("label");if(o){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 l=i.createElement("div");l.className="keksi-yt";let u=this.getAttribute("poster");u&&l.style.setProperty("--keksi-poster",`url(${JSON.stringify(u)})`);let b=i.createElement("p");b.textContent=n.t.ytText;let m=i.createElement("button");m.type="button",m.textContent=n.t.ytButton,r&&m.setAttribute("aria-label",`${n.t.ytButton}: ${r}`),m.onclick=()=>{this.show(!0,!0),O()};let S=i.createElement("p"),q=(d,N,P=!1)=>{let k=i.createElement("a");k.href=d,k.textContent=N,P&&(k.target="_blank",k.rel="noopener"),S.append(S.childNodes.length?" \\xB7 ":"",k)};q(`https://www.youtube.com/watch?v=${a}`,n.t.ytOpen,!0),n.policy&&q(n.policy,n.t.more),l.append(b,m,S),this.replaceChildren(l)}}n.yt&&!s.customElements.get("keksi-youtube")&&s.customElements.define("keksi-youtube",D);let j={get consent(){return c?.done?{...c.c}:null},open:M,save:e=>p({...c?.c??v,...e}),acceptAll:()=>p({analytics:!0,marketing:!0}),rejectAll:()=>p(v),sync:h};return s.keksi=j,h(),c&&C(c.c),j}H(JSON.parse(document.getElementById("keksi-data").textContent));})();'}</script>`;
|
|
325
337
|
}
|
|
326
338
|
function sections(config, lang, h, dialog) {
|
|
327
339
|
const t = textsFor(config, lang);
|
package/dist/types.d.ts
CHANGED
|
@@ -74,7 +74,10 @@ export interface RuntimeData {
|
|
|
74
74
|
host: string;
|
|
75
75
|
};
|
|
76
76
|
yt: boolean;
|
|
77
|
+
/** False with YouTube only: the settings exist but the banner doesn't show by itself. */
|
|
78
|
+
banner: boolean;
|
|
79
|
+
policy?: string;
|
|
77
80
|
/** First-party cookie and storage names per category, `*` = wildcard. */
|
|
78
81
|
del: Record<Category, string[]>;
|
|
79
|
-
t: Pick<Texts, 'ytText' | 'ytButton' | 'ytOpen'>;
|
|
82
|
+
t: Pick<Texts, 'ytText' | 'ytButton' | 'ytOpen' | 'more'>;
|
|
80
83
|
}
|