@hifilabs/pixel 0.2.0 → 0.6.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/dist/browser.js +260 -9
- package/dist/browser.min.js +102 -1
- package/dist/index.esm.d.ts +512 -26
- package/dist/index.js +367 -314
- package/dist/index.mjs +145 -92
- package/package.json +1 -1
package/dist/browser.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
var BalancePixel = (() => {
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
|
+
var __publicField = (obj, key, value) => {
|
|
5
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
6
|
+
return value;
|
|
7
|
+
};
|
|
8
|
+
|
|
2
9
|
// src/browser.ts
|
|
3
10
|
(function() {
|
|
4
11
|
function parseUserAgent(ua) {
|
|
@@ -46,13 +53,26 @@ var BalancePixel = (() => {
|
|
|
46
53
|
}
|
|
47
54
|
const currentScript = document.currentScript;
|
|
48
55
|
const artistId = currentScript?.dataset.artistId;
|
|
49
|
-
const
|
|
56
|
+
const rawProjectId = currentScript?.dataset.projectId;
|
|
57
|
+
const projectId = rawProjectId ? normalizeProjectId(rawProjectId) : void 0;
|
|
58
|
+
function normalizeProjectId(id) {
|
|
59
|
+
if (!id)
|
|
60
|
+
return id;
|
|
61
|
+
if (id.startsWith("release_") || id.startsWith("merch_") || id.startsWith("link_") || id.startsWith("custom_")) {
|
|
62
|
+
return id;
|
|
63
|
+
}
|
|
64
|
+
return `custom_${id}`;
|
|
65
|
+
}
|
|
50
66
|
const useEmulator = currentScript?.dataset.emulator === "true";
|
|
51
67
|
const debug = currentScript?.dataset.debug === "true";
|
|
52
68
|
const heartbeatInterval = parseInt(currentScript?.dataset.heartbeatInterval || "30000", 10);
|
|
53
69
|
const heartbeatEnabled = currentScript?.dataset.heartbeat !== "false";
|
|
54
70
|
const explicitSource = currentScript?.dataset.source;
|
|
55
71
|
const customEndpoint = currentScript?.dataset.endpoint;
|
|
72
|
+
const consentUIEnabled = currentScript?.dataset.consentUi === "true";
|
|
73
|
+
const consentStyle = currentScript?.dataset.consentStyle || "brutalist";
|
|
74
|
+
const consentPrimaryColor = currentScript?.dataset.primaryColor;
|
|
75
|
+
const consentBannerPosition = currentScript?.dataset.bannerPosition || "bottom";
|
|
56
76
|
function detectTrackingSource() {
|
|
57
77
|
if (explicitSource)
|
|
58
78
|
return explicitSource;
|
|
@@ -72,11 +92,13 @@ var BalancePixel = (() => {
|
|
|
72
92
|
const SESSION_TIMESTAMP_KEY = "session_timestamp";
|
|
73
93
|
const ATTRIBUTION_KEY = "attribution";
|
|
74
94
|
const FAN_ID_KEY = "fan_id_hash";
|
|
95
|
+
const VISITOR_ID_KEY = "visitor_id";
|
|
75
96
|
const CONSENT_STORAGE_KEY = "balance_consent";
|
|
76
97
|
const SESSION_DURATION = 60 * 60 * 1e3;
|
|
77
98
|
const STORAGE_PREFIX = "balance_";
|
|
78
99
|
const API_ENDPOINT = customEndpoint ? customEndpoint : useEmulator ? `http://localhost:5001/artist-os-distro/us-central1/pixelEndpoint` : `https://us-central1-artist-os-distro.cloudfunctions.net/pixelEndpoint`;
|
|
79
100
|
let sessionId = null;
|
|
101
|
+
let visitorId = null;
|
|
80
102
|
let fanIdHash = null;
|
|
81
103
|
let consent = null;
|
|
82
104
|
let attribution = {};
|
|
@@ -95,6 +117,169 @@ var BalancePixel = (() => {
|
|
|
95
117
|
if (debug)
|
|
96
118
|
console.log("[BALANCE Pixel]", ...args);
|
|
97
119
|
};
|
|
120
|
+
const CONSENT_STYLES = {
|
|
121
|
+
base: `
|
|
122
|
+
:host {
|
|
123
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
124
|
+
position: fixed;
|
|
125
|
+
left: 0;
|
|
126
|
+
right: 0;
|
|
127
|
+
z-index: 2147483647;
|
|
128
|
+
}
|
|
129
|
+
.banner {
|
|
130
|
+
padding: 16px 24px;
|
|
131
|
+
display: flex;
|
|
132
|
+
gap: 24px;
|
|
133
|
+
align-items: center;
|
|
134
|
+
justify-content: space-between;
|
|
135
|
+
flex-wrap: wrap;
|
|
136
|
+
}
|
|
137
|
+
.text {
|
|
138
|
+
font-size: 14px;
|
|
139
|
+
line-height: 1.5;
|
|
140
|
+
flex: 1;
|
|
141
|
+
min-width: 280px;
|
|
142
|
+
}
|
|
143
|
+
.text strong {
|
|
144
|
+
text-transform: uppercase;
|
|
145
|
+
letter-spacing: 0.5px;
|
|
146
|
+
}
|
|
147
|
+
.actions {
|
|
148
|
+
display: flex;
|
|
149
|
+
gap: 12px;
|
|
150
|
+
flex-shrink: 0;
|
|
151
|
+
}
|
|
152
|
+
.btn {
|
|
153
|
+
padding: 10px 20px;
|
|
154
|
+
font-size: 14px;
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
cursor: pointer;
|
|
157
|
+
transition: all 0.15s ease;
|
|
158
|
+
}
|
|
159
|
+
@media (max-width: 600px) {
|
|
160
|
+
.banner { flex-direction: column; text-align: center; }
|
|
161
|
+
.actions { width: 100%; justify-content: center; }
|
|
162
|
+
}
|
|
163
|
+
`,
|
|
164
|
+
brutalist: `
|
|
165
|
+
.banner {
|
|
166
|
+
background: #000;
|
|
167
|
+
color: #00ff00;
|
|
168
|
+
border-top: 4px solid #00ff00;
|
|
169
|
+
font-family: 'Courier New', monospace;
|
|
170
|
+
text-transform: uppercase;
|
|
171
|
+
}
|
|
172
|
+
.btn {
|
|
173
|
+
border: 2px solid #00ff00;
|
|
174
|
+
background: transparent;
|
|
175
|
+
color: #00ff00;
|
|
176
|
+
text-transform: uppercase;
|
|
177
|
+
letter-spacing: 1px;
|
|
178
|
+
}
|
|
179
|
+
.btn:hover { background: #00ff00; color: #000; }
|
|
180
|
+
.btn.decline { border-color: #ff0000; color: #ff0000; }
|
|
181
|
+
.btn.decline:hover { background: #ff0000; color: #000; }
|
|
182
|
+
`,
|
|
183
|
+
default: `
|
|
184
|
+
.banner {
|
|
185
|
+
background: #fff;
|
|
186
|
+
color: #000;
|
|
187
|
+
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
|
|
188
|
+
border-top: 1px solid #e0e0e0;
|
|
189
|
+
}
|
|
190
|
+
.btn {
|
|
191
|
+
border: 1px solid #000;
|
|
192
|
+
background: transparent;
|
|
193
|
+
color: #000;
|
|
194
|
+
border-radius: 4px;
|
|
195
|
+
}
|
|
196
|
+
.btn.accept { background: #000; color: #fff; }
|
|
197
|
+
.btn:hover { opacity: 0.8; }
|
|
198
|
+
`,
|
|
199
|
+
minimal: `
|
|
200
|
+
.banner { background: #1a1a1a; color: #e0e0e0; }
|
|
201
|
+
.btn {
|
|
202
|
+
border: 1px solid #e0e0e0;
|
|
203
|
+
background: transparent;
|
|
204
|
+
color: #e0e0e0;
|
|
205
|
+
border-radius: 4px;
|
|
206
|
+
}
|
|
207
|
+
.btn.accept { background: #fff; color: #000; border-color: #fff; }
|
|
208
|
+
.btn:hover { opacity: 0.8; }
|
|
209
|
+
`
|
|
210
|
+
};
|
|
211
|
+
class ConsentManager {
|
|
212
|
+
constructor(config) {
|
|
213
|
+
__publicField(this, "container", null);
|
|
214
|
+
__publicField(this, "shadow", null);
|
|
215
|
+
__publicField(this, "config");
|
|
216
|
+
this.config = config;
|
|
217
|
+
if (this.hasStoredConsent()) {
|
|
218
|
+
log("ConsentManager: Consent already exists, not showing banner");
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
this.container = document.createElement("div");
|
|
222
|
+
this.container.id = "balance-consent-manager";
|
|
223
|
+
this.shadow = this.container.attachShadow({ mode: "closed" });
|
|
224
|
+
this.render();
|
|
225
|
+
document.body.appendChild(this.container);
|
|
226
|
+
log("ConsentManager: Banner rendered");
|
|
227
|
+
}
|
|
228
|
+
hasStoredConsent() {
|
|
229
|
+
try {
|
|
230
|
+
return localStorage.getItem(CONSENT_STORAGE_KEY) !== null;
|
|
231
|
+
} catch {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
render() {
|
|
236
|
+
if (!this.shadow)
|
|
237
|
+
return;
|
|
238
|
+
const styleKey = this.config.style || "brutalist";
|
|
239
|
+
const baseStyles = CONSENT_STYLES.base;
|
|
240
|
+
const themeStyles = CONSENT_STYLES[styleKey] || CONSENT_STYLES.brutalist;
|
|
241
|
+
const positionStyle = this.config.position === "top" ? "top: 0;" : "bottom: 0;";
|
|
242
|
+
const customColor = this.config.primaryColor ? `.btn.accept { background: ${this.config.primaryColor} !important; border-color: ${this.config.primaryColor} !important; color: #fff !important; }` : "";
|
|
243
|
+
this.shadow.innerHTML = `
|
|
244
|
+
<style>
|
|
245
|
+
${baseStyles}
|
|
246
|
+
:host { ${positionStyle} }
|
|
247
|
+
${themeStyles}
|
|
248
|
+
${customColor}
|
|
249
|
+
</style>
|
|
250
|
+
<div class="banner" role="dialog" aria-label="Cookie consent" aria-modal="false">
|
|
251
|
+
<p class="text">
|
|
252
|
+
<strong>Privacy Notice:</strong> We use data to support the artist and improve your experience.
|
|
253
|
+
</p>
|
|
254
|
+
<div class="actions">
|
|
255
|
+
<button id="decline" class="btn decline">Decline</button>
|
|
256
|
+
<button id="accept" class="btn accept">Accept</button>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
`;
|
|
260
|
+
this.shadow.getElementById("accept")?.addEventListener("click", () => this.handleConsent(true));
|
|
261
|
+
this.shadow.getElementById("decline")?.addEventListener("click", () => this.handleConsent(false));
|
|
262
|
+
}
|
|
263
|
+
handleConsent(accepted) {
|
|
264
|
+
if (window.balance?.setConsent) {
|
|
265
|
+
window.balance.setConsent({
|
|
266
|
+
analytics: accepted,
|
|
267
|
+
marketing: accepted,
|
|
268
|
+
personalization: accepted,
|
|
269
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
this.remove();
|
|
273
|
+
}
|
|
274
|
+
remove() {
|
|
275
|
+
if (this.container) {
|
|
276
|
+
this.container.remove();
|
|
277
|
+
this.container = null;
|
|
278
|
+
this.shadow = null;
|
|
279
|
+
log("ConsentManager: Banner removed");
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
98
283
|
function getStorage() {
|
|
99
284
|
try {
|
|
100
285
|
return currentStorageTier === "local" ? localStorage : sessionStorage;
|
|
@@ -218,6 +403,36 @@ var BalancePixel = (() => {
|
|
|
218
403
|
} catch (e) {
|
|
219
404
|
}
|
|
220
405
|
}
|
|
406
|
+
function getOrCreateVisitorId() {
|
|
407
|
+
if (!consent?.analytics) {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
try {
|
|
411
|
+
const stored = localStorage.getItem(STORAGE_PREFIX + VISITOR_ID_KEY);
|
|
412
|
+
if (stored) {
|
|
413
|
+
return stored;
|
|
414
|
+
}
|
|
415
|
+
const newId = generateUUID();
|
|
416
|
+
localStorage.setItem(STORAGE_PREFIX + VISITOR_ID_KEY, newId);
|
|
417
|
+
log("Created persistent visitor ID:", newId.substring(0, 8) + "...");
|
|
418
|
+
return newId;
|
|
419
|
+
} catch (e) {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
function loadVisitorId() {
|
|
424
|
+
if (!consent?.analytics) {
|
|
425
|
+
visitorId = null;
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
try {
|
|
429
|
+
visitorId = localStorage.getItem(STORAGE_PREFIX + VISITOR_ID_KEY);
|
|
430
|
+
if (visitorId) {
|
|
431
|
+
log("Loaded visitor ID:", visitorId.substring(0, 8) + "...");
|
|
432
|
+
}
|
|
433
|
+
} catch (e) {
|
|
434
|
+
}
|
|
435
|
+
}
|
|
221
436
|
function loadConsent() {
|
|
222
437
|
try {
|
|
223
438
|
const stored = localStorage.getItem(CONSENT_STORAGE_KEY);
|
|
@@ -245,6 +460,9 @@ var BalancePixel = (() => {
|
|
|
245
460
|
}
|
|
246
461
|
if (preferences.analytics === true) {
|
|
247
462
|
upgradeStorageTier();
|
|
463
|
+
if (!visitorId) {
|
|
464
|
+
visitorId = getOrCreateVisitorId();
|
|
465
|
+
}
|
|
248
466
|
}
|
|
249
467
|
const event = buildEvent({
|
|
250
468
|
event_name: "consent_updated",
|
|
@@ -255,6 +473,13 @@ var BalancePixel = (() => {
|
|
|
255
473
|
}
|
|
256
474
|
});
|
|
257
475
|
enqueueEvent(event);
|
|
476
|
+
try {
|
|
477
|
+
window.dispatchEvent(new CustomEvent("balance:consent:updated", {
|
|
478
|
+
detail: preferences
|
|
479
|
+
}));
|
|
480
|
+
log("DOM event balance:consent:updated dispatched");
|
|
481
|
+
} catch (e) {
|
|
482
|
+
}
|
|
258
483
|
}
|
|
259
484
|
function getConsent() {
|
|
260
485
|
return consent;
|
|
@@ -275,6 +500,8 @@ var BalancePixel = (() => {
|
|
|
275
500
|
const base = {
|
|
276
501
|
artist_id: artistId,
|
|
277
502
|
fan_session_id: sessionId,
|
|
503
|
+
visitor_id: visitorId || void 0,
|
|
504
|
+
// Persistent visitor ID for deduplication
|
|
278
505
|
fan_id_hash: fanIdHash || void 0,
|
|
279
506
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
280
507
|
source_url: window.location.href,
|
|
@@ -456,6 +683,8 @@ var BalancePixel = (() => {
|
|
|
456
683
|
event_name: "identify",
|
|
457
684
|
fan_id_hash: fanIdHash,
|
|
458
685
|
metadata: {
|
|
686
|
+
email,
|
|
687
|
+
// Pass email for display_name fallback (extracted prefix only stored)
|
|
459
688
|
email_sha256: fanIdHash,
|
|
460
689
|
traits,
|
|
461
690
|
consent_preferences: consent || void 0,
|
|
@@ -491,22 +720,32 @@ var BalancePixel = (() => {
|
|
|
491
720
|
}
|
|
492
721
|
sessionId = getOrCreateSession();
|
|
493
722
|
loadFanId();
|
|
723
|
+
loadVisitorId();
|
|
494
724
|
if (!consent) {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
725
|
+
if (consentUIEnabled) {
|
|
726
|
+
log("Consent UI enabled, waiting for explicit user consent (Tier 0 mode)");
|
|
727
|
+
} else {
|
|
728
|
+
consent = {
|
|
729
|
+
analytics: true,
|
|
730
|
+
marketing: true,
|
|
731
|
+
personalization: true,
|
|
732
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
733
|
+
};
|
|
734
|
+
log("Default consent enabled (all tracking):", consent);
|
|
735
|
+
upgradeStorageTier();
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
if (consent?.analytics && !visitorId) {
|
|
739
|
+
visitorId = getOrCreateVisitorId();
|
|
503
740
|
}
|
|
504
741
|
captureAttribution();
|
|
505
742
|
startFlushTimer();
|
|
506
743
|
log("Initialized", {
|
|
507
744
|
artistId,
|
|
508
745
|
projectId: projectId || "(none - will track to all projects)",
|
|
746
|
+
rawProjectId: rawProjectId || "(none)",
|
|
509
747
|
sessionId,
|
|
748
|
+
visitorId: visitorId ? visitorId.substring(0, 8) + "..." : null,
|
|
510
749
|
fanIdHash,
|
|
511
750
|
consent,
|
|
512
751
|
storageTier: currentStorageTier,
|
|
@@ -567,20 +806,32 @@ var BalancePixel = (() => {
|
|
|
567
806
|
page: trackPageView,
|
|
568
807
|
purchase,
|
|
569
808
|
getSessionId: () => sessionId,
|
|
809
|
+
getVisitorId: () => visitorId,
|
|
570
810
|
getFanIdHash: () => fanIdHash,
|
|
571
811
|
getAttribution: () => attribution,
|
|
572
812
|
setConsent,
|
|
573
813
|
getConsent,
|
|
574
814
|
hasConsent
|
|
575
815
|
};
|
|
816
|
+
function initConsentManager() {
|
|
817
|
+
if (consentUIEnabled && !consent) {
|
|
818
|
+
new ConsentManager({
|
|
819
|
+
style: consentStyle,
|
|
820
|
+
primaryColor: consentPrimaryColor,
|
|
821
|
+
position: consentBannerPosition
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
}
|
|
576
825
|
if (document.readyState === "loading") {
|
|
577
826
|
document.addEventListener("DOMContentLoaded", () => {
|
|
578
827
|
init();
|
|
579
828
|
processQueue();
|
|
829
|
+
initConsentManager();
|
|
580
830
|
});
|
|
581
831
|
} else {
|
|
582
832
|
init();
|
|
583
833
|
processQueue();
|
|
834
|
+
initConsentManager();
|
|
584
835
|
}
|
|
585
836
|
log("Pixel script loaded");
|
|
586
837
|
})();
|
package/dist/browser.min.js
CHANGED
|
@@ -1 +1,102 @@
|
|
|
1
|
-
var BalancePixel=(()=>{(function(){function
|
|
1
|
+
var BalancePixel=(()=>{var Me=Object.defineProperty;var Be=(f,a,g)=>a in f?Me(f,a,{enumerable:!0,configurable:!0,writable:!0,value:g}):f[a]=g;var T=(f,a,g)=>(Be(f,typeof a!="symbol"?a+"":a,g),g);(function(){function f(e){let t="desktop";/ipad|tablet|android(?!.*mobile)/i.test(e)?t="tablet":/mobile|iphone|android.*mobile|blackberry|iemobile/i.test(e)&&(t="mobile");let n="Unknown";/edg/i.test(e)?n="Edge":/opr|opera/i.test(e)?n="Opera":/firefox/i.test(e)?n="Firefox":/chrome/i.test(e)?n="Chrome":/safari/i.test(e)&&(n="Safari");let o="Unknown";return/iphone|ipad/i.test(e)?o="iOS":/android/i.test(e)?o="Android":/windows/i.test(e)?o="Windows":/mac os/i.test(e)?o="macOS":/linux/i.test(e)?o="Linux":/cros/i.test(e)&&(o="ChromeOS"),{device_type:t,browser:n,os:o}}let a=null;function g(){if(!a)try{a=f(navigator.userAgent)}catch{a={device_type:"desktop",browser:"Unknown",os:"Unknown"}}return a}let s=document.currentScript,A=s?.dataset.artistId,P=s?.dataset.projectId,D=P?fe(P):void 0;function fe(e){return!e||e.startsWith("release_")||e.startsWith("merch_")||e.startsWith("link_")||e.startsWith("custom_")?e:`custom_${e}`}let W=s?.dataset.emulator==="true",ge=s?.dataset.debug==="true",O=parseInt(s?.dataset.heartbeatInterval||"30000",10),q=s?.dataset.heartbeat!=="false",V=s?.dataset.source,Y=s?.dataset.endpoint,J=s?.dataset.consentUi==="true",pe=s?.dataset.consentStyle||"brutalist",me=s?.dataset.primaryColor,he=s?.dataset.bannerPosition||"bottom";function be(){if(V)return V;let e=typeof window.dataLayer<"u"&&Array.isArray(window.dataLayer),t=typeof window.gtag=="function";return e&&t?"gtm":"pixel"}let _="pixel";if(!A){console.error("[BALANCE Pixel] Error: data-artist-id attribute is required");return}let G="session_id",L="session_timestamp",Q="attribution",X="fan_id_hash",N="visitor_id",U="balance_consent",ye=60*60*1e3,p="balance_",R=Y||(W?"http://localhost:5001/artist-os-distro/us-central1/pixelEndpoint":"https://us-central1-artist-os-distro.cloudfunctions.net/pixelEndpoint"),k=null,c=null,l=null,i=null,m={},d=[],M=null,u="session",h=null,ve=0,B=0,b=0,w=!0,we=2*60*1e3,Z=Date.now(),x=!1,r=(...e)=>{ge&&console.log("[BALANCE Pixel]",...e)},H={base:`
|
|
2
|
+
:host {
|
|
3
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
4
|
+
position: fixed;
|
|
5
|
+
left: 0;
|
|
6
|
+
right: 0;
|
|
7
|
+
z-index: 2147483647;
|
|
8
|
+
}
|
|
9
|
+
.banner {
|
|
10
|
+
padding: 16px 24px;
|
|
11
|
+
display: flex;
|
|
12
|
+
gap: 24px;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: space-between;
|
|
15
|
+
flex-wrap: wrap;
|
|
16
|
+
}
|
|
17
|
+
.text {
|
|
18
|
+
font-size: 14px;
|
|
19
|
+
line-height: 1.5;
|
|
20
|
+
flex: 1;
|
|
21
|
+
min-width: 280px;
|
|
22
|
+
}
|
|
23
|
+
.text strong {
|
|
24
|
+
text-transform: uppercase;
|
|
25
|
+
letter-spacing: 0.5px;
|
|
26
|
+
}
|
|
27
|
+
.actions {
|
|
28
|
+
display: flex;
|
|
29
|
+
gap: 12px;
|
|
30
|
+
flex-shrink: 0;
|
|
31
|
+
}
|
|
32
|
+
.btn {
|
|
33
|
+
padding: 10px 20px;
|
|
34
|
+
font-size: 14px;
|
|
35
|
+
font-weight: 600;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
transition: all 0.15s ease;
|
|
38
|
+
}
|
|
39
|
+
@media (max-width: 600px) {
|
|
40
|
+
.banner { flex-direction: column; text-align: center; }
|
|
41
|
+
.actions { width: 100%; justify-content: center; }
|
|
42
|
+
}
|
|
43
|
+
`,brutalist:`
|
|
44
|
+
.banner {
|
|
45
|
+
background: #000;
|
|
46
|
+
color: #00ff00;
|
|
47
|
+
border-top: 4px solid #00ff00;
|
|
48
|
+
font-family: 'Courier New', monospace;
|
|
49
|
+
text-transform: uppercase;
|
|
50
|
+
}
|
|
51
|
+
.btn {
|
|
52
|
+
border: 2px solid #00ff00;
|
|
53
|
+
background: transparent;
|
|
54
|
+
color: #00ff00;
|
|
55
|
+
text-transform: uppercase;
|
|
56
|
+
letter-spacing: 1px;
|
|
57
|
+
}
|
|
58
|
+
.btn:hover { background: #00ff00; color: #000; }
|
|
59
|
+
.btn.decline { border-color: #ff0000; color: #ff0000; }
|
|
60
|
+
.btn.decline:hover { background: #ff0000; color: #000; }
|
|
61
|
+
`,default:`
|
|
62
|
+
.banner {
|
|
63
|
+
background: #fff;
|
|
64
|
+
color: #000;
|
|
65
|
+
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
|
|
66
|
+
border-top: 1px solid #e0e0e0;
|
|
67
|
+
}
|
|
68
|
+
.btn {
|
|
69
|
+
border: 1px solid #000;
|
|
70
|
+
background: transparent;
|
|
71
|
+
color: #000;
|
|
72
|
+
border-radius: 4px;
|
|
73
|
+
}
|
|
74
|
+
.btn.accept { background: #000; color: #fff; }
|
|
75
|
+
.btn:hover { opacity: 0.8; }
|
|
76
|
+
`,minimal:`
|
|
77
|
+
.banner { background: #1a1a1a; color: #e0e0e0; }
|
|
78
|
+
.btn {
|
|
79
|
+
border: 1px solid #e0e0e0;
|
|
80
|
+
background: transparent;
|
|
81
|
+
color: #e0e0e0;
|
|
82
|
+
border-radius: 4px;
|
|
83
|
+
}
|
|
84
|
+
.btn.accept { background: #fff; color: #000; border-color: #fff; }
|
|
85
|
+
.btn:hover { opacity: 0.8; }
|
|
86
|
+
`};class xe{constructor(t){T(this,"container",null);T(this,"shadow",null);T(this,"config");if(this.config=t,this.hasStoredConsent()){r("ConsentManager: Consent already exists, not showing banner");return}this.container=document.createElement("div"),this.container.id="balance-consent-manager",this.shadow=this.container.attachShadow({mode:"closed"}),this.render(),document.body.appendChild(this.container),r("ConsentManager: Banner rendered")}hasStoredConsent(){try{return localStorage.getItem(U)!==null}catch{return!1}}render(){if(!this.shadow)return;let t=this.config.style||"brutalist",n=H.base,o=H[t]||H.brutalist,S=this.config.position==="top"?"top: 0;":"bottom: 0;",ue=this.config.primaryColor?`.btn.accept { background: ${this.config.primaryColor} !important; border-color: ${this.config.primaryColor} !important; color: #fff !important; }`:"";this.shadow.innerHTML=`
|
|
87
|
+
<style>
|
|
88
|
+
${n}
|
|
89
|
+
:host { ${S} }
|
|
90
|
+
${o}
|
|
91
|
+
${ue}
|
|
92
|
+
</style>
|
|
93
|
+
<div class="banner" role="dialog" aria-label="Cookie consent" aria-modal="false">
|
|
94
|
+
<p class="text">
|
|
95
|
+
<strong>Privacy Notice:</strong> We use data to support the artist and improve your experience.
|
|
96
|
+
</p>
|
|
97
|
+
<div class="actions">
|
|
98
|
+
<button id="decline" class="btn decline">Decline</button>
|
|
99
|
+
<button id="accept" class="btn accept">Accept</button>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
`,this.shadow.getElementById("accept")?.addEventListener("click",()=>this.handleConsent(!0)),this.shadow.getElementById("decline")?.addEventListener("click",()=>this.handleConsent(!1))}handleConsent(t){window.balance?.setConsent&&window.balance.setConsent({analytics:t,marketing:t,personalization:t,timestamp:new Date().toISOString()}),this.remove()}remove(){this.container&&(this.container.remove(),this.container=null,this.shadow=null,r("ConsentManager: Banner removed"))}}function ee(){try{return u==="local"?localStorage:sessionStorage}catch{return null}}function C(e){let t=ee();if(!t)return null;try{let n=p+e,o=t.getItem(n);if(!o&&u==="session")try{o=localStorage.getItem(n)}catch{}return o}catch{return null}}function I(e,t){let n=ee();if(n)try{n.setItem(p+e,t)}catch{}}function j(){if(u!=="local"){r("Upgrading storage tier: session -> local");try{let e=[];for(let t=0;t<sessionStorage.length;t++){let n=sessionStorage.key(t);n?.startsWith(p)&&e.push(n)}for(let t of e){let n=sessionStorage.getItem(t);n&&localStorage.setItem(t,n)}for(let t of e)sessionStorage.removeItem(t);u="local",r(`Storage tier upgraded, migrated ${e.length} items`)}catch(e){console.error("[BALANCE Pixel] Storage migration failed:",e)}}}function F(){return crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}function Ie(){try{let e=C(G),t=C(L);if(e&&t&&Date.now()-parseInt(t,10)<ye)return I(L,Date.now().toString()),e;let n=F();return I(G,n),I(L,Date.now().toString()),n}catch{return F()}}function Se(){let e=new URLSearchParams(window.location.search),t={};return["source","medium","campaign","content","term"].forEach(n=>{let o=e.get(`utm_${n}`);o&&(t[`utm_${n}`]=o)}),t}function _e(){try{let e=C(Q);if(e){m=JSON.parse(e),r("Loaded attribution:",m);return}let t=Se();Object.keys(t).length>0&&(m=t,I(Q,JSON.stringify(t)),r("Captured attribution:",m))}catch{}}function ke(){try{l=C(X)}catch{}}function te(){if(!i?.analytics)return null;try{let e=localStorage.getItem(p+N);if(e)return e;let t=F();return localStorage.setItem(p+N,t),r("Created persistent visitor ID:",t.substring(0,8)+"..."),t}catch{return null}}function Ce(){if(!i?.analytics){c=null;return}try{c=localStorage.getItem(p+N),c&&r("Loaded visitor ID:",c.substring(0,8)+"...")}catch{}}function Ee(){try{let e=localStorage.getItem(U);e&&(i=JSON.parse(e).preferences||null,r("Loaded consent:",i))}catch{}}function ne(e){let t=i;i=e;try{let o={preferences:e,method:"explicit",version:1};localStorage.setItem(U,JSON.stringify(o)),r("Consent saved:",e)}catch(o){console.error("[BALANCE Pixel] Could not save consent:",o)}e.analytics===!0&&(j(),c||(c=te()));let n=y({event_name:"consent_updated",metadata:{consent_preferences:e,consent_method:"explicit",previous_consent:t||void 0}});v(n);try{window.dispatchEvent(new CustomEvent("balance:consent:updated",{detail:e})),r("DOM event balance:consent:updated dispatched")}catch{}}function Te(){return i}function Ae(e){return i?.[e]===!0}async function Pe(e){let t=e.toLowerCase().trim(),o=new TextEncoder().encode(t),S=await crypto.subtle.digest("SHA-256",o);return Array.from(new Uint8Array(S)).map(Re=>Re.toString(16).padStart(2,"0")).join("")}function y(e){let t=g(),n={artist_id:A,fan_session_id:k,visitor_id:c||void 0,fan_id_hash:l||void 0,timestamp:new Date().toISOString(),source_url:window.location.href,referrer_url:document.referrer||void 0,user_agent:navigator.userAgent,device_type:t.device_type,browser:t.browser,os:t.os,tracking_source:_,...e,...m};return D&&!e.projectId&&(n.projectId=D),n}function v(e){d.push(e),r("Event queued:",e.event_name,"(queue:",d.length,")"),d.length>=10&&E()}async function E(){if(d.length===0)return;let e=[...d];d=[],r("Flushing",e.length,"events to",R);try{let t=await fetch(R,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({events:e}),keepalive:!0});if(!t.ok)throw new Error(`HTTP ${t.status}`);r("Events sent successfully")}catch(t){console.error("[BALANCE Pixel] Failed to send events:",t),d.length<50&&d.push(...e)}}function De(){M&&clearInterval(M),M=window.setInterval(()=>{d.length>0&&E()},5e3)}function z(){b||(ve=Date.now()),b=Date.now(),w=!0,r("Active time tracking started/resumed")}function re(){b&&w&&(B+=Date.now()-b),w=!1,r("Active time tracking paused, accumulated:",B,"ms")}function Oe(){let e=B;return w&&b&&(e+=Date.now()-b),e}function Le(){Z=Date.now(),x&&(x=!1,z(),r("User returned from idle"))}function oe(){if(document.visibilityState==="hidden"){r("Skipping heartbeat - tab hidden");return}if(Date.now()-Z>we){x||(x=!0,re(),r("User idle - pausing heartbeat"));return}let e=Oe(),t=Math.round(e/1e3),n=y({event_name:"engagement_heartbeat",metadata:{time_on_page_seconds:t,time_on_page_ms:e,heartbeat_interval_ms:O,is_active:w&&!x}});v(n),r("Heartbeat sent:",t,"seconds active")}function Ne(){if(!q){r('Heartbeat disabled via data-heartbeat="false"');return}h&&clearInterval(h),z(),h=window.setInterval(()=>{oe()},O),r("Heartbeat started with interval:",O,"ms")}function Ue(){h&&(clearInterval(h),h=null),q&&(oe(),r("Heartbeat stopped, final time sent"))}function $(e={}){let t=y({event_name:"pageview",page_title:e.title||document.title,source_url:e.url||window.location.href});v(t)}function ie(e,t={}){let n=y({event_name:"custom",metadata:{event_type:e,...t}});v(n)}async function ae(e,t={}){try{if(i&&i.analytics===!1){r("Identify skipped - user declined analytics consent");return}l=await Pe(e),i?.analytics===!0&&j(),I(X,l);let n=e.split("@"),o=n[0].charAt(0)+"***@"+(n[1]||"");r("Fan identified:",{name:t.name||"(no name)",email:o,hash:l.substring(0,16)+"...",traits:t,storageTier:u});let S=y({event_name:"identify",fan_id_hash:l,metadata:{email:e,email_sha256:l,traits:t,consent_preferences:i||void 0,storage_tier:u}});v(S)}catch(n){console.error("[BALANCE Pixel] Failed to identify:",n)}}function se(e,t="USD",n={}){let o=y({event_name:"purchase",metadata:{revenue:e,currency:t,...n}});v(o)}function ce(){_=be(),r("Tracking source detected:",_),Ee(),i?.analytics===!0?(u="local",r("Storage tier: local (analytics consent granted)")):(u="session",r("Storage tier: session (privacy by default)")),k=Ie(),ke(),Ce(),i||(J?r("Consent UI enabled, waiting for explicit user consent (Tier 0 mode)"):(i={analytics:!0,marketing:!0,personalization:!0,timestamp:new Date().toISOString()},r("Default consent enabled (all tracking):",i),j())),i?.analytics&&!c&&(c=te()),_e(),De(),r("Initialized",{artistId:A,projectId:D||"(none - will track to all projects)",rawProjectId:P||"(none)",sessionId:k,visitorId:c?c.substring(0,8)+"...":null,fanIdHash:l,consent:i,storageTier:u,trackingSource:_,useEmulator:W,endpoint:R}),window._balanceInitialPageviewFired=!0,$(),Ne(),["mousemove","keydown","scroll","touchstart"].forEach(e=>{document.addEventListener(e,Le,{passive:!0})}),window.addEventListener("beforeunload",()=>{Ue(),E()}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?(re(),E()):z()})}let K=window.balance?.q||[];function le(){if(K.length>0){r("Processing",K.length,"queued commands");for(let e of K){let[t,...n]=e;switch(t){case"track":ie(n[0],n[1]);break;case"identify":ae(n[0],n[1]);break;case"page":$(n[0]);break;case"purchase":se(n[0],n[1],n[2]);break;case"setConsent":ne(n[0]);break;default:r("Unknown queued command:",t)}}}}window.balance={track:ie,identify:ae,page:$,purchase:se,getSessionId:()=>k,getVisitorId:()=>c,getFanIdHash:()=>l,getAttribution:()=>m,setConsent:ne,getConsent:Te,hasConsent:Ae};function de(){J&&!i&&new xe({style:pe,primaryColor:me,position:he})}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{ce(),le(),de()}):(ce(),le(),de()),r("Pixel script loaded")})();})();
|