@hifilabs/pixel 0.0.9 → 0.0.10
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/index.js +146 -0
- package/dist/index.min.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -86,11 +86,56 @@ var BalancePixel = (() => {
|
|
|
86
86
|
|
|
87
87
|
// src/index.ts
|
|
88
88
|
(function() {
|
|
89
|
+
function parseUserAgent(ua) {
|
|
90
|
+
let device_type = "desktop";
|
|
91
|
+
if (/ipad|tablet|android(?!.*mobile)/i.test(ua))
|
|
92
|
+
device_type = "tablet";
|
|
93
|
+
else if (/mobile|iphone|android.*mobile|blackberry|iemobile/i.test(ua))
|
|
94
|
+
device_type = "mobile";
|
|
95
|
+
let browser = "Unknown";
|
|
96
|
+
if (/edg/i.test(ua))
|
|
97
|
+
browser = "Edge";
|
|
98
|
+
else if (/opr|opera/i.test(ua))
|
|
99
|
+
browser = "Opera";
|
|
100
|
+
else if (/firefox/i.test(ua))
|
|
101
|
+
browser = "Firefox";
|
|
102
|
+
else if (/chrome/i.test(ua))
|
|
103
|
+
browser = "Chrome";
|
|
104
|
+
else if (/safari/i.test(ua))
|
|
105
|
+
browser = "Safari";
|
|
106
|
+
let os = "Unknown";
|
|
107
|
+
if (/iphone|ipad/i.test(ua))
|
|
108
|
+
os = "iOS";
|
|
109
|
+
else if (/android/i.test(ua))
|
|
110
|
+
os = "Android";
|
|
111
|
+
else if (/windows/i.test(ua))
|
|
112
|
+
os = "Windows";
|
|
113
|
+
else if (/mac os/i.test(ua))
|
|
114
|
+
os = "macOS";
|
|
115
|
+
else if (/linux/i.test(ua))
|
|
116
|
+
os = "Linux";
|
|
117
|
+
else if (/cros/i.test(ua))
|
|
118
|
+
os = "ChromeOS";
|
|
119
|
+
return { device_type, browser, os };
|
|
120
|
+
}
|
|
121
|
+
let cachedDeviceInfo = null;
|
|
122
|
+
function getDeviceInfo() {
|
|
123
|
+
if (!cachedDeviceInfo) {
|
|
124
|
+
try {
|
|
125
|
+
cachedDeviceInfo = parseUserAgent(navigator.userAgent);
|
|
126
|
+
} catch {
|
|
127
|
+
cachedDeviceInfo = { device_type: "desktop", browser: "Unknown", os: "Unknown" };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return cachedDeviceInfo;
|
|
131
|
+
}
|
|
89
132
|
const currentScript = document.currentScript;
|
|
90
133
|
const artistId = currentScript?.dataset.artistId;
|
|
91
134
|
const projectId = currentScript?.dataset.projectId;
|
|
92
135
|
const useEmulator = currentScript?.dataset.emulator === "true";
|
|
93
136
|
const debug = currentScript?.dataset.debug === "true";
|
|
137
|
+
const heartbeatInterval = parseInt(currentScript?.dataset.heartbeatInterval || "30000", 10);
|
|
138
|
+
const heartbeatEnabled = currentScript?.dataset.heartbeat !== "false";
|
|
94
139
|
if (!artistId) {
|
|
95
140
|
console.error("[BALANCE Pixel] Error: data-artist-id attribute is required");
|
|
96
141
|
return;
|
|
@@ -110,6 +155,14 @@ var BalancePixel = (() => {
|
|
|
110
155
|
let eventQueue = [];
|
|
111
156
|
let flushTimer = null;
|
|
112
157
|
let currentStorageTier = "session";
|
|
158
|
+
let heartbeatTimer = null;
|
|
159
|
+
let pageStartTime = 0;
|
|
160
|
+
let activeTime = 0;
|
|
161
|
+
let lastActiveTimestamp = 0;
|
|
162
|
+
let isPageVisible = true;
|
|
163
|
+
const IDLE_TIMEOUT = 2 * 60 * 1e3;
|
|
164
|
+
let lastActivityTime = Date.now();
|
|
165
|
+
let isIdle = false;
|
|
113
166
|
const log = (...args) => {
|
|
114
167
|
if (debug)
|
|
115
168
|
console.log("[BALANCE Pixel]", ...args);
|
|
@@ -290,6 +343,7 @@ var BalancePixel = (() => {
|
|
|
290
343
|
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
291
344
|
}
|
|
292
345
|
function buildEvent(partial) {
|
|
346
|
+
const deviceInfo = getDeviceInfo();
|
|
293
347
|
const base = {
|
|
294
348
|
artist_id: artistId,
|
|
295
349
|
fan_session_id: sessionId,
|
|
@@ -298,6 +352,10 @@ var BalancePixel = (() => {
|
|
|
298
352
|
source_url: window.location.href,
|
|
299
353
|
referrer_url: document.referrer || void 0,
|
|
300
354
|
user_agent: navigator.userAgent,
|
|
355
|
+
// Device info (parsed client-side)
|
|
356
|
+
device_type: deviceInfo.device_type,
|
|
357
|
+
browser: deviceInfo.browser,
|
|
358
|
+
os: deviceInfo.os,
|
|
301
359
|
...partial,
|
|
302
360
|
...attribution
|
|
303
361
|
};
|
|
@@ -346,6 +404,86 @@ var BalancePixel = (() => {
|
|
|
346
404
|
flush();
|
|
347
405
|
}, 5e3);
|
|
348
406
|
}
|
|
407
|
+
function startActiveTimeTracking() {
|
|
408
|
+
if (!lastActiveTimestamp) {
|
|
409
|
+
pageStartTime = Date.now();
|
|
410
|
+
}
|
|
411
|
+
lastActiveTimestamp = Date.now();
|
|
412
|
+
isPageVisible = true;
|
|
413
|
+
log("Active time tracking started/resumed");
|
|
414
|
+
}
|
|
415
|
+
function pauseActiveTimeTracking() {
|
|
416
|
+
if (lastActiveTimestamp && isPageVisible) {
|
|
417
|
+
activeTime += Date.now() - lastActiveTimestamp;
|
|
418
|
+
}
|
|
419
|
+
isPageVisible = false;
|
|
420
|
+
log("Active time tracking paused, accumulated:", activeTime, "ms");
|
|
421
|
+
}
|
|
422
|
+
function getCumulativeActiveTime() {
|
|
423
|
+
let total = activeTime;
|
|
424
|
+
if (isPageVisible && lastActiveTimestamp) {
|
|
425
|
+
total += Date.now() - lastActiveTimestamp;
|
|
426
|
+
}
|
|
427
|
+
return total;
|
|
428
|
+
}
|
|
429
|
+
function resetIdleTimer() {
|
|
430
|
+
lastActivityTime = Date.now();
|
|
431
|
+
if (isIdle) {
|
|
432
|
+
isIdle = false;
|
|
433
|
+
startActiveTimeTracking();
|
|
434
|
+
log("User returned from idle");
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
function sendHeartbeat() {
|
|
438
|
+
if (document.visibilityState === "hidden") {
|
|
439
|
+
log("Skipping heartbeat - tab hidden");
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
if (Date.now() - lastActivityTime > IDLE_TIMEOUT) {
|
|
443
|
+
if (!isIdle) {
|
|
444
|
+
isIdle = true;
|
|
445
|
+
pauseActiveTimeTracking();
|
|
446
|
+
log("User idle - pausing heartbeat");
|
|
447
|
+
}
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
const timeOnPage = getCumulativeActiveTime();
|
|
451
|
+
const timeOnPageSeconds = Math.round(timeOnPage / 1e3);
|
|
452
|
+
const event = buildEvent({
|
|
453
|
+
event_name: "engagement_heartbeat",
|
|
454
|
+
metadata: {
|
|
455
|
+
time_on_page_seconds: timeOnPageSeconds,
|
|
456
|
+
time_on_page_ms: timeOnPage,
|
|
457
|
+
heartbeat_interval_ms: heartbeatInterval,
|
|
458
|
+
is_active: isPageVisible && !isIdle
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
enqueueEvent(event);
|
|
462
|
+
log("Heartbeat sent:", timeOnPageSeconds, "seconds active");
|
|
463
|
+
}
|
|
464
|
+
function startHeartbeat() {
|
|
465
|
+
if (!heartbeatEnabled) {
|
|
466
|
+
log('Heartbeat disabled via data-heartbeat="false"');
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (heartbeatTimer)
|
|
470
|
+
clearInterval(heartbeatTimer);
|
|
471
|
+
startActiveTimeTracking();
|
|
472
|
+
heartbeatTimer = window.setInterval(() => {
|
|
473
|
+
sendHeartbeat();
|
|
474
|
+
}, heartbeatInterval);
|
|
475
|
+
log("Heartbeat started with interval:", heartbeatInterval, "ms");
|
|
476
|
+
}
|
|
477
|
+
function stopHeartbeat() {
|
|
478
|
+
if (heartbeatTimer) {
|
|
479
|
+
clearInterval(heartbeatTimer);
|
|
480
|
+
heartbeatTimer = null;
|
|
481
|
+
}
|
|
482
|
+
if (heartbeatEnabled) {
|
|
483
|
+
sendHeartbeat();
|
|
484
|
+
log("Heartbeat stopped, final time sent");
|
|
485
|
+
}
|
|
486
|
+
}
|
|
349
487
|
function trackPageView(options = {}) {
|
|
350
488
|
const event = buildEvent({
|
|
351
489
|
event_name: "pageview",
|
|
@@ -445,12 +583,20 @@ var BalancePixel = (() => {
|
|
|
445
583
|
});
|
|
446
584
|
window._balanceInitialPageviewFired = true;
|
|
447
585
|
trackPageView();
|
|
586
|
+
startHeartbeat();
|
|
587
|
+
["mousemove", "keydown", "scroll", "touchstart"].forEach((eventType) => {
|
|
588
|
+
document.addEventListener(eventType, resetIdleTimer, { passive: true });
|
|
589
|
+
});
|
|
448
590
|
window.addEventListener("beforeunload", () => {
|
|
591
|
+
stopHeartbeat();
|
|
449
592
|
flush();
|
|
450
593
|
});
|
|
451
594
|
document.addEventListener("visibilitychange", () => {
|
|
452
595
|
if (document.visibilityState === "hidden") {
|
|
596
|
+
pauseActiveTimeTracking();
|
|
453
597
|
flush();
|
|
598
|
+
} else {
|
|
599
|
+
startActiveTimeTracking();
|
|
454
600
|
}
|
|
455
601
|
});
|
|
456
602
|
}
|
package/dist/index.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var BalancePixel=(()=>{var te=Object.create;var _=Object.defineProperty;var re=Object.getOwnPropertyDescriptor;var ie=Object.getOwnPropertyNames;var oe=Object.getPrototypeOf,ae=Object.prototype.hasOwnProperty;var z=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(i,o)=>(typeof require<"u"?require:i)[o]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var se=(t,i)=>{for(var o in i)_(t,o,{get:i[o],enumerable:!0})},U=(t,i,o,d)=>{if(i&&typeof i=="object"||typeof i=="function")for(let l of ie(i))!ae.call(t,l)&&l!==o&&_(t,l,{get:()=>i[l],enumerable:!(d=re(i,l))||d.enumerable});return t};var ce=(t,i,o)=>(o=t!=null?te(oe(t)):{},U(i||!t||!t.__esModule?_(o,"default",{value:t,enumerable:!0}):o,t)),le=t=>U(_({},"__esModule",{value:!0}),t);var Se={};se(Se,{BalanceAnalytics:()=>j,getAttribution:()=>ye,getConsent:()=>xe,getFanIdHash:()=>me,getSessionId:()=>we,hasConsent:()=>be,identify:()=>ge,page:()=>fe,purchase:()=>pe,setConsent:()=>he,track:()=>de});var u=ce(z("react")),P=z("next/navigation");function ue(){let t=(0,P.usePathname)(),i=(0,P.useSearchParams)(),o=(0,u.useRef)(!0),d=(0,u.useRef)(null);return(0,u.useEffect)(()=>{if(typeof window>"u"||!window.balance)return;let l=t+(i?.toString()||"");if(d.current===l)return;if(o.current&&(o.current=!1,window._balanceInitialPageviewFired)){d.current=l,console.log("[BalanceAnalytics] Skipping initial pageview (script already fired)");return}let b=window.location.href,m=document.title;d.current=l,window.balance.page({url:b,title:m})},[t,i]),null}function j(){return u.default.createElement(u.Suspense,{fallback:null},u.default.createElement(ue,null))}(function(){let t=document.currentScript,i=t?.dataset.artistId,o=t?.dataset.projectId,d=t?.dataset.emulator==="true",l=t?.dataset.debug==="true";if(!i){console.error("[BALANCE Pixel] Error: data-artist-id attribute is required");return}let b="session_id",m="session_timestamp",N="attribution",R="fan_id_hash",O="balance_consent",H=60*60*1e3,E="balance_",A=d?"http://localhost:5001/artist-os-distro/us-central1/pixelEndpoint":"https://us-central1-artist-os-distro.cloudfunctions.net/pixelEndpoint",S=null,g=null,c=null,w={},f=[],C=null,p="session",s=(...e)=>{l&&console.log("[BALANCE Pixel]",...e)};function B(){try{return p==="local"?localStorage:sessionStorage}catch{return null}}function v(e){let n=B();if(!n)return null;try{let r=E+e,a=n.getItem(r);if(!a&&p==="session")try{a=localStorage.getItem(r)}catch{}return a}catch{return null}}function y(e,n){let r=B();if(r)try{r.setItem(E+e,n)}catch{}}function k(){if(p!=="local"){s("Upgrading storage tier: session -> local");try{let e=[];for(let n=0;n<sessionStorage.length;n++){let r=sessionStorage.key(n);r?.startsWith(E)&&e.push(r)}for(let n of e){let r=sessionStorage.getItem(n);r&&localStorage.setItem(n,r)}for(let n of e)sessionStorage.removeItem(n);p="local",s(`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 n=Math.random()*16|0;return(e==="x"?n:n&3|8).toString(16)})}function M(){try{let e=v(b),n=v(m);if(e&&n&&Date.now()-parseInt(n,10)<H)return y(m,Date.now().toString()),e;let r=F();return y(b,r),y(m,Date.now().toString()),r}catch{return F()}}function K(){let e=new URLSearchParams(window.location.search),n={};return["source","medium","campaign","content","term"].forEach(r=>{let a=e.get(`utm_${r}`);a&&(n[`utm_${r}`]=a)}),n}function J(){try{let e=v(N);if(e){w=JSON.parse(e),s("Loaded attribution:",w);return}let n=K();Object.keys(n).length>0&&(w=n,y(N,JSON.stringify(n)),s("Captured attribution:",w))}catch{}}function Y(){try{g=v(R)}catch{}}function q(){try{let e=localStorage.getItem(O);e&&(c=JSON.parse(e).preferences||null,s("Loaded consent:",c))}catch{}}function $(e){let n=c;c=e;try{let a={preferences:e,method:"explicit",version:1};localStorage.setItem(O,JSON.stringify(a)),s("Consent saved:",e)}catch(a){console.error("[BALANCE Pixel] Could not save consent:",a)}e.analytics===!0&&k();let r=h({event_name:"consent_updated",metadata:{consent_preferences:e,consent_method:"explicit",previous_consent:n||void 0}});x(r)}function G(){return c}function W(e){return c?.[e]===!0}async function Q(e){let n=e.toLowerCase().trim(),a=new TextEncoder().encode(n),T=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(T)).map(ne=>ne.toString(16).padStart(2,"0")).join("")}function h(e){let n={artist_id:i,fan_session_id:S,fan_id_hash:g||void 0,timestamp:new Date().toISOString(),source_url:window.location.href,referrer_url:document.referrer||void 0,user_agent:navigator.userAgent,...e,...w};return o&&!e.projectId&&(n.projectId=o),n}function x(e){f.push(e),s("Event queued:",e.event_name,"(queue:",f.length,")"),f.length>=10&&I()}async function I(){if(f.length===0)return;let e=[...f];f=[],s("Flushing",e.length,"events to",A);try{let n=await fetch(A,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({events:e}),keepalive:!0});if(!n.ok)throw new Error(`HTTP ${n.status}`);s("Events sent successfully")}catch(n){console.error("[BALANCE Pixel] Failed to send events:",n),f.length<50&&f.push(...e)}}function V(){C&&clearInterval(C),C=window.setInterval(()=>{f.length>0&&I()},5e3)}function L(e={}){let n=h({event_name:"pageview",page_title:e.title||document.title,source_url:e.url||window.location.href});x(n)}function X(e,n={}){let r=h({event_name:"custom",metadata:{event_type:e,...n}});x(r)}async function Z(e,n={}){try{if(c&&c.analytics===!1){s("Identify skipped - user declined analytics consent");return}g=await Q(e),c?.analytics===!0&&k(),y(R,g);let r=e.split("@"),a=r[0].charAt(0)+"***@"+(r[1]||"");s("Fan identified:",{name:n.name||"(no name)",email:a,hash:g.substring(0,16)+"...",traits:n,storageTier:p});let T=h({event_name:"identify",fan_id_hash:g,metadata:{email_sha256:g,traits:n,consent_preferences:c||void 0,storage_tier:p}});x(T)}catch(r){console.error("[BALANCE Pixel] Failed to identify:",r)}}function ee(e,n="USD",r={}){let a=h({event_name:"purchase",metadata:{revenue:e,currency:n,...r}});x(a)}function D(){q(),c?.analytics===!0?(p="local",s("Storage tier: local (analytics consent granted)")):(p="session",s("Storage tier: session (privacy by default)")),S=M(),Y(),c||(c={analytics:!0,marketing:!0,personalization:!0,timestamp:new Date().toISOString()},s("Default consent enabled (all tracking):",c),k()),J(),V(),s("Initialized",{artistId:i,projectId:o||"(none - will track to all projects)",sessionId:S,fanIdHash:g,consent:c,storageTier:p,useEmulator:d,endpoint:A}),window._balanceInitialPageviewFired=!0,L(),window.addEventListener("beforeunload",()=>{I()}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&I()})}window.balance={track:X,identify:Z,page:L,purchase:ee,getSessionId:()=>S,getFanIdHash:()=>g,getAttribution:()=>w,setConsent:$,getConsent:G,hasConsent:W},document.readyState==="loading"?document.addEventListener("DOMContentLoaded",D):D(),s("Pixel script loaded")})();var de=(t,i)=>{typeof window>"u"||(window.balance?window.balance.track(t,i):console.warn("[Balance Pixel] track() called before pixel initialized:",t))},ge=(t,i)=>typeof window>"u"?Promise.resolve():window.balance?window.balance.identify(t,i):(console.warn("[Balance Pixel] identify() called before pixel initialized"),Promise.resolve()),fe=t=>{typeof window>"u"||(window.balance?window.balance.page(t):console.warn("[Balance Pixel] page() called before pixel initialized"))},pe=(t,i,o)=>{typeof window>"u"||(window.balance?window.balance.purchase(t,i,o):console.warn("[Balance Pixel] purchase() called before pixel initialized"))},we=()=>typeof window>"u"?null:window.balance?.getSessionId()??null,me=()=>typeof window>"u"?null:window.balance?.getFanIdHash()??null,ye=()=>typeof window>"u"?{}:window.balance?.getAttribution()??{},he=t=>{typeof window>"u"||(window.balance?window.balance.setConsent(t):console.warn("[Balance Pixel] setConsent() called before pixel initialized"))},xe=()=>typeof window>"u"?null:window.balance?.getConsent()??null,be=t=>typeof window>"u"?!1:window.balance?.hasConsent(t)??!1;return le(Se);})();
|
|
1
|
+
var BalancePixel=(()=>{var Se=Object.create;var k=Object.defineProperty;var Ie=Object.getOwnPropertyDescriptor;var _e=Object.getOwnPropertyNames;var Ae=Object.getPrototypeOf,Pe=Object.prototype.hasOwnProperty;var Q=(i=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(i,{get:(r,s)=>(typeof require<"u"?require:r)[s]}):i)(function(i){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+i+'" is not supported')});var Ee=(i,r)=>{for(var s in r)k(i,s,{get:r[s],enumerable:!0})},X=(i,r,s,c)=>{if(r&&typeof r=="object"||typeof r=="function")for(let d of _e(r))!Pe.call(i,d)&&d!==s&&k(i,d,{get:()=>r[d],enumerable:!(c=Ie(r,d))||c.enumerable});return i};var ke=(i,r,s)=>(s=i!=null?Se(Ae(i)):{},X(r||!i||!i.__esModule?k(s,"default",{value:i,enumerable:!0}):s,i)),Ce=i=>X(k({},"__esModule",{value:!0}),i);var je={};Ee(je,{BalanceAnalytics:()=>Z,getAttribution:()=>Le,getConsent:()=>He,getFanIdHash:()=>Be,getSessionId:()=>Ue,hasConsent:()=>ze,identify:()=>Oe,page:()=>Ne,purchase:()=>Re,setConsent:()=>Fe,track:()=>De});var u=ke(Q("react")),C=Q("next/navigation");function Te(){let i=(0,C.usePathname)(),r=(0,C.useSearchParams)(),s=(0,u.useRef)(!0),c=(0,u.useRef)(null);return(0,u.useEffect)(()=>{if(typeof window>"u"||!window.balance)return;let d=i+(r?.toString()||"");if(c.current===d)return;if(s.current&&(s.current=!1,window._balanceInitialPageviewFired)){c.current=d,console.log("[BalanceAnalytics] Skipping initial pageview (script already fired)");return}let v=window.location.href,_=document.title;c.current=d,window.balance.page({url:v,title:_})},[i,r]),null}function Z(){return u.default.createElement(u.Suspense,{fallback:null},u.default.createElement(Te,null))}(function(){function i(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 r=null;function s(){if(!r)try{r=i(navigator.userAgent)}catch{r={device_type:"desktop",browser:"Unknown",os:"Unknown"}}return r}let c=document.currentScript,d=c?.dataset.artistId,v=c?.dataset.projectId,_=c?.dataset.emulator==="true",ee=c?.dataset.debug==="true",T=parseInt(c?.dataset.heartbeatInterval||"30000",10),H=c?.dataset.heartbeat!=="false";if(!d){console.error("[BALANCE Pixel] Error: data-artist-id attribute is required");return}let z="session_id",D="session_timestamp",j="attribution",M="fan_id_hash",K="balance_consent",te=60*60*1e3,O="balance_",N=_?"http://localhost:5001/artist-os-distro/us-central1/pixelEndpoint":"https://us-central1-artist-os-distro.cloudfunctions.net/pixelEndpoint",A=null,f=null,l=null,m={},g=[],R=null,p="session",w=null,ne=0,U=0,b=0,x=!0,ie=2*60*1e3,J=Date.now(),S=!1,a=(...e)=>{ee&&console.log("[BALANCE Pixel]",...e)};function Y(){try{return p==="local"?localStorage:sessionStorage}catch{return null}}function P(e){let t=Y();if(!t)return null;try{let n=O+e,o=t.getItem(n);if(!o&&p==="session")try{o=localStorage.getItem(n)}catch{}return o}catch{return null}}function I(e,t){let n=Y();if(n)try{n.setItem(O+e,t)}catch{}}function B(){if(p!=="local"){a("Upgrading storage tier: session -> local");try{let e=[];for(let t=0;t<sessionStorage.length;t++){let n=sessionStorage.key(t);n?.startsWith(O)&&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);p="local",a(`Storage tier upgraded, migrated ${e.length} items`)}catch(e){console.error("[BALANCE Pixel] Storage migration failed:",e)}}}function q(){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 re(){try{let e=P(z),t=P(D);if(e&&t&&Date.now()-parseInt(t,10)<te)return I(D,Date.now().toString()),e;let n=q();return I(z,n),I(D,Date.now().toString()),n}catch{return q()}}function ae(){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 oe(){try{let e=P(j);if(e){m=JSON.parse(e),a("Loaded attribution:",m);return}let t=ae();Object.keys(t).length>0&&(m=t,I(j,JSON.stringify(t)),a("Captured attribution:",m))}catch{}}function se(){try{f=P(M)}catch{}}function le(){try{let e=localStorage.getItem(K);e&&(l=JSON.parse(e).preferences||null,a("Loaded consent:",l))}catch{}}function ce(e){let t=l;l=e;try{let o={preferences:e,method:"explicit",version:1};localStorage.setItem(K,JSON.stringify(o)),a("Consent saved:",e)}catch(o){console.error("[BALANCE Pixel] Could not save consent:",o)}e.analytics===!0&&B();let n=h({event_name:"consent_updated",metadata:{consent_preferences:e,consent_method:"explicit",previous_consent:t||void 0}});y(n)}function de(){return l}function ue(e){return l?.[e]===!0}async function fe(e){let t=e.toLowerCase().trim(),o=new TextEncoder().encode(t),F=await crypto.subtle.digest("SHA-256",o);return Array.from(new Uint8Array(F)).map(xe=>xe.toString(16).padStart(2,"0")).join("")}function h(e){let t=s(),n={artist_id:d,fan_session_id:A,fan_id_hash:f||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,...e,...m};return v&&!e.projectId&&(n.projectId=v),n}function y(e){g.push(e),a("Event queued:",e.event_name,"(queue:",g.length,")"),g.length>=10&&E()}async function E(){if(g.length===0)return;let e=[...g];g=[],a("Flushing",e.length,"events to",N);try{let t=await fetch(N,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({events:e}),keepalive:!0});if(!t.ok)throw new Error(`HTTP ${t.status}`);a("Events sent successfully")}catch(t){console.error("[BALANCE Pixel] Failed to send events:",t),g.length<50&&g.push(...e)}}function ge(){R&&clearInterval(R),R=window.setInterval(()=>{g.length>0&&E()},5e3)}function L(){b||(ne=Date.now()),b=Date.now(),x=!0,a("Active time tracking started/resumed")}function $(){b&&x&&(U+=Date.now()-b),x=!1,a("Active time tracking paused, accumulated:",U,"ms")}function pe(){let e=U;return x&&b&&(e+=Date.now()-b),e}function me(){J=Date.now(),S&&(S=!1,L(),a("User returned from idle"))}function G(){if(document.visibilityState==="hidden"){a("Skipping heartbeat - tab hidden");return}if(Date.now()-J>ie){S||(S=!0,$(),a("User idle - pausing heartbeat"));return}let e=pe(),t=Math.round(e/1e3),n=h({event_name:"engagement_heartbeat",metadata:{time_on_page_seconds:t,time_on_page_ms:e,heartbeat_interval_ms:T,is_active:x&&!S}});y(n),a("Heartbeat sent:",t,"seconds active")}function we(){if(!H){a('Heartbeat disabled via data-heartbeat="false"');return}w&&clearInterval(w),L(),w=window.setInterval(()=>{G()},T),a("Heartbeat started with interval:",T,"ms")}function be(){w&&(clearInterval(w),w=null),H&&(G(),a("Heartbeat stopped, final time sent"))}function W(e={}){let t=h({event_name:"pageview",page_title:e.title||document.title,source_url:e.url||window.location.href});y(t)}function he(e,t={}){let n=h({event_name:"custom",metadata:{event_type:e,...t}});y(n)}async function ye(e,t={}){try{if(l&&l.analytics===!1){a("Identify skipped - user declined analytics consent");return}f=await fe(e),l?.analytics===!0&&B(),I(M,f);let n=e.split("@"),o=n[0].charAt(0)+"***@"+(n[1]||"");a("Fan identified:",{name:t.name||"(no name)",email:o,hash:f.substring(0,16)+"...",traits:t,storageTier:p});let F=h({event_name:"identify",fan_id_hash:f,metadata:{email_sha256:f,traits:t,consent_preferences:l||void 0,storage_tier:p}});y(F)}catch(n){console.error("[BALANCE Pixel] Failed to identify:",n)}}function ve(e,t="USD",n={}){let o=h({event_name:"purchase",metadata:{revenue:e,currency:t,...n}});y(o)}function V(){le(),l?.analytics===!0?(p="local",a("Storage tier: local (analytics consent granted)")):(p="session",a("Storage tier: session (privacy by default)")),A=re(),se(),l||(l={analytics:!0,marketing:!0,personalization:!0,timestamp:new Date().toISOString()},a("Default consent enabled (all tracking):",l),B()),oe(),ge(),a("Initialized",{artistId:d,projectId:v||"(none - will track to all projects)",sessionId:A,fanIdHash:f,consent:l,storageTier:p,useEmulator:_,endpoint:N}),window._balanceInitialPageviewFired=!0,W(),we(),["mousemove","keydown","scroll","touchstart"].forEach(e=>{document.addEventListener(e,me,{passive:!0})}),window.addEventListener("beforeunload",()=>{be(),E()}),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?($(),E()):L()})}window.balance={track:he,identify:ye,page:W,purchase:ve,getSessionId:()=>A,getFanIdHash:()=>f,getAttribution:()=>m,setConsent:ce,getConsent:de,hasConsent:ue},document.readyState==="loading"?document.addEventListener("DOMContentLoaded",V):V(),a("Pixel script loaded")})();var De=(i,r)=>{typeof window>"u"||(window.balance?window.balance.track(i,r):console.warn("[Balance Pixel] track() called before pixel initialized:",i))},Oe=(i,r)=>typeof window>"u"?Promise.resolve():window.balance?window.balance.identify(i,r):(console.warn("[Balance Pixel] identify() called before pixel initialized"),Promise.resolve()),Ne=i=>{typeof window>"u"||(window.balance?window.balance.page(i):console.warn("[Balance Pixel] page() called before pixel initialized"))},Re=(i,r,s)=>{typeof window>"u"||(window.balance?window.balance.purchase(i,r,s):console.warn("[Balance Pixel] purchase() called before pixel initialized"))},Ue=()=>typeof window>"u"?null:window.balance?.getSessionId()??null,Be=()=>typeof window>"u"?null:window.balance?.getFanIdHash()??null,Le=()=>typeof window>"u"?{}:window.balance?.getAttribution()??{},Fe=i=>{typeof window>"u"||(window.balance?window.balance.setConsent(i):console.warn("[Balance Pixel] setConsent() called before pixel initialized"))},He=()=>typeof window>"u"?null:window.balance?.getConsent()??null,ze=i=>typeof window>"u"?!1:window.balance?.hasConsent(i)??!1;return Ce(je);})();
|