bitsnap-react 1.12.1 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -6
- package/dist/CartProvider-6D5WE2HA.js +2 -0
- package/dist/CartProvider-6D5WE2HA.js.map +1 -0
- package/dist/CartProvider-GLUQHAG3.mjs +2 -0
- package/dist/analytics.d.mts +2 -2
- package/dist/analytics.d.ts +2 -2
- package/dist/analytics.js +1 -1
- package/dist/analytics.js.map +1 -1
- package/dist/analytics.mjs +1 -1
- package/dist/catalog.d.mts +1 -1
- package/dist/catalog.d.ts +1 -1
- package/dist/catalog.js +1 -1
- package/dist/catalog.js.map +1 -1
- package/dist/catalog.mjs +1 -1
- package/dist/{chunk-HDLCLZOK.js → chunk-3N2VK426.js} +2 -2
- package/dist/chunk-3N2VK426.js.map +1 -0
- package/dist/{chunk-T2YSOHJM.mjs → chunk-3OEKRPWV.mjs} +2 -2
- package/dist/{chunk-T2YSOHJM.mjs.map → chunk-3OEKRPWV.mjs.map} +1 -1
- package/dist/{chunk-I6BRDRPB.js → chunk-5DDLYVBG.js} +2 -2
- package/dist/chunk-5DDLYVBG.js.map +1 -0
- package/dist/{chunk-4ZFIL5S7.js → chunk-7SYNGA3R.js} +2 -2
- package/dist/chunk-7SYNGA3R.js.map +1 -0
- package/dist/chunk-AWG5OAK7.js +2 -0
- package/dist/chunk-AWG5OAK7.js.map +1 -0
- package/dist/{chunk-G3VVAVG7.mjs → chunk-CYLZ5GYW.mjs} +2 -2
- package/dist/{chunk-STVEBT6M.js → chunk-FLDI3BPY.js} +2 -2
- package/dist/chunk-FLDI3BPY.js.map +1 -0
- package/dist/chunk-GKFET34B.mjs +2 -0
- package/dist/chunk-GKFET34B.mjs.map +1 -0
- package/dist/chunk-JSDGZ4NY.js.map +1 -1
- package/dist/{chunk-3ITTSNWG.js → chunk-JXVAT2ZE.js} +2 -2
- package/dist/chunk-JXVAT2ZE.js.map +1 -0
- package/dist/chunk-KOYBZORL.js.map +1 -1
- package/dist/chunk-MDCP5JNP.mjs +2 -0
- package/dist/chunk-MDCP5JNP.mjs.map +1 -0
- package/dist/{chunk-VF62KDNS.mjs → chunk-MVOJMCZG.mjs} +2 -2
- package/dist/{chunk-MZSLLGFS.js → chunk-PLS5RR27.js} +2 -2
- package/dist/chunk-PLS5RR27.js.map +1 -0
- package/dist/{chunk-VRL3IF5J.js → chunk-R2M6W4LD.js} +3 -3
- package/dist/chunk-R2M6W4LD.js.map +1 -0
- package/dist/{chunk-HBPSSGYN.mjs → chunk-SKFE6TUP.mjs} +2 -2
- package/dist/{chunk-GJNFDUJN.mjs → chunk-U5SAO2SH.mjs} +3 -3
- package/dist/chunk-U5SAO2SH.mjs.map +1 -0
- package/dist/{chunk-DRAKVCD7.mjs → chunk-VY7RNSFP.mjs} +2 -2
- package/dist/{chunk-VDLJW4JJ.mjs → chunk-VYYUTJZB.mjs} +2 -2
- package/dist/chunk-W3RGQYPD.js +2 -0
- package/dist/chunk-W3RGQYPD.js.map +1 -0
- package/dist/consent.js +4 -4
- package/dist/consent.js.map +1 -1
- package/dist/consent.mjs +4 -4
- package/dist/consent.mjs.map +1 -1
- package/dist/course.d.mts +2 -2
- package/dist/course.d.ts +2 -2
- package/dist/course.js +1 -1
- package/dist/course.js.map +1 -1
- package/dist/course.mjs +1 -1
- package/dist/embed/analytics.js +1 -1
- package/dist/embed/consent.js +4 -4
- package/dist/errors.js.map +1 -1
- package/dist/index.d.mts +20 -10
- package/dist/index.d.ts +20 -10
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/{instrument-CmxK7S4T.d.mts → instrument-BE8ktMUv.d.mts} +14 -0
- package/dist/{instrument-CmxK7S4T.d.ts → instrument-BE8ktMUv.d.ts} +14 -0
- package/dist/internal.js.map +1 -1
- package/dist/methods-N35A2OGE.mjs +2 -0
- package/dist/methods-WSICR2JH.js +2 -0
- package/dist/methods-WSICR2JH.js.map +1 -0
- package/dist/models.js.map +1 -1
- package/dist/returns.d.mts +1 -1
- package/dist/returns.d.ts +1 -1
- package/dist/returns.js +1 -1
- package/dist/returns.js.map +1 -1
- package/dist/returns.mjs +1 -1
- package/dist/{types-BnVWLcoB.d.mts → types-DP1vxo5E.d.mts} +1 -1
- package/dist/{types-i-IuN4Im.d.ts → types-u8VwmC3j.d.ts} +1 -1
- package/package.json +38 -13
- package/dist/CartProvider-WPJWECBS.mjs +0 -2
- package/dist/CartProvider-YPZ3HD7W.js +0 -2
- package/dist/CartProvider-YPZ3HD7W.js.map +0 -1
- package/dist/chunk-3ITTSNWG.js.map +0 -1
- package/dist/chunk-4BV5IUZH.js +0 -2
- package/dist/chunk-4BV5IUZH.js.map +0 -1
- package/dist/chunk-4ZFIL5S7.js.map +0 -1
- package/dist/chunk-ADYVTMGH.mjs +0 -2
- package/dist/chunk-ADYVTMGH.mjs.map +0 -1
- package/dist/chunk-GJNFDUJN.mjs.map +0 -1
- package/dist/chunk-HDLCLZOK.js.map +0 -1
- package/dist/chunk-I6BRDRPB.js.map +0 -1
- package/dist/chunk-MZSLLGFS.js.map +0 -1
- package/dist/chunk-STVEBT6M.js.map +0 -1
- package/dist/chunk-T3PAOMHI.mjs +0 -2
- package/dist/chunk-T3PAOMHI.mjs.map +0 -1
- package/dist/chunk-VRL3IF5J.js.map +0 -1
- package/dist/chunk-XIHGWNTS.js +0 -2
- package/dist/chunk-XIHGWNTS.js.map +0 -1
- package/dist/methods-FVDH6Y57.mjs +0 -2
- package/dist/methods-ISVX5JQ7.js +0 -2
- package/dist/methods-ISVX5JQ7.js.map +0 -1
- /package/dist/{CartProvider-WPJWECBS.mjs.map → CartProvider-GLUQHAG3.mjs.map} +0 -0
- /package/dist/{chunk-G3VVAVG7.mjs.map → chunk-CYLZ5GYW.mjs.map} +0 -0
- /package/dist/{chunk-VF62KDNS.mjs.map → chunk-MVOJMCZG.mjs.map} +0 -0
- /package/dist/{chunk-HBPSSGYN.mjs.map → chunk-SKFE6TUP.mjs.map} +0 -0
- /package/dist/{chunk-DRAKVCD7.mjs.map → chunk-VY7RNSFP.mjs.map} +0 -0
- /package/dist/{chunk-VDLJW4JJ.mjs.map → chunk-VYYUTJZB.mjs.map} +0 -0
- /package/dist/{methods-FVDH6Y57.mjs.map → methods-N35A2OGE.mjs.map} +0 -0
package/dist/consent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/emems/.t3/worktrees/bitsnap-react/cart-payment-method/dist/consent.js","../src/consent/react.tsx","../src/consent/core/types.ts","../src/consent/core/activate.ts","../src/consent/core/config.ts","../src/consent/core/styles.ts","../src/consent/core/ui.ts"],"names":["CONSENT_CATEGORIES","SCRIPT_SELECTOR","FRAME_SELECTOR","allowed","c","category","settled","script","resolve","run","doc","placeholder","name","value","external","loaded","activateScripts","activated","gate","loading","inline","frame","src","DEFAULT_API_HOST","DEFAULT_COLLECT_HOST","defaultConfig","THEMES","POSITIONS","HEX_COLOR","safeColor","safeUrl","url","cleanServices","services","item","cookie","mergeConfig","layers","result","layer","accent","privacy","fetchRemoteConfig","apiHost","key","timeoutMs","controller","timer","response"],"mappings":"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","file":"/home/emems/.t3/worktrees/bitsnap-react/cart-payment-method/dist/consent.js","sourcesContent":[null,"import { useCallback, useRef, useSyncExternalStore } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"../lib/isomorphic-layout-effect\";\nimport { createConsent } from \"./core/controller\";\nimport type {\n BitsnapConsentApi,\n ConsentChoices,\n ConsentConfig,\n ConsentOptions,\n ConsentState,\n} from \"./core/types\";\n\ndeclare global {\n interface Window {\n BitsnapConsent?: BitsnapConsentApi;\n }\n}\n\nexport type BitsnapConsentProps = {\n /** Public project key: loads the project's banner config and logs choices. */\n projectKey?: string;\n theme?: ConsentOptions[\"theme\"];\n locale?: ConsentOptions[\"locale\"];\n position?: ConsentOptions[\"position\"];\n privacyPolicyUrl?: string;\n /** `project` keeps the consent cookie per project key. */\n scope?: ConsentOptions[\"scope\"];\n apiHost?: string;\n collectHost?: string;\n /** Local config, merged over the project config. */\n config?: Partial<ConsentConfig>;\n reloadOnWithdraw?: boolean;\n /** Google Consent Mode v2: `auto` (default) is on when `gtag` or `dataLayer` already exist. */\n gcm?: ConsentOptions[\"gcm\"];\n /** Categories offered when no remote config is loaded. */\n categories?: ConsentOptions[\"categories\"];\n /** Dashboard preview: shows the banner from the local options only; touches no cookie. */\n preview?: boolean;\n /** Replaces the banner title. */\n title?: string;\n /** Replaces the banner description. */\n description?: string;\n};\n\n/** Mounts Bitsnap Consent on the page. Renders nothing itself (the banner lives in a Shadow DOM). */\nexport function BitsnapConsent(props: BitsnapConsentProps): null {\n const {\n projectKey, theme, locale, position, privacyPolicyUrl, scope, apiHost, collectHost, reloadOnWithdraw,\n gcm, preview, title, description,\n } = props;\n const configKey = JSON.stringify(props.config ?? null);\n const configRef = useRef(props.config);\n configRef.current = props.config;\n const categoriesKey = JSON.stringify(props.categories ?? null);\n const categoriesRef = useRef(props.categories);\n categoriesRef.current = props.categories;\n\n // A layout effect: the page's passive effects (first events, tracker) must find the API already there.\n useIsomorphicLayoutEffect(() => {\n // A banner from the script tag or another instance already runs on this page.\n if (window.BitsnapConsent) return;\n const api = createConsent({\n projectKey,\n theme,\n locale,\n position,\n privacyPolicyUrl,\n scope,\n apiHost,\n collectHost,\n reloadOnWithdraw,\n gcm,\n preview,\n title,\n description,\n categories: categoriesRef.current,\n config: configRef.current,\n });\n window.BitsnapConsent = api;\n return () => {\n api.destroy();\n if (window.BitsnapConsent === api) delete window.BitsnapConsent;\n };\n }, [\n projectKey, theme, locale, position, privacyPolicyUrl, scope, apiHost, collectHost, reloadOnWithdraw,\n gcm, preview, title, description, categoriesKey, configKey,\n ]);\n\n return null;\n}\n\nlet lastKey = \"null\";\nlet lastState: ConsentState | null = null;\n\nfunction getSnapshot(): ConsentState | null {\n const current = typeof window === \"undefined\" ? null : (window.BitsnapConsent?.get() ?? null);\n const key = JSON.stringify(current);\n if (key !== lastKey) {\n lastKey = key;\n lastState = current;\n }\n return lastState;\n}\n\nfunction subscribe(onChange: () => void): () => void {\n window.addEventListener(\"bitsnap:consent\", onChange);\n window.addEventListener(\"bitsnap:consent:ready\", onChange);\n return () => {\n window.removeEventListener(\"bitsnap:consent\", onChange);\n window.removeEventListener(\"bitsnap:consent:ready\", onChange);\n };\n}\n\nexport type UseConsent = {\n /** The stored choice, or `null` while there is none (or on the server). */\n consent: ConsentState | null;\n /** `true` once the visitor has made a choice that is still valid. */\n decided: boolean;\n open: () => void;\n accept: (categories?: Partial<ConsentChoices>) => void;\n acceptAll: () => void;\n rejectAll: () => void;\n reset: () => void;\n};\n\n/** Current consent state of the page, kept up to date as the visitor chooses. */\nexport function useConsent(): UseConsent {\n const consent = useSyncExternalStore(subscribe, getSnapshot, () => null);\n const open = useCallback(() => window.BitsnapConsent?.open(), []);\n const accept = useCallback((categories?: Partial<ConsentChoices>) => window.BitsnapConsent?.accept(categories), []);\n const acceptAll = useCallback(() => window.BitsnapConsent?.acceptAll(), []);\n const rejectAll = useCallback(() => window.BitsnapConsent?.rejectAll(), []);\n const reset = useCallback(() => window.BitsnapConsent?.reset(), []);\n return { consent, decided: consent !== null, open, accept, acceptAll, rejectAll, reset };\n}\n","export type ConsentCategory = \"analytics\" | \"marketing\" | \"preferences\";\n\nexport type ConsentChoices = Record<ConsentCategory, boolean>;\n\nexport type ConsentTheme = \"auto\" | \"light\" | \"dark\";\n\nexport type ConsentPosition = \"bottom\" | \"bottom-left\" | \"bottom-right\";\n\nexport type ConsentLocale = \"pl\" | \"en\";\n\nexport const CONSENT_CATEGORIES: readonly ConsentCategory[] = [\n \"analytics\",\n \"marketing\",\n \"preferences\",\n];\n\nexport const CONSENT_TEXT_KEYS = [\n \"title\",\n \"description\",\n \"acceptAll\",\n \"rejectAll\",\n \"settings\",\n \"save\",\n \"close\",\n \"necessaryTitle\",\n \"necessaryDescription\",\n \"analyticsTitle\",\n \"analyticsDescription\",\n \"marketingTitle\",\n \"marketingDescription\",\n \"preferencesTitle\",\n \"preferencesDescription\",\n \"poweredBy\",\n] as const;\n\nexport type ConsentTexts = Record<(typeof CONSENT_TEXT_KEYS)[number], string>;\n\nexport type ConsentService = {\n name: string;\n category: ConsentCategory;\n /** Cookies to delete (best effort) when the category is withdrawn. A trailing `*` matches a prefix. */\n cookies?: string[];\n};\n\nexport type ConsentConfig = {\n /** Banner version. A stored choice with a lower `bv` makes the banner ask again. */\n version: number;\n theme: ConsentTheme;\n accentColor?: string;\n position: ConsentPosition;\n privacyPolicyUrl?: string;\n categories: ConsentChoices;\n services: ConsentService[];\n texts?: Partial<Record<ConsentLocale, Partial<ConsentTexts>>>;\n poweredBy: true;\n};\n\n/** What the consent cookie holds, and what `get()` returns. */\nexport type ConsentState = {\n id: string;\n /** Unix seconds. */\n ts: number;\n /** Version of the banner the choice was made on. */\n bv: number;\n c: ConsentChoices;\n};\n\nexport type ConsentAction =\n | \"accept_all\"\n | \"reject_all\"\n | \"custom\"\n | \"withdraw\";\n\n/** `detail` of `bitsnap:consent` and the argument of `on('change')`. */\nexport type ConsentChangeDetail = ConsentState & {\n /** `reset` when the visitor's stored choice was cleared (`get()` is `null` then). */\n action: ConsentAction | \"reset\";\n};\n\nexport type ConsentOptions = {\n /** Public project key. Enables the remote config and the consent log. */\n projectKey?: string;\n theme?: ConsentTheme;\n locale?: \"auto\" | ConsentLocale;\n position?: ConsentPosition;\n privacyPolicyUrl?: string;\n /** `project` keeps one cookie per project key (`bitsnap_consent_<key>`). */\n scope?: \"global\" | \"project\";\n apiHost?: string;\n collectHost?: string;\n /** Google Consent Mode v2. `auto` is on when `gtag` or `dataLayer` already exist. */\n gcm?: \"auto\" | \"on\" | \"off\";\n /** Local overrides, merged over the defaults and the remote config. */\n config?: Partial<ConsentConfig>;\n /** Categories used when no remote config is loaded. */\n categories?: Partial<ConsentChoices>;\n reloadOnWithdraw?: boolean;\n /**\n * Dashboard preview: shows the banner at once from the local options, never\n * reads or writes the cookie, loads no remote config, sends no consent log,\n * and the buttons only close the banner.\n */\n preview?: boolean;\n /** Replaces the banner title (current locale, non-empty values only). */\n title?: string;\n /** Replaces the banner description (current locale, non-empty values only). */\n description?: string;\n};\n\nexport type BitsnapConsentApi = {\n /**\n * Whose choice this banner holds: `project` keeps one cookie per project key\n * (`bitsnap_consent_<key>`), `global` the site-wide `bitsnap_consent`.\n */\n readonly scope: \"global\" | \"project\";\n /** The bare public project key the banner was started with, if any (`pk_` stripped). */\n readonly projectKey: string | null;\n get(): ConsentState | null;\n /**\n * `true` when the visitor refused `category`, also in a choice made on an older banner\n * version (which `get()` no longer returns) that they have not answered again yet.\n */\n denied(category: ConsentCategory): boolean;\n on(event: \"change\", fn: (detail: ConsentChangeDetail) => void): () => void;\n open(): void;\n close(): void;\n accept(categories?: Partial<ConsentChoices>): void;\n acceptAll(): void;\n rejectAll(): void;\n reset(): void;\n /** Resolves with the stored choice (or `null`) once config and stored choice are applied. */\n ready: Promise<ConsentState | null>;\n destroy(): void;\n};\n","import { CONSENT_CATEGORIES, type ConsentChoices } from \"./types\";\n\nconst SCRIPT_SELECTOR = 'script[type=\"text/plain\"][data-bitsnap-consent]:not([data-bitsnap-queued])';\nconst FRAME_SELECTOR = \"iframe[data-bitsnap-consent][data-src]\";\n\nconst allowed = (c: ConsentChoices, category: string | null): boolean =>\n (CONSENT_CATEGORIES as readonly string[]).includes(category ?? \"\") &&\n c[category as keyof ConsentChoices];\n\n/** Settles when the script has loaded or failed: a broken script must not block the ones after it. */\nfunction settled(script: HTMLScriptElement): Promise<void> {\n return new Promise((resolve) => {\n script.addEventListener(\"load\", () => resolve(), { once: true });\n script.addEventListener(\"error\", () => resolve(), { once: true });\n });\n}\n\n/** Swaps the placeholder for a live script; resolves once an external one has loaded. */\nfunction run(doc: Document, placeholder: HTMLScriptElement): Promise<void> | undefined {\n const script = doc.createElement(\"script\");\n for (const { name, value } of Array.from(placeholder.attributes)) {\n if (name === \"type\" || name === \"data-bitsnap-queued\") continue;\n if (name === \"data-type\") {\n // The embed writes the original `type=\"module\"` here, as `type` itself is `text/plain`.\n if (value === \"module\") script.setAttribute(\"type\", \"module\");\n } else if (name === \"data-src\") script.setAttribute(\"src\", value);\n else script.setAttribute(name, value);\n }\n if (placeholder.nonce) script.nonce = placeholder.nonce;\n const external = script.hasAttribute(\"src\");\n if (external) {\n // Dynamic scripts are async by default; keep the order of the page.\n if (!placeholder.hasAttribute(\"async\")) script.async = false;\n } else {\n script.text = placeholder.text;\n }\n const loaded = external ? settled(script) : undefined;\n placeholder.parentNode?.replaceChild(script, placeholder);\n return loaded;\n}\n\n/**\n * Turns the consented placeholders into live elements:\n * `<script type=\"text/plain\" data-bitsnap-consent=\"marketing\" [data-src] [data-type=\"module\"]>` becomes\n * a real script (attributes copied, `type` dropped or restored from `data-type`, `data-src` -> `src`),\n * and `<iframe data-src>` gets its `src`.\n * Placeholders run in document order: an inline script waits for the external scripts before it\n * (`<script src=lib.js>` then `<script>Lib.init()</script>`), external ones only follow each other.\n */\nexport function activateScripts(doc: Document, c: ConsentChoices): number {\n let activated = 0;\n try {\n /** Set once a placeholder had to wait: everything after it queues behind it. */\n let gate: Promise<void> | null = null;\n /** External scripts inserted since the last inline one. */\n let loading: Promise<void>[] = [];\n doc.querySelectorAll<HTMLScriptElement>(SCRIPT_SELECTOR).forEach((placeholder) => {\n if (!allowed(c, placeholder.getAttribute(\"data-bitsnap-consent\"))) return;\n activated++;\n const inline = !placeholder.hasAttribute(\"data-src\") && !placeholder.hasAttribute(\"src\");\n if (gate || (inline && loading.length > 0)) {\n // Marked so that another call (a later choice) does not queue it again.\n placeholder.setAttribute(\"data-bitsnap-queued\", \"\");\n gate = (gate ?? Promise.all(loading).then(() => {})).then(() => {\n try {\n return run(doc, placeholder);\n } catch {\n // never throw into the host page\n }\n });\n loading = [];\n return;\n }\n const loaded = run(doc, placeholder);\n if (loaded) loading.push(loaded);\n });\n doc.querySelectorAll<HTMLIFrameElement>(FRAME_SELECTOR).forEach((frame) => {\n if (!allowed(c, frame.getAttribute(\"data-bitsnap-consent\"))) return;\n const src = frame.getAttribute(\"data-src\");\n if (src && frame.getAttribute(\"src\") !== src) {\n frame.setAttribute(\"src\", src);\n frame.removeAttribute(\"data-src\");\n activated++;\n }\n });\n } catch {\n // never throw into the host page\n }\n return activated;\n}\n","import {\n CONSENT_CATEGORIES,\n type ConsentChoices,\n type ConsentConfig,\n type ConsentService,\n} from \"./types\";\n\nexport const DEFAULT_API_HOST = \"https://bitsnap.pl\";\nexport const DEFAULT_COLLECT_HOST = \"https://collect.bitsnap.pl\";\n\nexport function defaultConfig(): ConsentConfig {\n return {\n version: 1,\n theme: \"auto\",\n position: \"bottom\",\n categories: { analytics: true, marketing: true, preferences: false },\n services: [],\n poweredBy: true,\n };\n}\n\nconst THEMES = [\"auto\", \"light\", \"dark\"];\nconst POSITIONS = [\"bottom\", \"bottom-left\", \"bottom-right\"];\n\nconst HEX_COLOR = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;\n\nexport function safeColor(value: unknown): string | undefined {\n return typeof value === \"string\" && HEX_COLOR.test(value.trim())\n ? value.trim()\n : undefined;\n}\n\n/** Only http(s) and relative URLs may end up in the banner. */\nexport function safeUrl(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const url = value.trim();\n return /^(?:https?:\\/\\/|\\/(?!\\/)|#|\\?)/i.test(url) ? url : undefined;\n}\n\nfunction cleanServices(value: unknown): ConsentService[] {\n if (!Array.isArray(value)) return [];\n const services: ConsentService[] = [];\n for (const item of value) {\n if (\n item &&\n typeof item.name === \"string\" &&\n (CONSENT_CATEGORIES as readonly string[]).includes(item.category)\n ) {\n services.push({\n name: item.name,\n category: item.category,\n cookies: Array.isArray(item.cookies)\n ? item.cookies.filter((cookie: unknown) => typeof cookie === \"string\")\n : undefined,\n });\n }\n }\n return services;\n}\n\n/** Layers partial configs (later wins) over the defaults, dropping invalid values. */\nexport function mergeConfig(\n ...layers: (Partial<ConsentConfig> | null | undefined)[]\n): ConsentConfig {\n const result = defaultConfig();\n for (const layer of layers) {\n if (!layer || typeof layer !== \"object\") continue;\n if (typeof layer.version === \"number\" && layer.version >= 0) {\n result.version = Math.floor(layer.version);\n }\n if (THEMES.includes(layer.theme as string)) result.theme = layer.theme!;\n if (POSITIONS.includes(layer.position as string)) result.position = layer.position!;\n const accent = safeColor(layer.accentColor);\n if (accent) result.accentColor = accent;\n const privacy = safeUrl(layer.privacyPolicyUrl);\n if (privacy) result.privacyPolicyUrl = privacy;\n if (layer.categories && typeof layer.categories === \"object\") {\n for (const category of CONSENT_CATEGORIES) {\n if (typeof layer.categories[category] === \"boolean\") {\n result.categories[category] = layer.categories[category];\n }\n }\n }\n if (layer.services !== undefined) result.services = cleanServices(layer.services);\n if (layer.texts && typeof layer.texts === \"object\") result.texts = layer.texts;\n }\n return result;\n}\n\nexport function parseCategoryList(value: string | null | undefined): ConsentChoices {\n const wanted = (value ?? \"\").split(\",\").map((item) => item.trim());\n return {\n analytics: wanted.includes(\"analytics\"),\n marketing: wanted.includes(\"marketing\"),\n preferences: wanted.includes(\"preferences\"),\n };\n}\n\n/** What `GET /api/consent/config` answers: whether the banner is on, and its config. */\nexport type RemoteConsentConfig = {\n /** `false` when the project turned the banner off: nothing is shown, stored choices still apply. */\n enabled: boolean;\n config: Partial<ConsentConfig> | null;\n};\n\n/** Loads the project's banner config. Resolves to `null` on any failure or after the timeout. */\nexport async function fetchRemoteConfig(\n apiHost: string,\n key: string,\n timeoutMs = 3000,\n): Promise<RemoteConsentConfig | null> {\n if (typeof fetch !== \"function\") return null;\n const controller = typeof AbortController === \"function\" ? new AbortController() : undefined;\n const timer = setTimeout(() => controller?.abort(), timeoutMs);\n try {\n const response = await fetch(\n `${apiHost.replace(/\\/+$/, \"\")}/api/consent/config?key=${encodeURIComponent(key)}`,\n { signal: controller?.signal, credentials: \"omit\" },\n );\n if (!response.ok) return null;\n const body = await response.json();\n if (!body || typeof body !== \"object\") return null;\n const config = body.config && typeof body.config === \"object\" ? body.config : null;\n const enabled = body.enabled !== false;\n return config || !enabled ? { enabled, config } : null;\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n}\n","/** Colors of both themes. Every text/background pair is checked against WCAG AA in the tests. */\nexport const PALETTE = {\n light: {\n bg: \"#ffffff\",\n fg: \"#14171c\",\n muted: \"#495260\",\n border: \"#c9ced6\",\n off: \"#6b7280\",\n thumb: \"#ffffff\",\n accent: \"#1d4ed8\",\n onAccent: \"#ffffff\",\n link: \"#1d4ed8\",\n },\n dark: {\n bg: \"#171a20\",\n fg: \"#f1f3f6\",\n muted: \"#b7beca\",\n border: \"#3a404b\",\n off: \"#8b93a1\",\n thumb: \"#171a20\",\n accent: \"#8ab4ff\",\n onAccent: \"#0b1220\",\n link: \"#9dc0ff\",\n },\n} as const;\n\nconst vars = (p: (typeof PALETTE)[\"light\"]) =>\n `--bg:${p.bg};--fg:${p.fg};--muted:${p.muted};--border:${p.border};--off:${p.off};--thumb:${p.thumb};--accent:${p.accent};--on-accent:${p.onAccent};--link:${p.link};--focus:${p.accent}`;\n\nexport const STYLES = `\n:host{all:initial}\n*{box-sizing:border-box}\n[hidden]{display:none!important}\n.root{${vars(PALETTE.light)};position:fixed;inset:0;pointer-events:none;z-index:2147483000;font:14px/1.5 system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;color:var(--fg);-webkit-text-size-adjust:100%}\n.root[data-theme=dark]{${vars(PALETTE.dark)};color-scheme:dark}\nbutton,input{font:inherit;color:inherit}\n.banner,.overlay{pointer-events:auto}\n@supports (color:color-mix(in srgb,red 50%,blue)){::selection{background:color-mix(in srgb,var(--accent) 24%,transparent)}}\n.banner{position:absolute;background:var(--bg);color:var(--fg);max-height:92vh;overflow:auto;outline:none;animation:in .22s ease-out}\n.bottom{left:0;right:0;bottom:0;border-top:1px solid var(--border);box-shadow:0 -8px 28px rgba(15,23,42,.08);padding:20px 24px calc(20px + env(safe-area-inset-bottom,0px))}\n.root[data-theme=dark] .bottom{box-shadow:0 -8px 28px rgba(0,0,0,.4)}\n.bottom .inner{display:grid;grid-template-columns:minmax(min(100%,42ch),1fr) auto;grid-template-areas:\"text actions\" \"foot actions\";gap:4px 40px;align-items:center;max-width:1120px;margin:0 auto}\n.bottom .text{max-width:68ch}\n.bottom .btn,.bottom .link-btn{flex:0 0 auto}\n.card{bottom:16px;width:min(400px,calc(100vw - 32px));border:1px solid var(--border);border-radius:14px;padding:20px;box-shadow:0 12px 32px rgba(15,23,42,.14)}\n.root[data-theme=dark] .card{box-shadow:0 12px 32px rgba(0,0,0,.45)}\n.bottom-left{left:16px}\n.bottom-right{right:16px}\n.card .inner{display:flex;flex-direction:column;gap:16px}\n.text{grid-area:text}\n.actions{grid-area:actions;display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:flex-end}\n.card .actions{justify-content:stretch}\n.foot{grid-area:foot}\n.title{overflow-wrap:anywhere;font-size:17px;font-weight:650;line-height:1.35;letter-spacing:-.01em;text-wrap:balance;margin:0 0 6px}\n.desc{overflow-wrap:anywhere;margin:0;color:var(--muted);line-height:1.55;text-wrap:pretty}\n.desc a{color:var(--link);text-decoration:underline;text-underline-offset:3px}\n.btn,.link-btn{min-height:38px;padding:0 16px;border-radius:8px;font-size:13.5px;font-weight:600;cursor:pointer;white-space:nowrap;transition:background-color .12s ease-out,filter .12s ease-out,border-color .12s ease-out}\n.btn{border:2px solid var(--accent);background:var(--accent);color:var(--on-accent);flex:1 1 150px}\n.btn:hover{filter:brightness(.93)}\n.btn:active{filter:brightness(.87)}\n.link-btn{border:1px solid var(--off);background:transparent;color:var(--fg);text-decoration:none}\n.link-btn:hover{border-color:var(--fg)}\n@supports (color:color-mix(in srgb,red 50%,blue)){.link-btn:hover{background:color-mix(in srgb,var(--fg) 9%,transparent)}.link-btn:active{background:color-mix(in srgb,var(--fg) 14%,transparent)}}\n.card .link-btn{flex:1 1 100%}\nbutton:focus-visible,a:focus-visible,.dialog:focus-visible{outline:2px solid var(--focus);outline-offset:2px}\n.foot{font-size:12px;color:var(--muted);padding-top:8px}\n.foot a{color:var(--muted);text-decoration:none;display:inline-flex;align-items:center;gap:5px;vertical-align:middle}\n.foot a:hover{text-decoration:underline}\n.foot b{color:var(--fg);font-weight:700;letter-spacing:-.01em}\n.foot svg{display:block}\n.overlay{position:absolute;inset:0;background:rgba(10,12,16,.58);display:flex;align-items:center;justify-content:center;padding:16px;animation:fade .2s ease-out}\n.dialog{background:var(--bg);color:var(--fg);width:100%;max-width:560px;max-height:min(92vh,760px);display:flex;flex-direction:column;border-radius:16px;box-shadow:0 16px 48px rgba(0,0,0,.35);outline:none;animation:in .22s ease-out}\n.d-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:16px 8px 8px 24px}\n.d-head .title{margin:0;font-size:18px}\n.x{width:44px;height:44px;border:0;border-radius:10px;background:none;cursor:pointer;display:flex;align-items:center;justify-content:center;flex:none}\n.x:hover{background:var(--border)}\n.d-body{overflow:auto;padding:0 24px 8px;flex:1}\n.cat{border-top:1px solid var(--border);padding:10px 0 14px}\n.row{display:flex;align-items:center;justify-content:space-between;gap:12px}\n.cat-title{font-weight:600;font-size:15px}\n.always{font-size:12px;color:var(--muted);margin-right:4px}\n.cat p{margin:0;color:var(--muted)}\n.cat .svc{margin-top:6px;font-size:13px}\n.cat .svc b{font-weight:600;color:var(--fg)}\n.switch{position:relative;width:52px;height:44px;display:flex;align-items:center;justify-content:flex-end;flex:none}\n.switch input{position:absolute;inset:0;width:100%;height:100%;margin:0;opacity:0;cursor:pointer}\n.switch input:disabled{cursor:default}\n.track{width:44px;height:24px;border-radius:12px;background:var(--off);position:relative;transition:background .15s}\n.track::after{content:\"\";position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;background:var(--thumb);transition:transform .15s}\ninput:checked+.track{background:var(--accent)}\ninput:checked+.track::after{transform:translateX(20px);background:var(--on-accent)}\ninput:disabled+.track{opacity:.6}\ninput:focus-visible+.track{outline:2px solid var(--focus);outline-offset:2px}\n.d-foot{display:flex;flex-direction:column;gap:8px;padding:12px 24px 16px;border-top:1px solid var(--border)}\n.d-foot .actions{justify-content:stretch}\n@keyframes in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}\n@keyframes fade{from{opacity:0}to{opacity:1}}\n@media(max-width:960px){\n.bottom .inner{grid-template-columns:minmax(0,1fr);grid-template-areas:\"text\" \"actions\" \"foot\";gap:14px}\n.bottom .actions{justify-content:flex-start}\n}\n@media(max-width:640px){\n.bottom{padding:16px 16px calc(16px + env(safe-area-inset-bottom,0px))}\n.bottom .inner{gap:12px}\n.bottom .actions{justify-content:stretch}\n.bottom .btn{flex:1 1 130px}\n.bottom .link-btn{flex:1 1 100%}\n.card{left:8px;right:8px;bottom:8px;width:auto}\n.overlay{align-items:flex-end;padding:0}\n.dialog{max-width:none;border-radius:16px 16px 0 0;max-height:92vh}\n.d-head{padding-left:16px}\n.d-body{padding:0 16px 8px}\n.d-foot{padding:12px 16px calc(16px + env(safe-area-inset-bottom,0px))}\n.btn{flex-basis:130px}\n.btn,.link-btn{padding:0 12px;min-height:40px}\n}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important}}\n`.replace(/\\n/g, \"\");\n","import { contrastRatio, onColor } from \"./contrast\";\nimport { UI_LABELS } from \"./texts\";\nimport { PALETTE, STYLES } from \"./styles\";\nimport {\n CONSENT_CATEGORIES,\n type ConsentCategory,\n type ConsentChoices,\n type ConsentConfig,\n type ConsentLocale,\n type ConsentTexts,\n} from \"./types\";\n\nexport type UIOptions = {\n doc: Document;\n config: ConsentConfig;\n texts: ConsentTexts;\n locale: ConsentLocale;\n /** Current choice, used to preset the toggles. */\n getChoices(): ConsentChoices | null;\n /** Whether a valid choice exists, i.e. closing the settings must not bring back the banner. */\n hasChoice(): boolean;\n onAcceptAll(): void;\n onRejectAll(): void;\n onSave(choices: ConsentChoices): void;\n};\n\nexport type ConsentUI = {\n /** `focus` moves keyboard focus into the banner; leave it off when the banner opens on page load. */\n showBanner(focus?: boolean): void;\n showSettings(): void;\n hide(): void;\n destroy(): void;\n};\n\nconst WORDMARK =\n '<svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" aria-hidden=\"true\"><rect width=\"16\" height=\"16\" rx=\"4\" fill=\"currentColor\" style=\"color:var(--fg)\"/><circle cx=\"8\" cy=\"8\" r=\"3\" fill=\"var(--bg)\"/></svg>';\n\nconst CLOSE_ICON =\n '<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"M3 3l10 10M13 3L3 13\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" fill=\"none\"/></svg>';\n\nconst FOCUSABLE = \"button:not([disabled]),input:not([disabled]),a[href]\";\n\nexport function createUI(options: UIOptions): ConsentUI {\n const { doc, config, texts, locale } = options;\n const labels = UI_LABELS[locale];\n let host: HTMLElement | null = null;\n let shadow: ShadowRoot | null = null;\n let root: HTMLElement;\n let banner: HTMLElement;\n let overlay: HTMLElement;\n let dialog: HTMLElement;\n let settingsButton: HTMLElement;\n let layer: \"none\" | \"banner\" | \"settings\" = \"none\";\n let opener: HTMLElement | null = null;\n let media: MediaQueryList | null = null;\n const toggles = new Map<ConsentCategory, HTMLInputElement>();\n\n function el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props: Record<string, string> = {},\n ...children: (Node | string)[]\n ): HTMLElementTagNameMap[K] {\n const node = doc.createElement(tag);\n for (const key of Object.keys(props)) {\n if (key === \"class\") node.className = props[key];\n else node.setAttribute(key, props[key]);\n }\n for (const child of children) node.append(child);\n return node;\n }\n\n function button(cls: string, text: string, onClick: () => void) {\n const node = el(\"button\", { type: \"button\", class: cls }, text);\n node.addEventListener(\"click\", onClick);\n return node;\n }\n\n function powered(): HTMLElement {\n const link = el(\"a\", {\n href: `https://bitsnap.pl/?utm_source=consent_banner&utm_medium=referral&utm_content=${encodeURIComponent(doc.location?.hostname ?? \"\")}`,\n target: \"_blank\",\n rel: \"noopener\",\n });\n link.append(texts.poweredBy + \" \");\n link.insertAdjacentHTML(\"beforeend\", WORDMARK);\n link.append(el(\"b\", {}, \"bitsnap\"));\n return el(\"div\", { class: \"foot\" }, link);\n }\n\n function description(): HTMLElement {\n const node = el(\"p\", { class: \"desc\", id: \"d\" }, texts.description);\n if (config.privacyPolicyUrl) {\n node.append(\n \" \",\n el(\n \"a\",\n { href: config.privacyPolicyUrl, target: \"_blank\", rel: \"noopener\" },\n labels.privacy,\n ),\n );\n }\n return node;\n }\n\n function buildBanner() {\n settingsButton = button(\"link-btn\", texts.settings, () => showSettings());\n banner = el(\n \"div\",\n {\n class: `banner ${config.position === \"bottom\" ? \"bottom\" : `card ${config.position}`}`,\n // Not modal: the page stays usable, so it is a live region, not a dialog.\n role: \"region\",\n \"aria-live\": \"polite\",\n \"aria-labelledby\": \"t\",\n \"aria-describedby\": \"d\",\n tabindex: \"-1\",\n },\n el(\n \"div\",\n { class: \"inner\" },\n el(\"div\", { class: \"text\" }, el(\"div\", { class: \"title\", id: \"t\" }, texts.title), description()),\n el(\n \"div\",\n { class: \"actions\" },\n button(\"btn\", texts.acceptAll, options.onAcceptAll),\n button(\"btn\", texts.rejectAll, options.onRejectAll),\n settingsButton,\n ),\n powered(),\n ),\n );\n }\n\n function category(id: ConsentCategory | \"necessary\"): HTMLElement {\n const services = config.services.filter((service) => service.category === id);\n const input = el(\"input\", { type: \"checkbox\", role: \"switch\", \"aria-labelledby\": `l-${id}`, \"aria-describedby\": `p-${id}` });\n if (id === \"necessary\") {\n input.checked = true;\n input.disabled = true;\n } else {\n toggles.set(id, input);\n }\n const node = el(\n \"div\",\n { class: \"cat\" },\n el(\n \"div\",\n { class: \"row\" },\n el(\"span\", { class: \"cat-title\", id: `l-${id}` }, texts[`${id}Title` as keyof ConsentTexts]),\n el(\n \"span\",\n { style: \"display:flex;align-items:center\" },\n ...(id === \"necessary\" ? [el(\"span\", { class: \"always\" }, labels.always)] : []),\n el(\"label\", { class: \"switch\" }, input, el(\"span\", { class: \"track\" })),\n ),\n ),\n el(\"p\", { id: `p-${id}` }, texts[`${id}Description` as keyof ConsentTexts]),\n );\n if (services.length > 0) {\n node.append(\n el(\"p\", { class: \"svc\" }, el(\"b\", {}, `${labels.services}: `), services.map((s) => s.name).join(\", \")),\n );\n }\n return node;\n }\n\n function buildSettings() {\n const close = el(\"button\", { type: \"button\", class: \"x\", \"aria-label\": texts.close });\n close.insertAdjacentHTML(\"beforeend\", CLOSE_ICON);\n close.addEventListener(\"click\", () => closeSettings());\n const body = el(\"div\", { class: \"d-body\" }, category(\"necessary\"));\n for (const id of CONSENT_CATEGORIES) {\n if (config.categories[id]) body.append(category(id));\n }\n dialog = el(\n \"div\",\n { class: \"dialog\", role: \"dialog\", \"aria-modal\": \"true\", \"aria-labelledby\": \"st\", tabindex: \"-1\" },\n el(\"div\", { class: \"d-head\" }, el(\"div\", { class: \"title\", id: \"st\" }, texts.settings), close),\n body,\n el(\n \"div\",\n { class: \"d-foot\" },\n el(\n \"div\",\n { class: \"actions\" },\n button(\"btn\", texts.save, () => options.onSave(readToggles())),\n button(\"btn\", texts.acceptAll, options.onAcceptAll),\n button(\"btn\", texts.rejectAll, options.onRejectAll),\n ),\n powered(),\n ),\n );\n overlay = el(\"div\", { class: \"overlay\" }, dialog);\n overlay.addEventListener(\"mousedown\", (event) => {\n if (event.target === overlay) closeSettings();\n });\n }\n\n function readToggles(): ConsentChoices {\n const choices = { analytics: false, marketing: false, preferences: false };\n toggles.forEach((input, key) => (choices[key] = input.checked));\n return choices;\n }\n\n function applyTheme() {\n const dark = config.theme === \"dark\" || (config.theme === \"auto\" && !!media?.matches);\n root.setAttribute(\"data-theme\", dark ? \"dark\" : \"light\");\n applyAccent(dark);\n }\n\n /** A custom accent that is too faint on the dark background gives way to the dark theme's own. */\n function applyAccent(dark: boolean) {\n const accent = config.accentColor;\n if (!accent) return;\n if (dark && contrastRatio(accent, PALETTE.dark.bg) < 3) {\n for (const name of [\"--accent\", \"--focus\", \"--on-accent\"]) root.style.removeProperty(name);\n return;\n }\n root.style.setProperty(\"--accent\", accent);\n root.style.setProperty(\"--focus\", accent);\n root.style.setProperty(\"--on-accent\", onColor(accent));\n }\n\n function ensure() {\n if (host) return;\n host = el(\"div\", { id: \"bitsnap-consent\", \"data-bitsnap-consent-host\": \"\" });\n shadow = host.attachShadow({ mode: \"open\" });\n shadow.append(el(\"style\", {}, STYLES));\n root = el(\"div\", { class: \"root\", lang: locale });\n buildBanner();\n buildSettings();\n banner.hidden = true;\n overlay.hidden = true;\n root.append(banner, overlay);\n shadow.append(root);\n if (typeof doc.defaultView?.matchMedia === \"function\") {\n media = doc.defaultView.matchMedia(\"(prefers-color-scheme: dark)\");\n media.addEventListener?.(\"change\", applyTheme);\n }\n applyTheme();\n doc.body.append(host);\n }\n\n function focusables(): HTMLElement[] {\n return Array.from(dialog.querySelectorAll<HTMLElement>(FOCUSABLE));\n }\n\n function onKeydown(event: KeyboardEvent) {\n if (layer !== \"settings\") return;\n if (event.key === \"Escape\") {\n event.preventDefault();\n closeSettings();\n return;\n }\n if (event.key !== \"Tab\") return;\n const items = focusables();\n if (items.length === 0) return;\n const active = shadow?.activeElement as HTMLElement | null;\n const first = items[0];\n const last = items[items.length - 1];\n const outside = !active || !dialog.contains(active);\n if (event.shiftKey && (outside || active === first || active === dialog)) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && (outside || active === last)) {\n event.preventDefault();\n first.focus();\n }\n }\n\n function showBanner(focus = false) {\n try {\n ensure();\n layer = \"banner\";\n doc.removeEventListener(\"keydown\", onKeydown, true);\n overlay.hidden = true;\n banner.hidden = false;\n if (focus) banner.focus({ preventScroll: true });\n } catch {\n // never throw into the host page\n }\n }\n\n function showSettings() {\n try {\n ensure();\n if (layer !== \"settings\") {\n const active = doc.activeElement as HTMLElement | null;\n opener = active && active !== host && active !== doc.body ? active : null;\n }\n const current = options.getChoices();\n toggles.forEach((input, key) => (input.checked = !!current?.[key]));\n layer = \"settings\";\n banner.hidden = true;\n overlay.hidden = false;\n doc.addEventListener(\"keydown\", onKeydown, true);\n dialog.focus({ preventScroll: true });\n } catch {\n // never throw into the host page\n }\n }\n\n function closeSettings() {\n if (layer !== \"settings\") return;\n doc.removeEventListener(\"keydown\", onKeydown, true);\n overlay.hidden = true;\n if (options.hasChoice()) {\n layer = \"none\";\n opener?.focus?.({ preventScroll: true });\n } else {\n layer = \"banner\";\n banner.hidden = false;\n settingsButton.focus({ preventScroll: true });\n }\n }\n\n function hide() {\n if (!host) return;\n doc.removeEventListener(\"keydown\", onKeydown, true);\n const wasSettings = layer === \"settings\";\n layer = \"none\";\n banner.hidden = true;\n overlay.hidden = true;\n if (wasSettings) opener?.focus?.({ preventScroll: true });\n }\n\n function destroy() {\n doc.removeEventListener(\"keydown\", onKeydown, true);\n media?.removeEventListener?.(\"change\", applyTheme);\n host?.remove();\n host = null;\n shadow = null;\n layer = \"none\";\n }\n\n return { showBanner, showSettings, hide, destroy };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/emems/.t3/worktrees/bitsnap-react/consent-shown/dist/consent.js","../src/consent/react.tsx","../src/consent/core/types.ts","../src/consent/core/activate.ts","../src/consent/core/config.ts","../src/consent/core/styles.ts","../src/consent/core/ui.ts"],"names":["CONSENT_CATEGORIES","SCRIPT_SELECTOR","FRAME_SELECTOR","allowed","c","category","settled","script","resolve","run","doc","placeholder","name","value","external","loaded","activateScripts","activated","gate","loading","inline","frame","src","DEFAULT_API_HOST","DEFAULT_COLLECT_HOST","defaultConfig","THEMES","POSITIONS","HEX_COLOR","safeColor","safeUrl","url","cleanServices","services","item","cookie","mergeConfig","layers","result","layer","accent","privacy","fetchRemoteConfig","apiHost","key","timeoutMs","controller","timer","response"],"mappings":"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","file":"/home/emems/.t3/worktrees/bitsnap-react/consent-shown/dist/consent.js","sourcesContent":[null,"import { useCallback, useRef, useSyncExternalStore } from \"react\";\nimport { useIsomorphicLayoutEffect } from \"../lib/isomorphic-layout-effect\";\nimport { createConsent } from \"./core/controller\";\nimport type {\n BitsnapConsentApi,\n ConsentChoices,\n ConsentConfig,\n ConsentOptions,\n ConsentState,\n} from \"./core/types\";\n\ndeclare global {\n interface Window {\n BitsnapConsent?: BitsnapConsentApi;\n }\n}\n\nexport type BitsnapConsentProps = {\n /** Public project key: loads the project's banner config and logs choices. */\n projectKey?: string;\n theme?: ConsentOptions[\"theme\"];\n locale?: ConsentOptions[\"locale\"];\n position?: ConsentOptions[\"position\"];\n privacyPolicyUrl?: string;\n /** `project` keeps the consent cookie per project key. */\n scope?: ConsentOptions[\"scope\"];\n apiHost?: string;\n collectHost?: string;\n /** Local config, merged over the project config. */\n config?: Partial<ConsentConfig>;\n reloadOnWithdraw?: boolean;\n /** Google Consent Mode v2: `auto` (default) is on when `gtag` or `dataLayer` already exist. */\n gcm?: ConsentOptions[\"gcm\"];\n /** Categories offered when no remote config is loaded. */\n categories?: ConsentOptions[\"categories\"];\n /** Dashboard preview: shows the banner from the local options only; touches no cookie. */\n preview?: boolean;\n /** Replaces the banner title. */\n title?: string;\n /** Replaces the banner description. */\n description?: string;\n};\n\n/** Mounts Bitsnap Consent on the page. Renders nothing itself (the banner lives in a Shadow DOM). */\nexport function BitsnapConsent(props: BitsnapConsentProps): null {\n const {\n projectKey, theme, locale, position, privacyPolicyUrl, scope, apiHost, collectHost, reloadOnWithdraw,\n gcm, preview, title, description,\n } = props;\n const configKey = JSON.stringify(props.config ?? null);\n const configRef = useRef(props.config);\n configRef.current = props.config;\n const categoriesKey = JSON.stringify(props.categories ?? null);\n const categoriesRef = useRef(props.categories);\n categoriesRef.current = props.categories;\n\n // A layout effect: the page's passive effects (first events, tracker) must find the API already there.\n useIsomorphicLayoutEffect(() => {\n // A banner from the script tag or another instance already runs on this page.\n if (window.BitsnapConsent) return;\n const api = createConsent({\n projectKey,\n theme,\n locale,\n position,\n privacyPolicyUrl,\n scope,\n apiHost,\n collectHost,\n reloadOnWithdraw,\n gcm,\n preview,\n title,\n description,\n categories: categoriesRef.current,\n config: configRef.current,\n });\n window.BitsnapConsent = api;\n return () => {\n api.destroy();\n if (window.BitsnapConsent === api) delete window.BitsnapConsent;\n };\n }, [\n projectKey, theme, locale, position, privacyPolicyUrl, scope, apiHost, collectHost, reloadOnWithdraw,\n gcm, preview, title, description, categoriesKey, configKey,\n ]);\n\n return null;\n}\n\nlet lastKey = \"null\";\nlet lastState: ConsentState | null = null;\n\nfunction getSnapshot(): ConsentState | null {\n const current = typeof window === \"undefined\" ? null : (window.BitsnapConsent?.get() ?? null);\n const key = JSON.stringify(current);\n if (key !== lastKey) {\n lastKey = key;\n lastState = current;\n }\n return lastState;\n}\n\nfunction subscribe(onChange: () => void): () => void {\n window.addEventListener(\"bitsnap:consent\", onChange);\n window.addEventListener(\"bitsnap:consent:ready\", onChange);\n return () => {\n window.removeEventListener(\"bitsnap:consent\", onChange);\n window.removeEventListener(\"bitsnap:consent:ready\", onChange);\n };\n}\n\nexport type UseConsent = {\n /** The stored choice, or `null` while there is none (or on the server). */\n consent: ConsentState | null;\n /** `true` once the visitor has made a choice that is still valid. */\n decided: boolean;\n open: () => void;\n accept: (categories?: Partial<ConsentChoices>) => void;\n acceptAll: () => void;\n rejectAll: () => void;\n reset: () => void;\n};\n\n/** Current consent state of the page, kept up to date as the visitor chooses. */\nexport function useConsent(): UseConsent {\n const consent = useSyncExternalStore(subscribe, getSnapshot, () => null);\n const open = useCallback(() => window.BitsnapConsent?.open(), []);\n const accept = useCallback((categories?: Partial<ConsentChoices>) => window.BitsnapConsent?.accept(categories), []);\n const acceptAll = useCallback(() => window.BitsnapConsent?.acceptAll(), []);\n const rejectAll = useCallback(() => window.BitsnapConsent?.rejectAll(), []);\n const reset = useCallback(() => window.BitsnapConsent?.reset(), []);\n return { consent, decided: consent !== null, open, accept, acceptAll, rejectAll, reset };\n}\n","export type ConsentCategory = \"analytics\" | \"marketing\" | \"preferences\";\n\nexport type ConsentChoices = Record<ConsentCategory, boolean>;\n\nexport type ConsentTheme = \"auto\" | \"light\" | \"dark\";\n\nexport type ConsentPosition = \"bottom\" | \"bottom-left\" | \"bottom-right\";\n\nexport type ConsentLocale = \"pl\" | \"en\";\n\nexport const CONSENT_CATEGORIES: readonly ConsentCategory[] = [\n \"analytics\",\n \"marketing\",\n \"preferences\",\n];\n\nexport const CONSENT_TEXT_KEYS = [\n \"title\",\n \"description\",\n \"acceptAll\",\n \"rejectAll\",\n \"settings\",\n \"save\",\n \"close\",\n \"necessaryTitle\",\n \"necessaryDescription\",\n \"analyticsTitle\",\n \"analyticsDescription\",\n \"marketingTitle\",\n \"marketingDescription\",\n \"preferencesTitle\",\n \"preferencesDescription\",\n \"poweredBy\",\n] as const;\n\nexport type ConsentTexts = Record<(typeof CONSENT_TEXT_KEYS)[number], string>;\n\nexport type ConsentService = {\n name: string;\n category: ConsentCategory;\n /** Cookies to delete (best effort) when the category is withdrawn. A trailing `*` matches a prefix. */\n cookies?: string[];\n};\n\nexport type ConsentConfig = {\n /** Banner version. A stored choice with a lower `bv` makes the banner ask again. */\n version: number;\n theme: ConsentTheme;\n accentColor?: string;\n position: ConsentPosition;\n privacyPolicyUrl?: string;\n categories: ConsentChoices;\n services: ConsentService[];\n texts?: Partial<Record<ConsentLocale, Partial<ConsentTexts>>>;\n poweredBy: true;\n};\n\n/** What the consent cookie holds, and what `get()` returns. */\nexport type ConsentState = {\n id: string;\n /** Unix seconds. */\n ts: number;\n /** Version of the banner the choice was made on. */\n bv: number;\n c: ConsentChoices;\n};\n\nexport type ConsentAction =\n | \"accept_all\"\n | \"reject_all\"\n | \"custom\"\n | \"withdraw\";\n\n/** `detail` of `bitsnap:consent` and the argument of `on('change')`. */\nexport type ConsentChangeDetail = ConsentState & {\n /** `reset` when the visitor's stored choice was cleared (`get()` is `null` then). */\n action: ConsentAction | \"reset\";\n};\n\nexport type ConsentOptions = {\n /** Public project key. Enables the remote config and the consent log. */\n projectKey?: string;\n theme?: ConsentTheme;\n locale?: \"auto\" | ConsentLocale;\n position?: ConsentPosition;\n privacyPolicyUrl?: string;\n /** `project` keeps one cookie per project key (`bitsnap_consent_<key>`). */\n scope?: \"global\" | \"project\";\n apiHost?: string;\n collectHost?: string;\n /** Google Consent Mode v2. `auto` is on when `gtag` or `dataLayer` already exist. */\n gcm?: \"auto\" | \"on\" | \"off\";\n /** Local overrides, merged over the defaults and the remote config. */\n config?: Partial<ConsentConfig>;\n /** Categories used when no remote config is loaded. */\n categories?: Partial<ConsentChoices>;\n reloadOnWithdraw?: boolean;\n /**\n * Dashboard preview: shows the banner at once from the local options, never\n * reads or writes the cookie, loads no remote config, sends no consent log,\n * and the buttons only close the banner.\n */\n preview?: boolean;\n /** Replaces the banner title (current locale, non-empty values only). */\n title?: string;\n /** Replaces the banner description (current locale, non-empty values only). */\n description?: string;\n};\n\nexport type BitsnapConsentApi = {\n /**\n * Whose choice this banner holds: `project` keeps one cookie per project key\n * (`bitsnap_consent_<key>`), `global` the site-wide `bitsnap_consent`.\n */\n readonly scope: \"global\" | \"project\";\n /** The bare public project key the banner was started with, if any (`pk_` stripped). */\n readonly projectKey: string | null;\n get(): ConsentState | null;\n /**\n * `true` when the visitor refused `category`, also in a choice made on an older banner\n * version (which `get()` no longer returns) that they have not answered again yet.\n */\n denied(category: ConsentCategory): boolean;\n on(event: \"change\", fn: (detail: ConsentChangeDetail) => void): () => void;\n open(): void;\n close(): void;\n accept(categories?: Partial<ConsentChoices>): void;\n acceptAll(): void;\n rejectAll(): void;\n reset(): void;\n /** Resolves with the stored choice (or `null`) once config and stored choice are applied. */\n ready: Promise<ConsentState | null>;\n destroy(): void;\n};\n","import { CONSENT_CATEGORIES, type ConsentChoices } from \"./types\";\n\nconst SCRIPT_SELECTOR = 'script[type=\"text/plain\"][data-bitsnap-consent]:not([data-bitsnap-queued])';\nconst FRAME_SELECTOR = \"iframe[data-bitsnap-consent][data-src]\";\n\nconst allowed = (c: ConsentChoices, category: string | null): boolean =>\n (CONSENT_CATEGORIES as readonly string[]).includes(category ?? \"\") &&\n c[category as keyof ConsentChoices];\n\n/** Settles when the script has loaded or failed: a broken script must not block the ones after it. */\nfunction settled(script: HTMLScriptElement): Promise<void> {\n return new Promise((resolve) => {\n script.addEventListener(\"load\", () => resolve(), { once: true });\n script.addEventListener(\"error\", () => resolve(), { once: true });\n });\n}\n\n/** Swaps the placeholder for a live script; resolves once an external one has loaded. */\nfunction run(doc: Document, placeholder: HTMLScriptElement): Promise<void> | undefined {\n const script = doc.createElement(\"script\");\n for (const { name, value } of Array.from(placeholder.attributes)) {\n if (name === \"type\" || name === \"data-bitsnap-queued\") continue;\n if (name === \"data-type\") {\n // The embed writes the original `type=\"module\"` here, as `type` itself is `text/plain`.\n if (value === \"module\") script.setAttribute(\"type\", \"module\");\n } else if (name === \"data-src\") script.setAttribute(\"src\", value);\n else script.setAttribute(name, value);\n }\n if (placeholder.nonce) script.nonce = placeholder.nonce;\n const external = script.hasAttribute(\"src\");\n if (external) {\n // Dynamic scripts are async by default; keep the order of the page.\n if (!placeholder.hasAttribute(\"async\")) script.async = false;\n } else {\n script.text = placeholder.text;\n }\n const loaded = external ? settled(script) : undefined;\n placeholder.parentNode?.replaceChild(script, placeholder);\n return loaded;\n}\n\n/**\n * Turns the consented placeholders into live elements:\n * `<script type=\"text/plain\" data-bitsnap-consent=\"marketing\" [data-src] [data-type=\"module\"]>` becomes\n * a real script (attributes copied, `type` dropped or restored from `data-type`, `data-src` -> `src`),\n * and `<iframe data-src>` gets its `src`.\n * Placeholders run in document order: an inline script waits for the external scripts before it\n * (`<script src=lib.js>` then `<script>Lib.init()</script>`), external ones only follow each other.\n */\nexport function activateScripts(doc: Document, c: ConsentChoices): number {\n let activated = 0;\n try {\n /** Set once a placeholder had to wait: everything after it queues behind it. */\n let gate: Promise<void> | null = null;\n /** External scripts inserted since the last inline one. */\n let loading: Promise<void>[] = [];\n doc.querySelectorAll<HTMLScriptElement>(SCRIPT_SELECTOR).forEach((placeholder) => {\n if (!allowed(c, placeholder.getAttribute(\"data-bitsnap-consent\"))) return;\n activated++;\n const inline = !placeholder.hasAttribute(\"data-src\") && !placeholder.hasAttribute(\"src\");\n if (gate || (inline && loading.length > 0)) {\n // Marked so that another call (a later choice) does not queue it again.\n placeholder.setAttribute(\"data-bitsnap-queued\", \"\");\n gate = (gate ?? Promise.all(loading).then(() => {})).then(() => {\n try {\n return run(doc, placeholder);\n } catch {\n // never throw into the host page\n }\n });\n loading = [];\n return;\n }\n const loaded = run(doc, placeholder);\n if (loaded) loading.push(loaded);\n });\n doc.querySelectorAll<HTMLIFrameElement>(FRAME_SELECTOR).forEach((frame) => {\n if (!allowed(c, frame.getAttribute(\"data-bitsnap-consent\"))) return;\n const src = frame.getAttribute(\"data-src\");\n if (src && frame.getAttribute(\"src\") !== src) {\n frame.setAttribute(\"src\", src);\n frame.removeAttribute(\"data-src\");\n activated++;\n }\n });\n } catch {\n // never throw into the host page\n }\n return activated;\n}\n","import {\n CONSENT_CATEGORIES,\n type ConsentChoices,\n type ConsentConfig,\n type ConsentService,\n} from \"./types\";\n\nexport const DEFAULT_API_HOST = \"https://bitsnap.pl\";\nexport const DEFAULT_COLLECT_HOST = \"https://collect.bitsnap.pl\";\n\nexport function defaultConfig(): ConsentConfig {\n return {\n version: 1,\n theme: \"auto\",\n position: \"bottom\",\n categories: { analytics: true, marketing: true, preferences: false },\n services: [],\n poweredBy: true,\n };\n}\n\nconst THEMES = [\"auto\", \"light\", \"dark\"];\nconst POSITIONS = [\"bottom\", \"bottom-left\", \"bottom-right\"];\n\nconst HEX_COLOR = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;\n\nexport function safeColor(value: unknown): string | undefined {\n return typeof value === \"string\" && HEX_COLOR.test(value.trim())\n ? value.trim()\n : undefined;\n}\n\n/** Only http(s) and relative URLs may end up in the banner. */\nexport function safeUrl(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const url = value.trim();\n return /^(?:https?:\\/\\/|\\/(?!\\/)|#|\\?)/i.test(url) ? url : undefined;\n}\n\nfunction cleanServices(value: unknown): ConsentService[] {\n if (!Array.isArray(value)) return [];\n const services: ConsentService[] = [];\n for (const item of value) {\n if (\n item &&\n typeof item.name === \"string\" &&\n (CONSENT_CATEGORIES as readonly string[]).includes(item.category)\n ) {\n services.push({\n name: item.name,\n category: item.category,\n cookies: Array.isArray(item.cookies)\n ? item.cookies.filter((cookie: unknown) => typeof cookie === \"string\")\n : undefined,\n });\n }\n }\n return services;\n}\n\n/** Layers partial configs (later wins) over the defaults, dropping invalid values. */\nexport function mergeConfig(\n ...layers: (Partial<ConsentConfig> | null | undefined)[]\n): ConsentConfig {\n const result = defaultConfig();\n for (const layer of layers) {\n if (!layer || typeof layer !== \"object\") continue;\n if (typeof layer.version === \"number\" && layer.version >= 0) {\n result.version = Math.floor(layer.version);\n }\n if (THEMES.includes(layer.theme as string)) result.theme = layer.theme!;\n if (POSITIONS.includes(layer.position as string)) result.position = layer.position!;\n const accent = safeColor(layer.accentColor);\n if (accent) result.accentColor = accent;\n const privacy = safeUrl(layer.privacyPolicyUrl);\n if (privacy) result.privacyPolicyUrl = privacy;\n if (layer.categories && typeof layer.categories === \"object\") {\n for (const category of CONSENT_CATEGORIES) {\n if (typeof layer.categories[category] === \"boolean\") {\n result.categories[category] = layer.categories[category];\n }\n }\n }\n if (layer.services !== undefined) result.services = cleanServices(layer.services);\n if (layer.texts && typeof layer.texts === \"object\") result.texts = layer.texts;\n }\n return result;\n}\n\nexport function parseCategoryList(value: string | null | undefined): ConsentChoices {\n const wanted = (value ?? \"\").split(\",\").map((item) => item.trim());\n return {\n analytics: wanted.includes(\"analytics\"),\n marketing: wanted.includes(\"marketing\"),\n preferences: wanted.includes(\"preferences\"),\n };\n}\n\n/** What `GET /api/consent/config` answers: whether the banner is on, and its config. */\nexport type RemoteConsentConfig = {\n /** `false` when the project turned the banner off: nothing is shown, stored choices still apply. */\n enabled: boolean;\n config: Partial<ConsentConfig> | null;\n};\n\n/** Loads the project's banner config. Resolves to `null` on any failure or after the timeout. */\nexport async function fetchRemoteConfig(\n apiHost: string,\n key: string,\n timeoutMs = 3000,\n): Promise<RemoteConsentConfig | null> {\n if (typeof fetch !== \"function\") return null;\n const controller = typeof AbortController === \"function\" ? new AbortController() : undefined;\n const timer = setTimeout(() => controller?.abort(), timeoutMs);\n try {\n const response = await fetch(\n `${apiHost.replace(/\\/+$/, \"\")}/api/consent/config?key=${encodeURIComponent(key)}`,\n { signal: controller?.signal, credentials: \"omit\" },\n );\n if (!response.ok) return null;\n const body = await response.json();\n if (!body || typeof body !== \"object\") return null;\n const config = body.config && typeof body.config === \"object\" ? body.config : null;\n const enabled = body.enabled !== false;\n return config || !enabled ? { enabled, config } : null;\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n}\n","/** Colors of both themes. Every text/background pair is checked against WCAG AA in the tests. */\nexport const PALETTE = {\n light: {\n bg: \"#ffffff\",\n fg: \"#14171c\",\n muted: \"#495260\",\n border: \"#c9ced6\",\n off: \"#6b7280\",\n thumb: \"#ffffff\",\n accent: \"#1d4ed8\",\n onAccent: \"#ffffff\",\n link: \"#1d4ed8\",\n },\n dark: {\n bg: \"#171a20\",\n fg: \"#f1f3f6\",\n muted: \"#b7beca\",\n border: \"#3a404b\",\n off: \"#8b93a1\",\n thumb: \"#171a20\",\n accent: \"#8ab4ff\",\n onAccent: \"#0b1220\",\n link: \"#9dc0ff\",\n },\n} as const;\n\nconst vars = (p: (typeof PALETTE)[\"light\"]) =>\n `--bg:${p.bg};--fg:${p.fg};--muted:${p.muted};--border:${p.border};--off:${p.off};--thumb:${p.thumb};--accent:${p.accent};--on-accent:${p.onAccent};--link:${p.link};--focus:${p.accent}`;\n\nexport const STYLES = `\n:host{all:initial}\n*{box-sizing:border-box}\n[hidden]{display:none!important}\n.root{${vars(PALETTE.light)};position:fixed;inset:0;pointer-events:none;z-index:2147483000;font:14px/1.5 system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;color:var(--fg);-webkit-text-size-adjust:100%}\n.root[data-theme=dark]{${vars(PALETTE.dark)};color-scheme:dark}\nbutton,input{font:inherit;color:inherit}\n.banner,.overlay{pointer-events:auto}\n@supports (color:color-mix(in srgb,red 50%,blue)){::selection{background:color-mix(in srgb,var(--accent) 24%,transparent)}}\n.banner{position:absolute;background:var(--bg);color:var(--fg);max-height:92vh;overflow:auto;outline:none;animation:in .22s ease-out}\n.bottom{left:0;right:0;bottom:0;border-top:1px solid var(--border);box-shadow:0 -8px 28px rgba(15,23,42,.08);padding:20px 24px calc(20px + env(safe-area-inset-bottom,0px))}\n.root[data-theme=dark] .bottom{box-shadow:0 -8px 28px rgba(0,0,0,.4)}\n.bottom .inner{display:grid;grid-template-columns:minmax(min(100%,42ch),1fr) auto;grid-template-areas:\"text actions\" \"foot actions\";gap:4px 40px;align-items:center;max-width:1120px;margin:0 auto}\n.bottom .text{max-width:68ch}\n.bottom .btn,.bottom .link-btn{flex:0 0 auto}\n.card{bottom:16px;width:min(400px,calc(100vw - 32px));border:1px solid var(--border);border-radius:14px;padding:20px;box-shadow:0 12px 32px rgba(15,23,42,.14)}\n.root[data-theme=dark] .card{box-shadow:0 12px 32px rgba(0,0,0,.45)}\n.bottom-left{left:16px}\n.bottom-right{right:16px}\n.card .inner{display:flex;flex-direction:column;gap:16px}\n.text{grid-area:text}\n.actions{grid-area:actions;display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:flex-end}\n.card .actions{justify-content:stretch}\n.foot{grid-area:foot}\n.title{overflow-wrap:anywhere;font-size:17px;font-weight:650;line-height:1.35;letter-spacing:-.01em;text-wrap:balance;margin:0 0 6px}\n.desc{overflow-wrap:anywhere;margin:0;color:var(--muted);line-height:1.55;text-wrap:pretty}\n.desc a{color:var(--link);text-decoration:underline;text-underline-offset:3px}\n.btn,.link-btn{min-height:38px;padding:0 16px;border-radius:8px;font-size:13.5px;font-weight:600;cursor:pointer;white-space:nowrap;transition:background-color .12s ease-out,filter .12s ease-out,border-color .12s ease-out}\n.btn{border:2px solid var(--accent);background:var(--accent);color:var(--on-accent);flex:1 1 150px}\n.btn:hover{filter:brightness(.93)}\n.btn:active{filter:brightness(.87)}\n.link-btn{border:1px solid var(--off);background:transparent;color:var(--fg);text-decoration:none}\n.link-btn:hover{border-color:var(--fg)}\n@supports (color:color-mix(in srgb,red 50%,blue)){.link-btn:hover{background:color-mix(in srgb,var(--fg) 9%,transparent)}.link-btn:active{background:color-mix(in srgb,var(--fg) 14%,transparent)}}\n.card .link-btn{flex:1 1 100%}\nbutton:focus-visible,a:focus-visible,.dialog:focus-visible{outline:2px solid var(--focus);outline-offset:2px}\n.foot{font-size:12px;color:var(--muted);padding-top:8px}\n.foot a{color:var(--muted);text-decoration:none;display:inline-flex;align-items:center;gap:5px;vertical-align:middle}\n.foot a:hover{text-decoration:underline}\n.foot b{color:var(--fg);font-weight:700;letter-spacing:-.01em}\n.foot svg{display:block}\n.overlay{position:absolute;inset:0;background:rgba(10,12,16,.58);display:flex;align-items:center;justify-content:center;padding:16px;animation:fade .2s ease-out}\n.dialog{background:var(--bg);color:var(--fg);width:100%;max-width:560px;max-height:min(92vh,760px);display:flex;flex-direction:column;border-radius:16px;box-shadow:0 16px 48px rgba(0,0,0,.35);outline:none;animation:in .22s ease-out}\n.d-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:16px 8px 8px 24px}\n.d-head .title{margin:0;font-size:18px}\n.x{width:44px;height:44px;border:0;border-radius:10px;background:none;cursor:pointer;display:flex;align-items:center;justify-content:center;flex:none}\n.x:hover{background:var(--border)}\n.d-body{overflow:auto;padding:0 24px 8px;flex:1}\n.cat{border-top:1px solid var(--border);padding:10px 0 14px}\n.row{display:flex;align-items:center;justify-content:space-between;gap:12px}\n.cat-title{font-weight:600;font-size:15px}\n.always{font-size:12px;color:var(--muted);margin-right:4px}\n.cat p{margin:0;color:var(--muted)}\n.cat .svc{margin-top:6px;font-size:13px}\n.cat .svc b{font-weight:600;color:var(--fg)}\n.switch{position:relative;width:52px;height:44px;display:flex;align-items:center;justify-content:flex-end;flex:none}\n.switch input{position:absolute;inset:0;width:100%;height:100%;margin:0;opacity:0;cursor:pointer}\n.switch input:disabled{cursor:default}\n.track{width:44px;height:24px;border-radius:12px;background:var(--off);position:relative;transition:background .15s}\n.track::after{content:\"\";position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;background:var(--thumb);transition:transform .15s}\ninput:checked+.track{background:var(--accent)}\ninput:checked+.track::after{transform:translateX(20px);background:var(--on-accent)}\ninput:disabled+.track{opacity:.6}\ninput:focus-visible+.track{outline:2px solid var(--focus);outline-offset:2px}\n.d-foot{display:flex;flex-direction:column;gap:8px;padding:12px 24px 16px;border-top:1px solid var(--border)}\n.d-foot .actions{justify-content:stretch}\n@keyframes in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}\n@keyframes fade{from{opacity:0}to{opacity:1}}\n@media(max-width:960px){\n.bottom .inner{grid-template-columns:minmax(0,1fr);grid-template-areas:\"text\" \"actions\" \"foot\";gap:14px}\n.bottom .actions{justify-content:flex-start}\n}\n@media(max-width:640px){\n.bottom{padding:16px 16px calc(16px + env(safe-area-inset-bottom,0px))}\n.bottom .inner{gap:12px}\n.bottom .actions{justify-content:stretch}\n.bottom .btn{flex:1 1 130px}\n.bottom .link-btn{flex:1 1 100%}\n.card{left:8px;right:8px;bottom:8px;width:auto}\n.overlay{align-items:flex-end;padding:0}\n.dialog{max-width:none;border-radius:16px 16px 0 0;max-height:92vh}\n.d-head{padding-left:16px}\n.d-body{padding:0 16px 8px}\n.d-foot{padding:12px 16px calc(16px + env(safe-area-inset-bottom,0px))}\n.btn{flex-basis:130px}\n.btn,.link-btn{padding:0 12px;min-height:40px}\n}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important}}\n`.replace(/\\n/g, \"\");\n","import { contrastRatio, onColor } from \"./contrast\";\nimport { UI_LABELS } from \"./texts\";\nimport { PALETTE, STYLES } from \"./styles\";\nimport {\n CONSENT_CATEGORIES,\n type ConsentCategory,\n type ConsentChoices,\n type ConsentConfig,\n type ConsentLocale,\n type ConsentTexts,\n} from \"./types\";\n\nexport type UIOptions = {\n doc: Document;\n config: ConsentConfig;\n texts: ConsentTexts;\n locale: ConsentLocale;\n /** Current choice, used to preset the toggles. */\n getChoices(): ConsentChoices | null;\n /** Whether a valid choice exists, i.e. closing the settings must not bring back the banner. */\n hasChoice(): boolean;\n onAcceptAll(): void;\n onRejectAll(): void;\n onSave(choices: ConsentChoices): void;\n};\n\nexport type ConsentUI = {\n /** `focus` moves keyboard focus into the banner; leave it off when the banner opens on page load. */\n showBanner(focus?: boolean): void;\n showSettings(): void;\n hide(): void;\n destroy(): void;\n};\n\nconst WORDMARK =\n '<svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" aria-hidden=\"true\"><rect width=\"16\" height=\"16\" rx=\"4\" fill=\"currentColor\" style=\"color:var(--fg)\"/><circle cx=\"8\" cy=\"8\" r=\"3\" fill=\"var(--bg)\"/></svg>';\n\nconst CLOSE_ICON =\n '<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"M3 3l10 10M13 3L3 13\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" fill=\"none\"/></svg>';\n\nconst FOCUSABLE = \"button:not([disabled]),input:not([disabled]),a[href]\";\n\nexport function createUI(options: UIOptions): ConsentUI {\n const { doc, config, texts, locale } = options;\n const labels = UI_LABELS[locale];\n let host: HTMLElement | null = null;\n let shadow: ShadowRoot | null = null;\n let root: HTMLElement;\n let banner: HTMLElement;\n let overlay: HTMLElement;\n let dialog: HTMLElement;\n let settingsButton: HTMLElement;\n let layer: \"none\" | \"banner\" | \"settings\" = \"none\";\n let opener: HTMLElement | null = null;\n let media: MediaQueryList | null = null;\n const toggles = new Map<ConsentCategory, HTMLInputElement>();\n\n function el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props: Record<string, string> = {},\n ...children: (Node | string)[]\n ): HTMLElementTagNameMap[K] {\n const node = doc.createElement(tag);\n for (const key of Object.keys(props)) {\n if (key === \"class\") node.className = props[key];\n else node.setAttribute(key, props[key]);\n }\n for (const child of children) node.append(child);\n return node;\n }\n\n function button(cls: string, text: string, onClick: () => void) {\n const node = el(\"button\", { type: \"button\", class: cls }, text);\n node.addEventListener(\"click\", onClick);\n return node;\n }\n\n function powered(): HTMLElement {\n const link = el(\"a\", {\n href: `https://bitsnap.pl/?utm_source=consent_banner&utm_medium=referral&utm_content=${encodeURIComponent(doc.location?.hostname ?? \"\")}`,\n target: \"_blank\",\n rel: \"noopener\",\n });\n link.append(texts.poweredBy + \" \");\n link.insertAdjacentHTML(\"beforeend\", WORDMARK);\n link.append(el(\"b\", {}, \"bitsnap\"));\n return el(\"div\", { class: \"foot\" }, link);\n }\n\n function description(): HTMLElement {\n const node = el(\"p\", { class: \"desc\", id: \"d\" }, texts.description);\n if (config.privacyPolicyUrl) {\n node.append(\n \" \",\n el(\n \"a\",\n { href: config.privacyPolicyUrl, target: \"_blank\", rel: \"noopener\" },\n labels.privacy,\n ),\n );\n }\n return node;\n }\n\n function buildBanner() {\n settingsButton = button(\"link-btn\", texts.settings, () => showSettings());\n banner = el(\n \"div\",\n {\n class: `banner ${config.position === \"bottom\" ? \"bottom\" : `card ${config.position}`}`,\n // Not modal: the page stays usable, so it is a live region, not a dialog.\n role: \"region\",\n \"aria-live\": \"polite\",\n \"aria-labelledby\": \"t\",\n \"aria-describedby\": \"d\",\n tabindex: \"-1\",\n },\n el(\n \"div\",\n { class: \"inner\" },\n el(\"div\", { class: \"text\" }, el(\"div\", { class: \"title\", id: \"t\" }, texts.title), description()),\n el(\n \"div\",\n { class: \"actions\" },\n button(\"btn\", texts.acceptAll, options.onAcceptAll),\n button(\"btn\", texts.rejectAll, options.onRejectAll),\n settingsButton,\n ),\n powered(),\n ),\n );\n }\n\n function category(id: ConsentCategory | \"necessary\"): HTMLElement {\n const services = config.services.filter((service) => service.category === id);\n const input = el(\"input\", { type: \"checkbox\", role: \"switch\", \"aria-labelledby\": `l-${id}`, \"aria-describedby\": `p-${id}` });\n if (id === \"necessary\") {\n input.checked = true;\n input.disabled = true;\n } else {\n toggles.set(id, input);\n }\n const node = el(\n \"div\",\n { class: \"cat\" },\n el(\n \"div\",\n { class: \"row\" },\n el(\"span\", { class: \"cat-title\", id: `l-${id}` }, texts[`${id}Title` as keyof ConsentTexts]),\n el(\n \"span\",\n { style: \"display:flex;align-items:center\" },\n ...(id === \"necessary\" ? [el(\"span\", { class: \"always\" }, labels.always)] : []),\n el(\"label\", { class: \"switch\" }, input, el(\"span\", { class: \"track\" })),\n ),\n ),\n el(\"p\", { id: `p-${id}` }, texts[`${id}Description` as keyof ConsentTexts]),\n );\n if (services.length > 0) {\n node.append(\n el(\"p\", { class: \"svc\" }, el(\"b\", {}, `${labels.services}: `), services.map((s) => s.name).join(\", \")),\n );\n }\n return node;\n }\n\n function buildSettings() {\n const close = el(\"button\", { type: \"button\", class: \"x\", \"aria-label\": texts.close });\n close.insertAdjacentHTML(\"beforeend\", CLOSE_ICON);\n close.addEventListener(\"click\", () => closeSettings());\n const body = el(\"div\", { class: \"d-body\" }, category(\"necessary\"));\n for (const id of CONSENT_CATEGORIES) {\n if (config.categories[id]) body.append(category(id));\n }\n dialog = el(\n \"div\",\n { class: \"dialog\", role: \"dialog\", \"aria-modal\": \"true\", \"aria-labelledby\": \"st\", tabindex: \"-1\" },\n el(\"div\", { class: \"d-head\" }, el(\"div\", { class: \"title\", id: \"st\" }, texts.settings), close),\n body,\n el(\n \"div\",\n { class: \"d-foot\" },\n el(\n \"div\",\n { class: \"actions\" },\n button(\"btn\", texts.save, () => options.onSave(readToggles())),\n button(\"btn\", texts.acceptAll, options.onAcceptAll),\n button(\"btn\", texts.rejectAll, options.onRejectAll),\n ),\n powered(),\n ),\n );\n overlay = el(\"div\", { class: \"overlay\" }, dialog);\n overlay.addEventListener(\"mousedown\", (event) => {\n if (event.target === overlay) closeSettings();\n });\n }\n\n function readToggles(): ConsentChoices {\n const choices = { analytics: false, marketing: false, preferences: false };\n toggles.forEach((input, key) => (choices[key] = input.checked));\n return choices;\n }\n\n function applyTheme() {\n const dark = config.theme === \"dark\" || (config.theme === \"auto\" && !!media?.matches);\n root.setAttribute(\"data-theme\", dark ? \"dark\" : \"light\");\n applyAccent(dark);\n }\n\n /** A custom accent that is too faint on the dark background gives way to the dark theme's own. */\n function applyAccent(dark: boolean) {\n const accent = config.accentColor;\n if (!accent) return;\n if (dark && contrastRatio(accent, PALETTE.dark.bg) < 3) {\n for (const name of [\"--accent\", \"--focus\", \"--on-accent\"]) root.style.removeProperty(name);\n return;\n }\n root.style.setProperty(\"--accent\", accent);\n root.style.setProperty(\"--focus\", accent);\n root.style.setProperty(\"--on-accent\", onColor(accent));\n }\n\n function ensure() {\n if (host) return;\n host = el(\"div\", { id: \"bitsnap-consent\", \"data-bitsnap-consent-host\": \"\" });\n shadow = host.attachShadow({ mode: \"open\" });\n shadow.append(el(\"style\", {}, STYLES));\n root = el(\"div\", { class: \"root\", lang: locale });\n buildBanner();\n buildSettings();\n banner.hidden = true;\n overlay.hidden = true;\n root.append(banner, overlay);\n shadow.append(root);\n if (typeof doc.defaultView?.matchMedia === \"function\") {\n media = doc.defaultView.matchMedia(\"(prefers-color-scheme: dark)\");\n media.addEventListener?.(\"change\", applyTheme);\n }\n applyTheme();\n doc.body.append(host);\n }\n\n function focusables(): HTMLElement[] {\n return Array.from(dialog.querySelectorAll<HTMLElement>(FOCUSABLE));\n }\n\n function onKeydown(event: KeyboardEvent) {\n if (layer !== \"settings\") return;\n if (event.key === \"Escape\") {\n event.preventDefault();\n closeSettings();\n return;\n }\n if (event.key !== \"Tab\") return;\n const items = focusables();\n if (items.length === 0) return;\n const active = shadow?.activeElement as HTMLElement | null;\n const first = items[0];\n const last = items[items.length - 1];\n const outside = !active || !dialog.contains(active);\n if (event.shiftKey && (outside || active === first || active === dialog)) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && (outside || active === last)) {\n event.preventDefault();\n first.focus();\n }\n }\n\n function showBanner(focus = false) {\n try {\n ensure();\n layer = \"banner\";\n doc.removeEventListener(\"keydown\", onKeydown, true);\n overlay.hidden = true;\n banner.hidden = false;\n if (focus) banner.focus({ preventScroll: true });\n } catch {\n // never throw into the host page\n }\n }\n\n function showSettings() {\n try {\n ensure();\n if (layer !== \"settings\") {\n const active = doc.activeElement as HTMLElement | null;\n opener = active && active !== host && active !== doc.body ? active : null;\n }\n const current = options.getChoices();\n toggles.forEach((input, key) => (input.checked = !!current?.[key]));\n layer = \"settings\";\n banner.hidden = true;\n overlay.hidden = false;\n doc.addEventListener(\"keydown\", onKeydown, true);\n dialog.focus({ preventScroll: true });\n } catch {\n // never throw into the host page\n }\n }\n\n function closeSettings() {\n if (layer !== \"settings\") return;\n doc.removeEventListener(\"keydown\", onKeydown, true);\n overlay.hidden = true;\n if (options.hasChoice()) {\n layer = \"none\";\n opener?.focus?.({ preventScroll: true });\n } else {\n layer = \"banner\";\n banner.hidden = false;\n settingsButton.focus({ preventScroll: true });\n }\n }\n\n function hide() {\n if (!host) return;\n doc.removeEventListener(\"keydown\", onKeydown, true);\n const wasSettings = layer === \"settings\";\n layer = \"none\";\n banner.hidden = true;\n overlay.hidden = true;\n if (wasSettings) opener?.focus?.({ preventScroll: true });\n }\n\n function destroy() {\n doc.removeEventListener(\"keydown\", onKeydown, true);\n media?.removeEventListener?.(\"change\", applyTheme);\n host?.remove();\n host = null;\n shadow = null;\n layer = \"none\";\n }\n\n return { showBanner, showSettings, hide, destroy };\n}\n"]}
|
package/dist/consent.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import{b as
|
|
1
|
+
import{b as ne,c as oe,j as ie,k as re,l as se,m as ae,n as ce,p as V,q as le}from"./chunk-MDCP5JNP.mjs";import"./chunk-U6FLIRIV.mjs";import{useCallback as N,useRef as Ee,useSyncExternalStore as Fe}from"react";var T=["analytics","marketing","preferences"];var je='script[type="text/plain"][data-bitsnap-consent]:not([data-bitsnap-queued])',Pe="iframe[data-bitsnap-consent][data-src]",de=(e,n)=>T.includes(n??"")&&e[n];function Oe(e){return new Promise(n=>{e.addEventListener("load",()=>n(),{once:!0}),e.addEventListener("error",()=>n(),{once:!0})})}function pe(e,n){let t=e.createElement("script");for(let{name:a,value:c}of Array.from(n.attributes))a==="type"||a==="data-bitsnap-queued"||(a==="data-type"?c==="module"&&t.setAttribute("type","module"):a==="data-src"?t.setAttribute("src",c):t.setAttribute(a,c));n.nonce&&(t.nonce=n.nonce);let s=t.hasAttribute("src");s?n.hasAttribute("async")||(t.async=!1):t.text=n.text;let d=s?Oe(t):void 0;return n.parentNode?.replaceChild(t,n),d}function fe(e,n){let t=0;try{let s=null,d=[];e.querySelectorAll(je).forEach(a=>{if(!de(n,a.getAttribute("data-bitsnap-consent")))return;t++;let c=!a.hasAttribute("data-src")&&!a.hasAttribute("src");if(s||c&&d.length>0){a.setAttribute("data-bitsnap-queued",""),s=(s??Promise.all(d).then(()=>{})).then(()=>{try{return pe(e,a)}catch{}}),d=[];return}let m=pe(e,a);m&&d.push(m)}),e.querySelectorAll(Pe).forEach(a=>{if(!de(n,a.getAttribute("data-bitsnap-consent")))return;let c=a.getAttribute("data-src");c&&a.getAttribute("src")!==c&&(a.setAttribute("src",c),a.removeAttribute("data-src"),t++)})}catch{}return t}var ue="https://bitsnap.pl",ge="https://collect.bitsnap.pl";function Q(){return{version:1,theme:"auto",position:"bottom",categories:{analytics:!0,marketing:!0,preferences:!1},services:[],poweredBy:!0}}var _e=["auto","light","dark"],ze=["bottom","bottom-left","bottom-right"],Me=/^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;function Ue(e){return typeof e=="string"&&Me.test(e.trim())?e.trim():void 0}function Be(e){if(typeof e!="string")return;let n=e.trim();return/^(?:https?:\/\/|\/(?!\/)|#|\?)/i.test(n)?n:void 0}function De(e){if(!Array.isArray(e))return[];let n=[];for(let t of e)t&&typeof t.name=="string"&&T.includes(t.category)&&n.push({name:t.name,category:t.category,cookies:Array.isArray(t.cookies)?t.cookies.filter(s=>typeof s=="string"):void 0});return n}function R(...e){let n=Q();for(let t of e){if(!t||typeof t!="object")continue;typeof t.version=="number"&&t.version>=0&&(n.version=Math.floor(t.version)),_e.includes(t.theme)&&(n.theme=t.theme),ze.includes(t.position)&&(n.position=t.position);let s=Ue(t.accentColor);s&&(n.accentColor=s);let d=Be(t.privacyPolicyUrl);if(d&&(n.privacyPolicyUrl=d),t.categories&&typeof t.categories=="object")for(let a of T)typeof t.categories[a]=="boolean"&&(n.categories[a]=t.categories[a]);t.services!==void 0&&(n.services=De(t.services)),t.texts&&typeof t.texts=="object"&&(n.texts=t.texts)}return n}async function ye(e,n,t=3e3){if(typeof fetch!="function")return null;let s=typeof AbortController=="function"?new AbortController:void 0,d=setTimeout(()=>s?.abort(),t);try{let a=await fetch(`${e.replace(/\/+$/,"")}/api/consent/config?key=${encodeURIComponent(n)}`,{signal:s?.signal,credentials:"omit"});if(!a.ok)return null;let c=await a.json();if(!c||typeof c!="object")return null;let m=c.config&&typeof c.config=="object"?c.config:null,g=c.enabled!==!1;return m||!g?{enabled:g,config:m}:null}catch{return null}finally{clearTimeout(d)}}var He={ad_storage:"denied",ad_user_data:"denied",ad_personalization:"denied",analytics_storage:"denied",functionality_storage:"denied",personalization_storage:"denied",security_storage:"granted",wait_for_update:500},z=e=>e?"granted":"denied";function ee(e){return{analytics_storage:z(e.analytics),ad_storage:z(e.marketing),ad_user_data:z(e.marketing),ad_personalization:z(e.marketing),functionality_storage:z(e.preferences),personalization_storage:z(e.preferences)}}function Re(e,n){return n==="off"?!1:n==="on"||typeof e.gtag=="function"||Array.isArray(e.dataLayer)}function Ne(e){if(typeof e.gtag=="function")return e.gtag;let n=e.dataLayer=e.dataLayer||[],t=function(){n.push(arguments)};return e.gtag=t,t}function me(e,n){if(!Re(e,n))return null;let t=Ne(e);return{setDefault:()=>t("consent","default",{...He}),update:s=>t("consent","update",ee(s))}}var te={pl:{title:"Szanujemy Twoj\u0105 prywatno\u015B\u0107",description:"U\u017Cywamy plik\xF3w cookie, aby strona dzia\u0142a\u0142a poprawnie. Za Twoj\u0105 zgod\u0105 wykorzystujemy je te\u017C do statystyk, personalizacji i reklam. Mo\u017Cesz zaakceptowa\u0107 wszystkie, odrzuci\u0107 wszystkie albo wybra\u0107, na co si\u0119 zgadzasz.",acceptAll:"Akceptuj wszystkie",rejectAll:"Odrzu\u0107 wszystkie",settings:"Ustawienia",save:"Zapisz wyb\xF3r",close:"Zamknij",necessaryTitle:"Niezb\u0119dne",necessaryDescription:"Potrzebne do dzia\u0142ania strony: koszyk, zapami\u0119tanie Twojego wyboru, bezpiecze\u0144stwo. Nie mo\u017Cna ich wy\u0142\u0105czy\u0107.",analyticsTitle:"Statystyki",analyticsDescription:"Pomagaj\u0105 nam zrozumie\u0107, jak odwiedzaj\u0105cy korzystaj\u0105 ze strony, \u017Ceby\u015Bmy mogli j\u0105 ulepsza\u0107.",marketingTitle:"Marketing",marketingDescription:"Pozwalaj\u0105 mierzy\u0107 skuteczno\u015B\u0107 reklam i pokazywa\u0107 Ci oferty dopasowane do zainteresowa\u0144.",preferencesTitle:"Preferencje",preferencesDescription:"Zapami\u0119tuj\u0105 Twoje ustawienia, na przyk\u0142ad j\u0119zyk czy wygl\u0105d strony.",poweredBy:"Powered by"},en:{title:"We value your privacy",description:"We use cookies to make this site work. With your consent we also use them for statistics, personalization and ads. You can accept all, reject all, or choose what you agree to.",acceptAll:"Accept all",rejectAll:"Reject all",settings:"Settings",save:"Save choices",close:"Close",necessaryTitle:"Necessary",necessaryDescription:"Needed for the site to work: the cart, remembering your choice, security. They cannot be turned off.",analyticsTitle:"Analytics",analyticsDescription:"Help us understand how visitors use the site, so we can improve it.",marketingTitle:"Marketing",marketingDescription:"Measure how well ads work and show you offers that match your interests.",preferencesTitle:"Preferences",preferencesDescription:"Remember your settings, such as language or how the site looks.",poweredBy:"Powered by"}},be={pl:{privacy:"Polityka prywatno\u015Bci",services:"Us\u0142ugi",always:"Zawsze aktywne"},en:{privacy:"Privacy policy",services:"Services",always:"Always on"}};function X(e,n){return e==="pl"||e==="en"?e:/^pl\b/i.test(n??"")?"pl":"en"}function q(e,n){let t={...te[e]},s=n?.[e];if(s)for(let d of Object.keys(t)){let a=s[d];typeof a=="string"&&a.trim()!==""&&(t[d]=a)}return t}function Ie(e){let n=e.replace("#","");n.length===3&&(n=n.replace(/./g,"$&$&"));let t=parseInt(n,16);return[t>>16&255,t>>8&255,t&255]}function he(e){let[n,t,s]=Ie(e).map(d=>{let a=d/255;return a<=.03928?a/12.92:Math.pow((a+.055)/1.055,2.4)});return .2126*n+.7152*t+.0722*s}function Y(e,n){let[t,s]=[he(e),he(n)].sort((d,a)=>a-d);return(t+.05)/(s+.05)}function Ce(e){return Y(e,"#ffffff")>=Y(e,"#111111")?"#ffffff":"#111111"}var Z={light:{bg:"#ffffff",fg:"#14171c",muted:"#495260",border:"#c9ced6",off:"#6b7280",thumb:"#ffffff",accent:"#1d4ed8",onAccent:"#ffffff",link:"#1d4ed8"},dark:{bg:"#171a20",fg:"#f1f3f6",muted:"#b7beca",border:"#3a404b",off:"#8b93a1",thumb:"#171a20",accent:"#8ab4ff",onAccent:"#0b1220",link:"#9dc0ff"}},xe=e=>`--bg:${e.bg};--fg:${e.fg};--muted:${e.muted};--border:${e.border};--off:${e.off};--thumb:${e.thumb};--accent:${e.accent};--on-accent:${e.onAccent};--link:${e.link};--focus:${e.accent}`,ve=`
|
|
2
2
|
:host{all:initial}
|
|
3
3
|
*{box-sizing:border-box}
|
|
4
4
|
[hidden]{display:none!important}
|
|
5
|
-
.root{${
|
|
6
|
-
.root[data-theme=dark]{${
|
|
5
|
+
.root{${xe(Z.light)};position:fixed;inset:0;pointer-events:none;z-index:2147483000;font:14px/1.5 system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;color:var(--fg);-webkit-text-size-adjust:100%}
|
|
6
|
+
.root[data-theme=dark]{${xe(Z.dark)};color-scheme:dark}
|
|
7
7
|
button,input{font:inherit;color:inherit}
|
|
8
8
|
.banner,.overlay{pointer-events:auto}
|
|
9
9
|
@supports (color:color-mix(in srgb,red 50%,blue)){::selection{background:color-mix(in srgb,var(--accent) 24%,transparent)}}
|
|
@@ -86,5 +86,5 @@ input:focus-visible+.track{outline:2px solid var(--focus);outline-offset:2px}
|
|
|
86
86
|
.btn,.link-btn{padding:0 12px;min-height:40px}
|
|
87
87
|
}
|
|
88
88
|
@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation:none!important;transition:none!important}}
|
|
89
|
-
`.replace(/\n/g,"");var He='<svg width="14" height="14" viewBox="0 0 16 16" aria-hidden="true"><rect width="16" height="16" rx="4" fill="currentColor" style="color:var(--fg)"/><circle cx="8" cy="8" r="3" fill="var(--bg)"/></svg>',Re='<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 3l10 10M13 3L3 13" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none"/></svg>',Ie="button:not([disabled]),input:not([disabled]),a[href]";function xe(e){let{doc:n,config:t,texts:r,locale:p}=e,s=ye[p],l=null,b=null,g,u,f,C,v,h="none",w=null,T=null,S=new Map;function c(o,a={},...d){let y=n.createElement(o);for(let m of Object.keys(a))m==="class"?y.className=a[m]:y.setAttribute(m,a[m]);for(let m of d)y.append(m);return y}function x(o,a,d){let y=c("button",{type:"button",class:o},a);return y.addEventListener("click",d),y}function M(){let o=c("a",{href:`https://bitsnap.pl/?utm_source=consent_banner&utm_medium=referral&utm_content=${encodeURIComponent(n.location?.hostname??"")}`,target:"_blank",rel:"noopener"});return o.append(r.poweredBy+" "),o.insertAdjacentHTML("beforeend",He),o.append(c("b",{},"bitsnap")),c("div",{class:"foot"},o)}function k(){let o=c("p",{class:"desc",id:"d"},r.description);return t.privacyPolicyUrl&&o.append(" ",c("a",{href:t.privacyPolicyUrl,target:"_blank",rel:"noopener"},s.privacy)),o}function j(){v=x("link-btn",r.settings,()=>D()),u=c("div",{class:`banner ${t.position==="bottom"?"bottom":`card ${t.position}`}`,role:"region","aria-live":"polite","aria-labelledby":"t","aria-describedby":"d",tabindex:"-1"},c("div",{class:"inner"},c("div",{class:"text"},c("div",{class:"title",id:"t"},r.title),k()),c("div",{class:"actions"},x("btn",r.acceptAll,e.onAcceptAll),x("btn",r.rejectAll,e.onRejectAll),v),M()))}function U(o){let a=t.services.filter(m=>m.category===o),d=c("input",{type:"checkbox",role:"switch","aria-labelledby":`l-${o}`,"aria-describedby":`p-${o}`});o==="necessary"?(d.checked=!0,d.disabled=!0):S.set(o,d);let y=c("div",{class:"cat"},c("div",{class:"row"},c("span",{class:"cat-title",id:`l-${o}`},r[`${o}Title`]),c("span",{style:"display:flex;align-items:center"},...o==="necessary"?[c("span",{class:"always"},s.always)]:[],c("label",{class:"switch"},d,c("span",{class:"track"})))),c("p",{id:`p-${o}`},r[`${o}Description`]));return a.length>0&&y.append(c("p",{class:"svc"},c("b",{},`${s.services}: `),a.map(m=>m.name).join(", "))),y}function I(){let o=c("button",{type:"button",class:"x","aria-label":r.close});o.insertAdjacentHTML("beforeend",Re),o.addEventListener("click",()=>O());let a=c("div",{class:"d-body"},U("necessary"));for(let d of A)t.categories[d]&&a.append(U(d));C=c("div",{class:"dialog",role:"dialog","aria-modal":"true","aria-labelledby":"st",tabindex:"-1"},c("div",{class:"d-head"},c("div",{class:"title",id:"st"},r.settings),o),a,c("div",{class:"d-foot"},c("div",{class:"actions"},x("btn",r.save,()=>e.onSave(N())),x("btn",r.acceptAll,e.onAcceptAll),x("btn",r.rejectAll,e.onRejectAll)),M())),f=c("div",{class:"overlay"},C),f.addEventListener("mousedown",d=>{d.target===f&&O()})}function N(){let o={analytics:!1,marketing:!1,preferences:!1};return S.forEach((a,d)=>o[d]=a.checked),o}function E(){let o=t.theme==="dark"||t.theme==="auto"&&!!T?.matches;g.setAttribute("data-theme",o?"dark":"light"),$(o)}function $(o){let a=t.accentColor;if(a){if(o&&q(a,Y.dark.bg)<3){for(let d of["--accent","--focus","--on-accent"])g.style.removeProperty(d);return}g.style.setProperty("--accent",a),g.style.setProperty("--focus",a),g.style.setProperty("--on-accent",be(a))}}function B(){l||(l=c("div",{id:"bitsnap-consent","data-bitsnap-consent-host":""}),b=l.attachShadow({mode:"open"}),b.append(c("style",{},Ce)),g=c("div",{class:"root",lang:p}),j(),I(),u.hidden=!0,f.hidden=!0,g.append(u,f),b.append(g),typeof n.defaultView?.matchMedia=="function"&&(T=n.defaultView.matchMedia("(prefers-color-scheme: dark)"),T.addEventListener?.("change",E)),E(),n.body.append(l))}function L(){return Array.from(C.querySelectorAll(Ie))}function P(o){if(h!=="settings")return;if(o.key==="Escape"){o.preventDefault(),O();return}if(o.key!=="Tab")return;let a=L();if(a.length===0)return;let d=b?.activeElement,y=a[0],m=a[a.length-1],W=!d||!C.contains(d);o.shiftKey&&(W||d===y||d===C)?(o.preventDefault(),m.focus()):!o.shiftKey&&(W||d===m)&&(o.preventDefault(),y.focus())}function K(o=!1){try{B(),h="banner",n.removeEventListener("keydown",P,!0),f.hidden=!0,u.hidden=!1,o&&u.focus({preventScroll:!0})}catch{}}function D(){try{if(B(),h!=="settings"){let a=n.activeElement;w=a&&a!==l&&a!==n.body?a:null}let o=e.getChoices();S.forEach((a,d)=>a.checked=!!o?.[d]),h="settings",u.hidden=!0,f.hidden=!1,n.addEventListener("keydown",P,!0),C.focus({preventScroll:!0})}catch{}}function O(){h==="settings"&&(n.removeEventListener("keydown",P,!0),f.hidden=!0,e.hasChoice()?(h="none",w?.focus?.({preventScroll:!0})):(h="banner",u.hidden=!1,v.focus({preventScroll:!0})))}function G(){if(!l)return;n.removeEventListener("keydown",P,!0);let o=h==="settings";h="none",u.hidden=!0,f.hidden=!0,o&&w?.focus?.({preventScroll:!0})}function i(){n.removeEventListener("keydown",P,!0),T?.removeEventListener?.("change",E),l?.remove(),l=null,b=null,h="none"}return{showBanner:K,showSettings:D,hide:G,destroy:i}}var _=()=>({analytics:!1,marketing:!1,preferences:!1}),ve=e=>e&&{id:e.id,ts:e.ts,bv:e.bv,c:{...e.c}};function Z(e={}){let n=typeof window<"u"?window:void 0,t=n?.document,r=new Set,p=e.apiHost||pe,s=e.collectHost||fe,l=e.projectKey?te(e.projectKey):null,b=ne(l??void 0,e.scope),g=!!e.preview,u=H(M()),f=null,C=null,v=null,h=!1,w=!1,T,S=[],c=n&&!g?k(()=>ge(n,e.gcm),null):null;k(()=>c?.setDefault(),void 0);let x=t&&!g?ie(t,b):null;function M(i){let o={...e.config};return e.theme&&(o.theme=e.theme),e.position&&(o.position=e.position),e.privacyPolicyUrl&&(o.privacyPolicyUrl=e.privacyPolicyUrl),{...i,...o}}function k(i,o){try{return i()}catch{return o}}function j(i){let o=_();for(let a of A)o[a]=!!i?.[a]&&u.categories[a];return o}function U(i){r.forEach(o=>k(()=>o({...i,c:{...i.c}}),void 0)),k(()=>{n?.dispatchEvent(new n.CustomEvent("bitsnap:consent",{detail:{...i,c:{...i.c}}}))},void 0)}function I(i,o){g||!l||typeof fetch!="function"||k(()=>{let a=JSON.stringify({k:l,consent_id:o.id,action:i,c:o.c,bv:o.bv,host:n?.location?.hostname??"",ts:o.ts});fetch(`${s.replace(/\/+$/,"")}/consent`,{method:"POST",keepalive:!0,credentials:"omit",body:a}).catch(()=>{})},void 0)}function N(i){return A.filter(o=>i[o]&&u.categories[o])}function E(i,o){if(!t||w)return;if(g){v?.hide();return}let a=f??C,d=a?N(a.c).filter(W=>!i[W]):[],y=d.length>0?"withdraw":o,m={id:a?.id??oe(),ts:Math.floor(Date.now()/1e3),bv:u.version,c:i};re(t,b,m),f=m,C=null,$(m.c),d.length>0&&J(t,u.services,d),v?.hide(),U({...m,action:y}),I(y,m),d.length>0&&e.reloadOnWithdraw&&B()}function $(i){k(()=>c?.update(i),void 0),t&&de(t,i)}function B(){setTimeout(()=>k(()=>n?.location.reload(),void 0),50)}function L(){if(!t||!h||w)return null;if(!v){let i=F(e.locale,n?.navigator?.language);v=xe({doc:t,config:u,locale:i,texts:P(X(i,u.texts)),getChoices:()=>f?.c??null,hasChoice:()=>f!==null,onAcceptAll:()=>E(j({analytics:!0,marketing:!0,preferences:!0}),"accept_all"),onRejectAll:()=>E(_(),"reject_all"),onSave:o=>E(j(o),"custom")})}return v}function P(i){let o=e.title?.trim(),a=e.description?.trim();return{...i,...o&&{title:o},...a&&{description:a}}}function K(i){i.target?.closest?.("[data-bitsnap-consent-open]")&&(i.preventDefault(),G.open())}let D=(async()=>{if(!t||!n)return null;let i=l&&!g?await ue(p,l):null;if(w)return null;let o=i?.config?void 0:e.categories;if(u=H(i?.config,o?{categories:{..._(),...o}}:void 0,M()),t.readyState==="loading"&&await new Promise(y=>t.addEventListener("DOMContentLoaded",()=>y(),{once:!0})),w)return null;h=!0,t.addEventListener("click",K),T=()=>t.removeEventListener("click",K);let a=!l||i?.config!=null,d=i?.enabled===!1;return g?L()?.showBanner():x&&(d||!a||x.bv===u.version)?(f=x,$(j(f.c))):(x&&(C=x),d||L()?.showBanner()),S.splice(0).forEach(y=>k(y,void 0)),k(()=>n.dispatchEvent(new n.CustomEvent("bitsnap:consent:ready")),void 0),ve(f)})().catch(()=>null);function O(i){h?i():S.push(i)}let G={scope:e.scope==="project"&&l?"project":"global",projectKey:l,get:()=>ve(f),denied:i=>(f??C)?.c[i]===!1,on(i,o){return i!=="change"||typeof o!="function"?()=>{}:(r.add(o),()=>void r.delete(o))},open(){h?L()?.showSettings():D.then(()=>L()?.showSettings())},close(){v?.hide()},accept:i=>O(()=>E(j(i),"custom")),acceptAll:()=>O(()=>E(j({analytics:!0,marketing:!0,preferences:!0}),"accept_all")),rejectAll:()=>O(()=>E(_(),"reject_all")),reset(){if(!t||w)return;if(g){L()?.showBanner();return}let i=f??C,o=i?N(i.c):[];se(t,b),f=null,C=i,k(()=>c?.update(_()),void 0),o.length>0&&J(t,u.services,o),U({id:i?.id??"",ts:Math.floor(Date.now()/1e3),bv:u.version,c:_(),action:"reset"}),i&&o.length>0&&I("withdraw",{id:i.id,ts:Math.floor(Date.now()/1e3),bv:u.version,c:_()}),L()?.showBanner(!0),o.length>0&&e.reloadOnWithdraw&&B()},ready:D,destroy(){w=!0,T?.(),v?.destroy(),v=null,r.clear()}};return Ne(G)}function Ne(e){let n={...e};for(let t of Object.keys(e)){let r=e[t];typeof r=="function"&&(n[t]=(...p)=>{try{return r(...p)}catch{return t==="get"?null:t==="on"?()=>{}:void 0}})}return n}function Ke(e){let{projectKey:n,theme:t,locale:r,position:p,privacyPolicyUrl:s,scope:l,apiHost:b,collectHost:g,reloadOnWithdraw:u,gcm:f,preview:C,title:v,description:h}=e,w=JSON.stringify(e.config??null),T=we(e.config);T.current=e.config;let S=JSON.stringify(e.categories??null),c=we(e.categories);return c.current=e.categories,ae(()=>{if(window.BitsnapConsent)return;let x=Z({projectKey:n,theme:t,locale:r,position:p,privacyPolicyUrl:s,scope:l,apiHost:b,collectHost:g,reloadOnWithdraw:u,gcm:f,preview:C,title:v,description:h,categories:c.current,config:T.current});return window.BitsnapConsent=x,()=>{x.destroy(),window.BitsnapConsent===x&&delete window.BitsnapConsent}},[n,t,r,p,s,l,b,g,u,f,C,v,h,S,w]),null}var ke="null",Te=null;function Ge(){let e=typeof window>"u"?null:window.BitsnapConsent?.get()??null,n=JSON.stringify(e);return n!==ke&&(ke=n,Te=e),Te}function We(e){return window.addEventListener("bitsnap:consent",e),window.addEventListener("bitsnap:consent:ready",e),()=>{window.removeEventListener("bitsnap:consent",e),window.removeEventListener("bitsnap:consent:ready",e)}}function Fe(){let e=$e(We,Ge,()=>null),n=R(()=>window.BitsnapConsent?.open(),[]),t=R(l=>window.BitsnapConsent?.accept(l),[]),r=R(()=>window.BitsnapConsent?.acceptAll(),[]),p=R(()=>window.BitsnapConsent?.rejectAll(),[]),s=R(()=>window.BitsnapConsent?.reset(),[]);return{consent:e,decided:e!==null,open:n,accept:t,acceptAll:r,rejectAll:p,reset:s}}export{Ke as BitsnapConsent,ee as DEFAULT_TEXTS,Z as createConsent,V as defaultConfig,H as mergeConfig,X as mergeTexts,F as resolveLocale,Q as toConsentModeUpdate,Fe as useConsent};
|
|
89
|
+
`.replace(/\n/g,"");var $e='<svg width="14" height="14" viewBox="0 0 16 16" aria-hidden="true"><rect width="16" height="16" rx="4" fill="currentColor" style="color:var(--fg)"/><circle cx="8" cy="8" r="3" fill="var(--bg)"/></svg>',Ke='<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 3l10 10M13 3L3 13" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none"/></svg>',We="button:not([disabled]),input:not([disabled]),a[href]";function we(e){let{doc:n,config:t,texts:s,locale:d}=e,a=be[d],c=null,m=null,g,u,f,h,x,b="none",v=null,k=null,S=new Map;function l(r,o={},...i){let p=n.createElement(r);for(let y of Object.keys(o))y==="class"?p.className=o[y]:p.setAttribute(y,o[y]);for(let y of i)p.append(y);return p}function C(r,o,i){let p=l("button",{type:"button",class:r},o);return p.addEventListener("click",i),p}function M(){let r=l("a",{href:`https://bitsnap.pl/?utm_source=consent_banner&utm_medium=referral&utm_content=${encodeURIComponent(n.location?.hostname??"")}`,target:"_blank",rel:"noopener"});return r.append(s.poweredBy+" "),r.insertAdjacentHTML("beforeend",$e),r.append(l("b",{},"bitsnap")),l("div",{class:"foot"},r)}function w(){let r=l("p",{class:"desc",id:"d"},s.description);return t.privacyPolicyUrl&&r.append(" ",l("a",{href:t.privacyPolicyUrl,target:"_blank",rel:"noopener"},a.privacy)),r}function P(){x=C("link-btn",s.settings,()=>G()),u=l("div",{class:`banner ${t.position==="bottom"?"bottom":`card ${t.position}`}`,role:"region","aria-live":"polite","aria-labelledby":"t","aria-describedby":"d",tabindex:"-1"},l("div",{class:"inner"},l("div",{class:"text"},l("div",{class:"title",id:"t"},s.title),w()),l("div",{class:"actions"},C("btn",s.acceptAll,e.onAcceptAll),C("btn",s.rejectAll,e.onRejectAll),x),M()))}function U(r){let o=t.services.filter(y=>y.category===r),i=l("input",{type:"checkbox",role:"switch","aria-labelledby":`l-${r}`,"aria-describedby":`p-${r}`});r==="necessary"?(i.checked=!0,i.disabled=!0):S.set(r,i);let p=l("div",{class:"cat"},l("div",{class:"row"},l("span",{class:"cat-title",id:`l-${r}`},s[`${r}Title`]),l("span",{style:"display:flex;align-items:center"},...r==="necessary"?[l("span",{class:"always"},a.always)]:[],l("label",{class:"switch"},i,l("span",{class:"track"})))),l("p",{id:`p-${r}`},s[`${r}Description`]));return o.length>0&&p.append(l("p",{class:"svc"},l("b",{},`${a.services}: `),o.map(y=>y.name).join(", "))),p}function I(){let r=l("button",{type:"button",class:"x","aria-label":s.close});r.insertAdjacentHTML("beforeend",Ke),r.addEventListener("click",()=>_());let o=l("div",{class:"d-body"},U("necessary"));for(let i of T)t.categories[i]&&o.append(U(i));h=l("div",{class:"dialog",role:"dialog","aria-modal":"true","aria-labelledby":"st",tabindex:"-1"},l("div",{class:"d-head"},l("div",{class:"title",id:"st"},s.settings),r),o,l("div",{class:"d-foot"},l("div",{class:"actions"},C("btn",s.save,()=>e.onSave($())),C("btn",s.acceptAll,e.onAcceptAll),C("btn",s.rejectAll,e.onRejectAll)),M())),f=l("div",{class:"overlay"},h),f.addEventListener("mousedown",i=>{i.target===f&&_()})}function $(){let r={analytics:!1,marketing:!1,preferences:!1};return S.forEach((o,i)=>r[i]=o.checked),r}function B(){let r=t.theme==="dark"||t.theme==="auto"&&!!k?.matches;g.setAttribute("data-theme",r?"dark":"light"),K(r)}function K(r){let o=t.accentColor;if(o){if(r&&Y(o,Z.dark.bg)<3){for(let i of["--accent","--focus","--on-accent"])g.style.removeProperty(i);return}g.style.setProperty("--accent",o),g.style.setProperty("--focus",o),g.style.setProperty("--on-accent",Ce(o))}}function E(){c||(c=l("div",{id:"bitsnap-consent","data-bitsnap-consent-host":""}),m=c.attachShadow({mode:"open"}),m.append(l("style",{},ve)),g=l("div",{class:"root",lang:d}),P(),I(),u.hidden=!0,f.hidden=!0,g.append(u,f),m.append(g),typeof n.defaultView?.matchMedia=="function"&&(k=n.defaultView.matchMedia("(prefers-color-scheme: dark)"),k.addEventListener?.("change",B)),B(),n.body.append(c))}function W(){return Array.from(h.querySelectorAll(We))}function L(r){if(b!=="settings")return;if(r.key==="Escape"){r.preventDefault(),_();return}if(r.key!=="Tab")return;let o=W();if(o.length===0)return;let i=m?.activeElement,p=o[0],y=o[o.length-1],A=!i||!h.contains(i);r.shiftKey&&(A||i===p||i===h)?(r.preventDefault(),y.focus()):!r.shiftKey&&(A||i===y)&&(r.preventDefault(),p.focus())}function j(r=!1){try{E(),b="banner",n.removeEventListener("keydown",L,!0),f.hidden=!0,u.hidden=!1,r&&u.focus({preventScroll:!0})}catch{}}function G(){try{if(E(),b!=="settings"){let o=n.activeElement;v=o&&o!==c&&o!==n.body?o:null}let r=e.getChoices();S.forEach((o,i)=>o.checked=!!r?.[i]),b="settings",u.hidden=!0,f.hidden=!1,n.addEventListener("keydown",L,!0),h.focus({preventScroll:!0})}catch{}}function _(){b==="settings"&&(n.removeEventListener("keydown",L,!0),f.hidden=!0,e.hasChoice()?(b="none",v?.focus?.({preventScroll:!0})):(b="banner",u.hidden=!1,x.focus({preventScroll:!0})))}function F(){if(!c)return;n.removeEventListener("keydown",L,!0);let r=b==="settings";b="none",u.hidden=!0,f.hidden=!0,r&&v?.focus?.({preventScroll:!0})}function D(){n.removeEventListener("keydown",L,!0),k?.removeEventListener?.("change",B),c?.remove(),c=null,m=null,b="none"}return{showBanner:j,showSettings:G,hide:F,destroy:D}}var O=()=>({analytics:!1,marketing:!1,preferences:!1}),ke=e=>e&&{id:e.id,ts:e.ts,bv:e.bv,c:{...e.c}},Te=new WeakMap;function J(e={}){let n=typeof window<"u"?window:void 0,t=n?.document,s=new Set,d=e.apiHost||ue,a=e.collectHost||ge,c=e.projectKey?oe(e.projectKey):null,m=ie(c??void 0,e.scope),g=!!e.preview,u=R(M()),f=null,h=null,x=null,b=!1,v=!1,k,S=[],l=n&&!g?w(()=>me(n,e.gcm),null):null;w(()=>l?.setDefault(),void 0);let C=t&&!g?se(t,m):null;function M(o){let i={...e.config};return e.theme&&(i.theme=e.theme),e.position&&(i.position=e.position),e.privacyPolicyUrl&&(i.privacyPolicyUrl=e.privacyPolicyUrl),{...o,...i}}function w(o,i){try{return o()}catch{return i}}function P(o){let i=O();for(let p of T)i[p]=!!o?.[p]&&u.categories[p];return i}function U(o){s.forEach(i=>w(()=>i({...o,c:{...o.c}}),void 0)),w(()=>{n?.dispatchEvent(new n.CustomEvent("bitsnap:consent",{detail:{...o,c:{...o.c}}}))},void 0)}function I(o){g||!c||typeof fetch!="function"||w(()=>{fetch(`${a.replace(/\/+$/,"")}/consent`,{method:"POST",keepalive:!0,credentials:"omit",body:JSON.stringify({k:c,host:n?.location?.hostname??"",...o})}).catch(()=>{})},void 0)}function $(o,i){I({consent_id:i.id,action:o,c:i.c,bv:i.bv,ts:i.ts})}function B(){if(!n||!c)return;let o=Te.get(n)??new Set;o.has(c)||(Te.set(n,o.add(c)),I({action:ne,bv:u.version,ts:Math.floor(Date.now()/1e3)}))}function K(o){return T.filter(i=>o[i]&&u.categories[i])}function E(o,i){if(!t||v)return;if(g){x?.hide();return}let p=f??h,y=p?K(p.c).filter(Le=>!o[Le]):[],A=y.length>0?"withdraw":i,H={id:p?.id??re(),ts:Math.floor(Date.now()/1e3),bv:u.version,c:o};ae(t,m,H),f=H,h=null,W(H.c),y.length>0&&V(t,u.services,y),x?.hide(),U({...H,action:A}),$(A,H),y.length>0&&e.reloadOnWithdraw&&L()}function W(o){w(()=>l?.update(o),void 0),t&&fe(t,o)}function L(){setTimeout(()=>w(()=>n?.location.reload(),void 0),50)}function j(){if(!t||!b||v)return null;if(!x){let o=X(e.locale,n?.navigator?.language);x=we({doc:t,config:u,locale:o,texts:G(q(o,u.texts)),getChoices:()=>f?.c??null,hasChoice:()=>f!==null,onAcceptAll:()=>E(P({analytics:!0,marketing:!0,preferences:!0}),"accept_all"),onRejectAll:()=>E(O(),"reject_all"),onSave:i=>E(P(i),"custom")})}return x}function G(o){let i=e.title?.trim(),p=e.description?.trim();return{...o,...i&&{title:i},...p&&{description:p}}}function _(o){o.target?.closest?.("[data-bitsnap-consent-open]")&&(o.preventDefault(),r.open())}let F=(async()=>{if(!t||!n)return null;let o=c&&!g?await ye(d,c):null;if(v)return null;let i=o?.config?void 0:e.categories;if(u=R(o?.config,i?{categories:{...O(),...i}}:void 0,M()),t.readyState==="loading"&&await new Promise(A=>t.addEventListener("DOMContentLoaded",()=>A(),{once:!0})),v)return null;b=!0,t.addEventListener("click",_),k=()=>t.removeEventListener("click",_);let p=!c||o?.config!=null,y=o?.enabled===!1;return g?j()?.showBanner():C&&(y||!p||C.bv===u.version)?(f=C,W(P(f.c))):(C&&(h=C),y||j()?.showBanner()),S.splice(0).forEach(A=>w(A,void 0)),!v&&!g&&!y&&f===null&&B(),w(()=>n.dispatchEvent(new n.CustomEvent("bitsnap:consent:ready")),void 0),ke(f)})().catch(()=>null);function D(o){b?o():S.push(o)}let r={scope:e.scope==="project"&&c?"project":"global",projectKey:c,get:()=>ke(f),denied:o=>(f??h)?.c[o]===!1,on(o,i){return o!=="change"||typeof i!="function"?()=>{}:(s.add(i),()=>void s.delete(i))},open(){b?j()?.showSettings():F.then(()=>j()?.showSettings())},close(){x?.hide()},accept:o=>D(()=>E(P(o),"custom")),acceptAll:()=>D(()=>E(P({analytics:!0,marketing:!0,preferences:!0}),"accept_all")),rejectAll:()=>D(()=>E(O(),"reject_all")),reset(){if(!t||v)return;if(g){j()?.showBanner();return}let o=f??h,i=o?K(o.c):[];ce(t,m),f=null,h=o,w(()=>l?.update(O()),void 0),i.length>0&&V(t,u.services,i),U({id:o?.id??"",ts:Math.floor(Date.now()/1e3),bv:u.version,c:O(),action:"reset"}),o&&i.length>0&&$("withdraw",{id:o.id,ts:Math.floor(Date.now()/1e3),bv:u.version,c:O()}),j()?.showBanner(!0),i.length>0&&e.reloadOnWithdraw&&L()},ready:F,destroy(){v=!0,k?.(),x?.destroy(),x=null,s.clear()}};return Ge(r)}function Ge(e){let n={...e};for(let t of Object.keys(e)){let s=e[t];typeof s=="function"&&(n[t]=(...d)=>{try{return s(...d)}catch{return t==="get"?null:t==="on"?()=>{}:void 0}})}return n}function Xe(e){let{projectKey:n,theme:t,locale:s,position:d,privacyPolicyUrl:a,scope:c,apiHost:m,collectHost:g,reloadOnWithdraw:u,gcm:f,preview:h,title:x,description:b}=e,v=JSON.stringify(e.config??null),k=Ee(e.config);k.current=e.config;let S=JSON.stringify(e.categories??null),l=Ee(e.categories);return l.current=e.categories,le(()=>{if(window.BitsnapConsent)return;let C=J({projectKey:n,theme:t,locale:s,position:d,privacyPolicyUrl:a,scope:c,apiHost:m,collectHost:g,reloadOnWithdraw:u,gcm:f,preview:h,title:x,description:b,categories:l.current,config:k.current});return window.BitsnapConsent=C,()=>{C.destroy(),window.BitsnapConsent===C&&delete window.BitsnapConsent}},[n,t,s,d,a,c,m,g,u,f,h,x,b,S,v]),null}var Ae="null",Se=null;function qe(){let e=typeof window>"u"?null:window.BitsnapConsent?.get()??null,n=JSON.stringify(e);return n!==Ae&&(Ae=n,Se=e),Se}function Ye(e){return window.addEventListener("bitsnap:consent",e),window.addEventListener("bitsnap:consent:ready",e),()=>{window.removeEventListener("bitsnap:consent",e),window.removeEventListener("bitsnap:consent:ready",e)}}function Ze(){let e=Fe(Ye,qe,()=>null),n=N(()=>window.BitsnapConsent?.open(),[]),t=N(c=>window.BitsnapConsent?.accept(c),[]),s=N(()=>window.BitsnapConsent?.acceptAll(),[]),d=N(()=>window.BitsnapConsent?.rejectAll(),[]),a=N(()=>window.BitsnapConsent?.reset(),[]);return{consent:e,decided:e!==null,open:n,accept:t,acceptAll:s,rejectAll:d,reset:a}}export{Xe as BitsnapConsent,te as DEFAULT_TEXTS,J as createConsent,Q as defaultConfig,R as mergeConfig,q as mergeTexts,X as resolveLocale,ee as toConsentModeUpdate,Ze as useConsent};
|
|
90
90
|
//# sourceMappingURL=consent.mjs.map
|