safebanner 0.1.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/LICENSE +21 -0
- package/README.md +89 -0
- package/dist/safebanner.js +204 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 SafeBanner
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# safebanner
|
|
2
|
+
|
|
3
|
+
Lightweight cookie consent banner with Google Consent Mode v2. One script tag, ~6kb gzipped, zero dependencies.
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[]()
|
|
7
|
+
|
|
8
|
+
> **This package ships a pre-built browser script, not an importable module.**
|
|
9
|
+
> Use it via CDN or copy `dist/safebanner.js` to your own host.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### CDN (recommended)
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<script src="https://www.safebanner.com/safebanner.js"></script>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Self-hosted
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install safebanner
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then copy `node_modules/safebanner/dist/safebanner.js` to your public folder and serve it yourself.
|
|
26
|
+
|
|
27
|
+
### Google Consent Mode v2 — load order matters
|
|
28
|
+
|
|
29
|
+
If you use Google tags, SafeBanner **must load before them**. Place it in `<head>` above your Google tag scripts:
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<head>
|
|
33
|
+
<!-- 1. SafeBanner FIRST -->
|
|
34
|
+
<script src="https://www.safebanner.com/safebanner.js"></script>
|
|
35
|
+
|
|
36
|
+
<!-- 2. Google tag AFTER -->
|
|
37
|
+
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXX"></script>
|
|
38
|
+
</head>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
```html
|
|
44
|
+
<script
|
|
45
|
+
src="https://www.safebanner.com/safebanner.js"
|
|
46
|
+
data-position="bottom-right"
|
|
47
|
+
data-theme="dark"
|
|
48
|
+
data-color="#8b5cf6"
|
|
49
|
+
data-company="Acme Inc"
|
|
50
|
+
data-privacy="https://acme.com/privacy"
|
|
51
|
+
data-lang="en"
|
|
52
|
+
data-google-consent="advanced"
|
|
53
|
+
></script>
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
| Attribute | Values | Default | Notes |
|
|
57
|
+
|-----------|--------|---------|-------|
|
|
58
|
+
| `data-position` | `bottom`, `top`, `bottom-left`, `bottom-right` | `bottom` | |
|
|
59
|
+
| `data-theme` | `light`, `dark`, `auto` | `light` | `auto` requires Pro |
|
|
60
|
+
| `data-color` | Any hex color | `#2563eb` | |
|
|
61
|
+
| `data-company` | Your company name | — | |
|
|
62
|
+
| `data-privacy` | URL to privacy policy | — | |
|
|
63
|
+
| `data-lang` | `en`, `fr`, `de` (free) · 40+ with Pro | `en` | |
|
|
64
|
+
| `data-google-consent` | `advanced`, `basic`, `off` | `advanced` | |
|
|
65
|
+
| `data-project-key` | Pro license key | — | Unlocks Pro features |
|
|
66
|
+
| `data-layout` | `bar`, `card` | — | Pro only |
|
|
67
|
+
| `data-logo` | Image URL | — | Pro only |
|
|
68
|
+
| `data-button-style` | `rounded`, `square`, `pill` | — | Pro only |
|
|
69
|
+
| `data-banner-title` | string | — | Pro only |
|
|
70
|
+
| `data-banner-description` | string | — | Pro only |
|
|
71
|
+
|
|
72
|
+
## JavaScript API
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
window.safeBanner.hasConsentFor('analytics') // true/false
|
|
76
|
+
window.safeBanner.getConsent() // { necessary, analytics, marketing, timestamp }
|
|
77
|
+
window.safeBanner.updateConsent({ analytics: false })
|
|
78
|
+
window.safeBanner.reset() // clears consent, shows banner again
|
|
79
|
+
window.safeBanner.show()
|
|
80
|
+
window.safeBanner.hide()
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Pro
|
|
84
|
+
|
|
85
|
+
Pro removes the "Powered by SafeBanner" badge, unlocks visual customization, 40+ languages, and includes a commercial license for client work. $15/mo or $149/yr at [safebanner.com/upgrade](https://safebanner.com/upgrade).
|
|
86
|
+
|
|
87
|
+
## License
|
|
88
|
+
|
|
89
|
+
MIT
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";(()=>{var w="safebanner_consent";function d(){try{let e=localStorage.getItem(w);return e?JSON.parse(e):null}catch{return null}}function m(e){try{localStorage.setItem(w,JSON.stringify(e))}catch{}}function M(){try{localStorage.removeItem(w)}catch{}}function C(){return d()!==null}var ue=[{pattern:/^_ga/,category:"analytics"},{pattern:/^_gid/,category:"analytics"},{pattern:/^_gat/,category:"analytics"},{pattern:/^__utm/,category:"analytics"},{pattern:/^_hjid/,category:"analytics"},{pattern:/^mp_/,category:"analytics"},{pattern:/^amplitude/,category:"analytics"},{pattern:/^plausible/,category:"analytics"},{pattern:/^_fbp/,category:"marketing"},{pattern:/^_fbc/,category:"marketing"},{pattern:/^fr$/,category:"marketing"},{pattern:/^_gcl/,category:"marketing"},{pattern:/^_pinterest/,category:"marketing"},{pattern:/^_tt_/,category:"marketing"},{pattern:/^li_/,category:"marketing"},{pattern:/^IDE$/,category:"marketing"},{pattern:/^ads$/,category:"marketing"}];function k(){let e=document.cookie.split(";").map(n=>n.trim().split("=")[0]),t=[];for(let n of e){if(!n)continue;let a="necessary";for(let{pattern:o,category:r}of ue)if(o.test(n)){a=r;break}t.push({name:n,category:a})}return t}function E(e){let n=k().filter(a=>a.category===e);for(let a of n)document.cookie=`${a.name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`,document.cookie=`${a.name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain=${window.location.hostname}`}function ge(e){let t=e.primaryColor||"#2563eb",n=e.position==="top",a=e.position?.includes("-"),o=e.theme==="auto",r=e.theme==="dark",s=e.layout==="bar",i=e.layout==="card",l=e.offset??16,p=e.maxWidth,h=a||i?16:0,j=e.borderRadius??h,te=e.buttonStyle==="pill"?9999:e.buttonStyle==="square"?0:e.borderRadius!=null?Math.max(4,Math.round(e.borderRadius*.5)):6,ne=r?"#1f2937":"#ffffff",ae=r?"#f9fafb":"#111827",S=r?"#d1d5db":"#6b7280",oe=r?"#374151":"#f3f4f6",re=r?"#f9fafb":"#374151",R=r?"#6b7280":"#9ca3af",y;if(i)y=`
|
|
2
|
+
left: 50%;
|
|
3
|
+
bottom: ${l}px;
|
|
4
|
+
max-width: ${p??480}px;
|
|
5
|
+
width: calc(100% - ${l*2}px);
|
|
6
|
+
transform: translateX(-50%) translateY(12px);
|
|
7
|
+
`;else if(a){let de=e.position?.includes("right")?`right: ${l}px;`:`left: ${l}px;`,pe=n?`top: ${l}px;`:`bottom: ${l}px;`;y=`${de} ${pe} max-width: 400px;`}else n?y=`top: 0; left: 0; right: 0;
|
|
8
|
+
${p?`max-width: ${p}px; margin-left: auto; margin-right: auto;`:""}`:y=`bottom: 0; left: 0; right: 0;
|
|
9
|
+
${p?`max-width: ${p}px; margin-left: auto; margin-right: auto;`:""}`;let ie=i?"transform: translateX(-50%) translateY(12px);":`transform: translateY(${n?"-8px":"8px"});`,se=i?"transform: translateX(-50%) translateY(0);":"transform: translateY(0);",ce=o?`
|
|
10
|
+
@media (prefers-color-scheme: dark) {
|
|
11
|
+
.cm-banner {
|
|
12
|
+
background: #1f2937 !important;
|
|
13
|
+
color: #f9fafb !important;
|
|
14
|
+
}
|
|
15
|
+
.cm-text { color: #d1d5db !important; }
|
|
16
|
+
.cm-btn-secondary { background: #374151 !important; color: #f9fafb !important; }
|
|
17
|
+
.cm-btn-link { color: #d1d5db !important; }
|
|
18
|
+
.cm-label-required { color: #9ca3af !important; }
|
|
19
|
+
.cm-powered-by, .cm-powered-by a { color: #6b7280 !important; }
|
|
20
|
+
.cm-bar-text { color: #d1d5db !important; }
|
|
21
|
+
}
|
|
22
|
+
`:"",le=s?`
|
|
23
|
+
.cm-banner {
|
|
24
|
+
padding: 0 24px;
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: space-between;
|
|
28
|
+
gap: 16px;
|
|
29
|
+
min-height: 60px;
|
|
30
|
+
flex-wrap: wrap;
|
|
31
|
+
}
|
|
32
|
+
.cm-bar-text {
|
|
33
|
+
font-size: 13px;
|
|
34
|
+
color: ${S};
|
|
35
|
+
flex: 1;
|
|
36
|
+
min-width: 160px;
|
|
37
|
+
}
|
|
38
|
+
.cm-buttons {
|
|
39
|
+
flex-wrap: nowrap;
|
|
40
|
+
gap: 8px;
|
|
41
|
+
}
|
|
42
|
+
.cm-btn {
|
|
43
|
+
padding: 7px 14px;
|
|
44
|
+
font-size: 13px;
|
|
45
|
+
white-space: nowrap;
|
|
46
|
+
}
|
|
47
|
+
.cm-btn-link {
|
|
48
|
+
padding: 7px 8px;
|
|
49
|
+
}
|
|
50
|
+
`:"";return`
|
|
51
|
+
.cm-overlay {
|
|
52
|
+
position: fixed;
|
|
53
|
+
inset: 0;
|
|
54
|
+
background: rgba(0, 0, 0, 0.4);
|
|
55
|
+
z-index: 99998;
|
|
56
|
+
opacity: 0;
|
|
57
|
+
transition: opacity 0.2s ease;
|
|
58
|
+
}
|
|
59
|
+
.cm-overlay.cm-visible {
|
|
60
|
+
opacity: 1;
|
|
61
|
+
}
|
|
62
|
+
${s?".cm-overlay { display: none; }":""}
|
|
63
|
+
|
|
64
|
+
.cm-banner {
|
|
65
|
+
position: fixed;
|
|
66
|
+
${y}
|
|
67
|
+
background: ${ne};
|
|
68
|
+
color: ${ae};
|
|
69
|
+
padding: 24px 28px;
|
|
70
|
+
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.18), 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
71
|
+
z-index: 99999;
|
|
72
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
73
|
+
font-size: 14px;
|
|
74
|
+
line-height: 1.5;
|
|
75
|
+
${j>0?`border-radius: ${j}px;`:""}
|
|
76
|
+
opacity: 0;
|
|
77
|
+
${ie}
|
|
78
|
+
transition: opacity 0.25s ease, transform 0.25s ease;
|
|
79
|
+
}
|
|
80
|
+
.cm-banner.cm-visible {
|
|
81
|
+
opacity: 1;
|
|
82
|
+
${se}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.cm-title {
|
|
86
|
+
font-size: 16px;
|
|
87
|
+
font-weight: 600;
|
|
88
|
+
margin: 0 0 8px 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.cm-text {
|
|
92
|
+
margin: 0 0 16px 0;
|
|
93
|
+
color: ${S};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.cm-link {
|
|
97
|
+
color: ${t};
|
|
98
|
+
text-decoration: none;
|
|
99
|
+
}
|
|
100
|
+
.cm-link:hover {
|
|
101
|
+
text-decoration: underline;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.cm-buttons {
|
|
105
|
+
display: flex;
|
|
106
|
+
gap: 12px;
|
|
107
|
+
flex-wrap: wrap;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.cm-btn {
|
|
111
|
+
padding: 10px 20px;
|
|
112
|
+
border-radius: ${te}px;
|
|
113
|
+
font-size: 14px;
|
|
114
|
+
font-weight: 500;
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
border: none;
|
|
117
|
+
transition: opacity 0.2s ease;
|
|
118
|
+
}
|
|
119
|
+
.cm-btn:hover {
|
|
120
|
+
opacity: 0.9;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.cm-btn-primary {
|
|
124
|
+
background: ${t};
|
|
125
|
+
color: #ffffff;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.cm-btn-secondary {
|
|
129
|
+
background: ${oe};
|
|
130
|
+
color: ${re};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.cm-btn-link {
|
|
134
|
+
background: transparent;
|
|
135
|
+
color: ${S};
|
|
136
|
+
padding: 10px 12px;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.cm-categories {
|
|
140
|
+
margin: 16px 0;
|
|
141
|
+
display: flex;
|
|
142
|
+
flex-direction: column;
|
|
143
|
+
gap: 8px;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.cm-category {
|
|
147
|
+
display: flex;
|
|
148
|
+
align-items: center;
|
|
149
|
+
gap: 8px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.cm-checkbox {
|
|
153
|
+
width: 18px;
|
|
154
|
+
height: 18px;
|
|
155
|
+
accent-color: ${t};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.cm-checkbox:disabled {
|
|
159
|
+
opacity: 0.5;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.cm-label {
|
|
163
|
+
display: flex;
|
|
164
|
+
align-items: center;
|
|
165
|
+
gap: 8px;
|
|
166
|
+
cursor: pointer;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.cm-label-text {
|
|
170
|
+
font-weight: 500;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.cm-label-required {
|
|
174
|
+
font-size: 12px;
|
|
175
|
+
color: #9ca3af;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.cm-powered-by {
|
|
179
|
+
margin-top: 12px;
|
|
180
|
+
font-size: 11px;
|
|
181
|
+
color: ${R};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.cm-powered-by a {
|
|
185
|
+
color: ${R};
|
|
186
|
+
text-decoration: none;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.cm-powered-by a:hover {
|
|
190
|
+
text-decoration: underline;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.cm-logo {
|
|
194
|
+
display: block;
|
|
195
|
+
max-height: 32px;
|
|
196
|
+
max-width: 120px;
|
|
197
|
+
width: auto;
|
|
198
|
+
margin-bottom: 12px;
|
|
199
|
+
object-fit: contain;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
${le}
|
|
203
|
+
${ce}
|
|
204
|
+
`}function q(e){let t="consent-manager-styles";if(document.getElementById(t))return;let n=document.createElement("style");n.id=t,n.textContent=ge(e),document.head.appendChild(n)}var U=["en","fr","de"],I=["es","it","nl","pt","pl","sv","da","fi","cs","no","ro","hu","el","tr","uk","bg","hr","sk","sl","lt","lv","et","mt","ga","ca","eu","gl","is","sq","sr","bs","mk","ru","ar","he","ja","ko","zh","hi","id","ms","th","vi","fa"],F={en:{title:"Cookie Consent",description:"We use cookies to improve your experience and analyze site traffic.",preferencesTitle:"Cookie Preferences",preferencesDescription:"Choose which cookies you want to accept.",acceptAll:"Accept All",rejectAll:"Reject All",customize:"Customize",save:"Save Preferences",necessary:"Necessary",analytics:"Analytics",marketing:"Marketing",required:"(Required)",privacyPolicy:"Privacy Policy"},fr:{title:"Consentement aux cookies",description:"Nous utilisons des cookies pour am\xE9liorer votre exp\xE9rience et analyser le trafic.",preferencesTitle:"Pr\xE9f\xE9rences de cookies",preferencesDescription:"Choisissez les cookies que vous souhaitez accepter.",acceptAll:"Tout accepter",rejectAll:"Tout refuser",customize:"Personnaliser",save:"Enregistrer",necessary:"N\xE9cessaires",analytics:"Analytiques",marketing:"Marketing",required:"(Requis)",privacyPolicy:"Politique de confidentialit\xE9"},de:{title:"Cookie-Einwilligung",description:"Wir verwenden Cookies, um Ihre Erfahrung zu verbessern und den Datenverkehr zu analysieren.",preferencesTitle:"Cookie-Einstellungen",preferencesDescription:"W\xE4hlen Sie, welche Cookies Sie akzeptieren m\xF6chten.",acceptAll:"Alle akzeptieren",rejectAll:"Alle ablehnen",customize:"Anpassen",save:"Speichern",necessary:"Notwendig",analytics:"Analyse",marketing:"Marketing",required:"(Erforderlich)",privacyPolicy:"Datenschutzrichtlinie"}},T={...F};function x(e){if(!e)return null;let t=e.toLowerCase().slice(0,2);return me(t)?t:null}function me(e){return U.includes(e)||I.includes(e)}function O(e){Object.assign(T,e)}function H(e){let t=x(e);return t?!!T[t]:!1}function W(e){let t=x(e);return t&&T[t]||F.en}function P(e){let t=x(e);return t?I.includes(t):!1}function K(e,t){let n=x(e);return n&&(U.includes(n)||t)?n:"en"}var c=null,g=null;function J(e){try{let t=new URL(e,window.location.origin);return t.protocol==="http:"||t.protocol==="https:"?t.toString():null}catch{return null}}function Y(e){return document.createTextNode(e)}function fe(e){if(!e)return null;let t=J(e);if(!t)return null;let n=document.createElement("img");return n.src=t,n.className="cm-logo",n.alt="",n.setAttribute("aria-hidden","true"),n}function _(){let e=document.createElement("div");e.className="cm-powered-by";let t=document.createElement("a");return t.href="https://www.safebanner.com/upgrade?ref=badge",t.target="_blank",t.rel="noopener noreferrer",t.textContent="Powered by SafeBanner",e.appendChild(t),e}function X(e,t){if(!e)return null;let n=J(e);if(!n)return null;let a=document.createElement("a");return a.href=n,a.className="cm-link",a.target="_blank",a.rel="noopener noreferrer",a.textContent=t,a}function u(e,t,n){let a=document.createElement("button");return a.className=n,a.dataset.action=t,a.textContent=e,a}function A(e,t,n,a,o,r){let s=document.createElement("label");s.className="cm-category";let i=document.createElement("input");i.type="checkbox",i.className="cm-checkbox",i.dataset.category=e,i.checked=n,i.disabled=a;let l=document.createElement("span");l.className="cm-label";let p=document.createElement("span");if(p.className="cm-label-text",p.textContent=t,l.appendChild(p),o&&r){let h=document.createElement("span");h.className="cm-label-required",h.textContent=r,l.appendChild(h)}return s.appendChild(i),s.appendChild(l),s}function he(e,t){let n=document.createDocumentFragment(),a=fe(e.logoUrl);a&&n.appendChild(a);let o=document.createElement("div");o.className="cm-title",o.textContent=e.bannerTitle||t.title,n.appendChild(o);let r=document.createElement("p");r.className="cm-text",r.appendChild(Y((e.bannerDescription||t.description)+" "));let s=X(e.privacyPolicyUrl,t.privacyPolicy);s&&r.appendChild(s),n.appendChild(r);let i=document.createElement("div");return i.className="cm-buttons",i.appendChild(u(e.acceptLabel||t.acceptAll,"accept-all","cm-btn cm-btn-primary")),i.appendChild(u(e.rejectLabel||t.rejectAll,"reject-all","cm-btn cm-btn-secondary")),i.appendChild(u(e.customizeLabel||t.customize,"customize","cm-btn cm-btn-link")),n.appendChild(i),e.showBranding!==!1&&n.appendChild(_()),n}function ye(e,t){let n=document.createDocumentFragment(),a=d(),o=document.createElement("div");o.className="cm-title",o.textContent=t.preferencesTitle,n.appendChild(o);let r=document.createElement("p");r.className="cm-text",r.appendChild(Y(t.preferencesDescription+" "));let s=X(e.privacyPolicyUrl,t.privacyPolicy);s&&r.appendChild(s),n.appendChild(r);let i=document.createElement("div");i.className="cm-categories",i.appendChild(A("necessary",t.necessary,!0,!0,!0,t.required)),i.appendChild(A("analytics",t.analytics,a?.analytics??!1,!1,!1)),i.appendChild(A("marketing",t.marketing,a?.marketing??!1,!1,!1)),n.appendChild(i);let l=document.createElement("div");return l.className="cm-buttons",l.appendChild(u(e.saveLabel||t.save,"save","cm-btn cm-btn-primary")),l.appendChild(u(e.acceptLabel||t.acceptAll,"accept-all","cm-btn cm-btn-secondary")),n.appendChild(l),e.showBranding!==!1&&n.appendChild(_()),n}function be(){let e=c?.querySelector('[data-category="analytics"]')?.checked??!1,t=c?.querySelector('[data-category="marketing"]')?.checked??!1;return{necessary:!0,analytics:e,marketing:t,timestamp:Date.now()}}function Ce(e,t){let n=document.createDocumentFragment(),a=document.createElement("span");a.className="cm-bar-text",a.textContent=e.bannerDescription||t.description,n.appendChild(a);let o=document.createElement("div");return o.className="cm-buttons",o.appendChild(u(e.acceptLabel||t.acceptAll,"accept-all","cm-btn cm-btn-primary")),o.appendChild(u(e.rejectLabel||t.rejectAll,"reject-all","cm-btn cm-btn-secondary")),o.appendChild(u(e.customizeLabel||t.customize,"customize","cm-btn cm-btn-link")),n.appendChild(o),e.showBranding!==!1&&n.appendChild(_()),n}function b(e){q(e);let t=W(e.lang),n=e.layout==="bar";g=document.createElement("div"),g.className="cm-overlay",document.body.appendChild(g),c=document.createElement("div"),c.className="cm-banner",c.setAttribute("role","dialog"),c.setAttribute("aria-label",e.bannerTitle||t.title),c.appendChild(n?Ce(e,t):he(e,t)),document.body.appendChild(c),requestAnimationFrame(()=>{g?.classList.add("cm-visible"),c?.classList.add("cm-visible")}),c.addEventListener("click",a=>{let r=a.target.dataset.action;if(r)switch(r){case"accept-all":{let s={necessary:!0,analytics:!0,marketing:!0,timestamp:Date.now()};m(s),f(),e.onAccept?.(s);break}case"reject-all":{let s={necessary:!0,analytics:!1,marketing:!1,timestamp:Date.now()};m(s),f(),e.onDecline?.();break}case"customize":{c&&(c.textContent="",c.appendChild(ye(e,t)));break}case"save":{let s=be();m(s),f(),e.onUpdate?.(s);break}}})}function f(){g?.classList.remove("cm-visible"),c?.classList.remove("cm-visible"),setTimeout(()=>{g?.remove(),c?.remove(),g=null,c=null},300)}function v(){return c!==null}var V=!1,z="advanced";function $(){return typeof window<"u"&&typeof window.gtag=="function"}function Q(e,t){if($())try{window.gtag("consent",e,t)}catch(n){console.warn("[SafeBanner] Failed to send Google consent signal:",n)}}function Z(e,t){let n=e?.analytics??!1,a=e?.marketing??!1,o={analytics_storage:n?"granted":"denied",ad_storage:a?"granted":"denied",ad_user_data:a?"granted":"denied",ad_personalization:a?"granted":"denied"};return t==="advanced"&&!a&&(o.ads_data_redaction=!0),o}function B(e="advanced"){if(z=e,$()){let n=window.dataLayer;Array.isArray(n)&&n.some(o=>Array.isArray(o)&&o[0]==="consent")&&console.warn("[SafeBanner] Google consent signals detected before SafeBanner initialized. Ensure SafeBanner script loads BEFORE Google tags for proper consent handling.")}window.dataLayer=window.dataLayer||[],$()||(window.gtag=function(){window.dataLayer.push(arguments)});let t=Z(null,e);t.wait_for_update=500,Q("default",t),V=!0}function D(e){V||B(z);let t=Z(e,z);Q("update",t)}var G="safebanner_license:",xe=24*60*60*1e3,ve=320;function Le(){let e=document.currentScript;if(!e)return{};let t="advanced",n=e.dataset.googleConsent;return(n==="basic"||n==="off")&&(t=n),{position:e.dataset.position||"bottom",theme:e.dataset.theme||"light",primaryColor:e.dataset.color,companyName:e.dataset.company,privacyPolicyUrl:e.dataset.privacy,lang:e.dataset.lang,googleConsentMode:t,projectKey:e.dataset.projectKey,layout:e.dataset.layout,maxWidth:e.dataset.maxWidth!=null?parseInt(e.dataset.maxWidth,10):void 0,offset:e.dataset.offset!=null?parseInt(e.dataset.offset,10):void 0,logoUrl:e.dataset.logo,borderRadius:e.dataset.radius!=null?parseInt(e.dataset.radius,10):void 0,buttonStyle:e.dataset.buttonStyle,bannerTitle:e.dataset.bannerTitle,bannerDescription:e.dataset.bannerDescription,acceptLabel:e.dataset.acceptLabel,rejectLabel:e.dataset.rejectLabel,customizeLabel:e.dataset.customizeLabel,saveLabel:e.dataset.saveLabel}}function ee(){return document.currentScript}function Se(){let e=ee();if(!e?.src)return null;try{return new URL("/api/validate-key",e.src).toString()}catch{return null}}function we(){let e=ee();if(!e?.src)return null;try{return new URL("/safebanner-pro-translations.json",e.src).toString()}catch{return null}}function ke(e){try{let t=localStorage.getItem(`${G}${e}`);if(!t)return null;let n=JSON.parse(t);return n.expiresAt<Date.now()?(localStorage.removeItem(`${G}${e}`),null):n.valid}catch{return null}}function Ee(e,t){try{localStorage.setItem(`${G}${e}`,JSON.stringify({valid:t,expiresAt:Date.now()+xe}))}catch{}}var N=class{constructor(t={}){this.initialized=!1;this.googleConsentInitialized=!1;this.hasProLicense=!1;this.validationStarted=!1;this.proTranslationsPromise=null;this.config={...Le(),...t},this.requestedLanguage=this.config.lang,this.applyLicenseState(this.getCachedLicenseState()),this.initGoogleConsentMode(),this.validateProjectKey()}initGoogleConsentMode(){if(this.googleConsentInitialized||this.config.googleConsentMode==="off")return;let t=this.config.googleConsentMode==="basic"?"basic":"advanced";B(t),this.googleConsentInitialized=!0;let n=d();n&&D(n)}async init(){if(!this.initialized){if(this.initialized=!0,C()){this.enforceConsent();return}await this.ensureRequestedLanguageLoaded(),b(this.getBannerConfig())}}sendGoogleConsentUpdate(t){this.config.googleConsentMode!=="off"&&D(t)}getCachedLicenseState(){return this.config.projectKey?ke(this.config.projectKey)===!0:!1}applyLicenseState(t){this.hasProLicense=t,this.config.lang=K(this.requestedLanguage,t),this.config.showBranding=!t}async ensureRequestedLanguageLoaded(){if(!this.hasProLicense||!P(this.requestedLanguage)||H(this.requestedLanguage))return;if(this.proTranslationsPromise)return this.proTranslationsPromise;let t=we();if(t)return this.proTranslationsPromise=(async()=>{try{let n=await fetch(t,{mode:"cors"});if(!n.ok)return;let a=await n.json();O(a)}catch{}finally{this.proTranslationsPromise=null}})(),this.proTranslationsPromise}async validateProjectKey(){if(this.validationStarted||!this.config.projectKey)return;this.validationStarted=!0;let t=Se();if(t)try{let n=await fetch(t,{method:"POST",mode:"cors",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectKey:this.config.projectKey,hostname:window.location.hostname})});if(!n.ok)return;let o=(await n.json()).valid===!0;Ee(this.config.projectKey,o),await this.upgradeLicenseState(o)}catch{}}async upgradeLicenseState(t){let n=this.config.lang,a=this.config.showBranding;if(this.applyLicenseState(t),await this.ensureRequestedLanguageLoaded(),!v())return;let o=a!==this.config.showBranding,r=n!==this.config.lang&&P(this.requestedLanguage);o&&!this.config.showBranding&&document.querySelector(".cm-powered-by")?.remove(),(o||r)&&(f(),window.setTimeout(()=>{C()||b(this.getBannerConfig())},ve))}getBannerConfig(){if(!this.hasProLicense){let n=[this.config.layout&&"data-layout",this.config.logoUrl&&"data-logo",this.config.bannerTitle&&"data-banner-title",this.config.bannerDescription&&"data-banner-description",this.config.buttonStyle&&"data-button-style",this.config.theme==="auto"&&'data-theme="auto"',this.config.acceptLabel&&"data-accept-label",this.config.rejectLabel&&"data-reject-label"].filter(Boolean);n.length>0&&console.info(`[SafeBanner] ${n.join(", ")} require${n.length===1?"s":""} a Pro license and will be ignored. Upgrade at https://www.safebanner.com/upgrade`)}let t=this.hasProLicense?{layout:this.config.layout,theme:this.config.theme,maxWidth:this.config.maxWidth,offset:this.config.offset,logoUrl:this.config.logoUrl,borderRadius:this.config.borderRadius,buttonStyle:this.config.buttonStyle,bannerTitle:this.config.bannerTitle,bannerDescription:this.config.bannerDescription,acceptLabel:this.config.acceptLabel,rejectLabel:this.config.rejectLabel,customizeLabel:this.config.customizeLabel,saveLabel:this.config.saveLabel}:{layout:void 0,theme:this.config.theme==="auto"?"light":this.config.theme,maxWidth:void 0,offset:void 0,logoUrl:void 0,borderRadius:void 0,buttonStyle:void 0,bannerTitle:void 0,bannerDescription:void 0,acceptLabel:void 0,rejectLabel:void 0,customizeLabel:void 0,saveLabel:void 0};return{...this.config,...t,onAccept:n=>{this.sendGoogleConsentUpdate(n),this.enforceConsent(),this.config.onAccept?.(n)},onDecline:()=>{let n=d();n&&this.sendGoogleConsentUpdate(n),this.enforceConsent(),this.config.onDecline?.()},onUpdate:n=>{this.sendGoogleConsentUpdate(n),this.enforceConsent(),this.config.onUpdate?.(n)}}}enforceConsent(){let t=d();t&&(t.analytics||E("analytics"),t.marketing||E("marketing"))}getConsent(){return d()}hasConsented(){return C()}hasConsentFor(t){let n=d();return n?n[t]:t==="necessary"}updateConsent(t){let a={...d()||{necessary:!0,analytics:!1,marketing:!1,timestamp:Date.now()},...t,necessary:!0,timestamp:Date.now()};m(a),this.sendGoogleConsentUpdate(a),this.enforceConsent(),this.config.onUpdate?.(a)}reset(){M(),this.sendGoogleConsentUpdate({necessary:!0,analytics:!1,marketing:!1,timestamp:Date.now()}),v()||b(this.getBannerConfig())}show(){v()||b(this.getBannerConfig())}hide(){f()}detectCookies(){return k()}},L=new N;document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>L.init()):L.init();window.safeBanner=L;var Ue=L;})();
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "safebanner",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lightweight cookie consent banner with Google Consent Mode v2. One script tag, ~6kb gzipped, zero dependencies.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Kari Burt <hello@safebanner.com> (https://safebanner.com)",
|
|
7
|
+
"homepage": "https://safebanner.com",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/hellokariburt/SafeBanner.git",
|
|
11
|
+
"directory": "packages/consent-script"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"cookie-consent",
|
|
15
|
+
"gdpr",
|
|
16
|
+
"cookie-banner",
|
|
17
|
+
"google-consent-mode",
|
|
18
|
+
"consent-management",
|
|
19
|
+
"privacy",
|
|
20
|
+
"cookie-consent-banner",
|
|
21
|
+
"gdpr-compliance"
|
|
22
|
+
],
|
|
23
|
+
"main": "dist/safebanner.js",
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "node build.mjs",
|
|
31
|
+
"dev": "node build.mjs --watch",
|
|
32
|
+
"prepublishOnly": "node build.mjs"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"esbuild": "^0.20.0",
|
|
36
|
+
"typescript": "^5.3.0"
|
|
37
|
+
}
|
|
38
|
+
}
|