@thrivestack/analytics-browser
Version:
ThriveStack Analytics Platform - Comprehensive web analytics tracking with privacy-first approach
3 lines (2 loc) • 30 kB
JavaScript
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).ThriveStack={})}(this,function(t){"use strict";class e{shouldSendVisitorGeneratedEvent(t){return null===this.lastSessionId||this.lastSessionId!==t}shouldSendVisitorGeneratedForNewSession(){if(!this.getDeviceIdFromCookie())return!1;if(this.getVisitorGeneratedSentFromCookie())return!1;const t=this.getLastSessionIdFromCookie();if(!t)return!0;return t!==this.getSessionId()}sendFingerprintEvent(t,e,i){try{const i=this.locationInfo||{},o={event_name:"visitor_generated",properties:{ip_address:e?.ip_address||this.ipAddress||"",country:i.country_code||"",region:i.region||"",city:i.city||""},user_id:this.userId||this.getUserIdFromCookie()||"",context:{session_id:this.getSessionId(),device_id:t,group_id:this.groupId||this.getGroupIdFromCookie()||"",source:this.source},timestamp:(new Date).toISOString()};this.queueEvent(o),this.visitorGeneratedSent=!0,this.lastSessionId=this.getSessionId(),this.setVisitorGeneratedSentCookie(!0),this.setLastSessionIdCookie(this.lastSessionId)}catch(t){}}sendFingerprintEventFromCache(t){try{const e=this.getLocationInfoFromCookie(),i={event_name:"visitor_generated",properties:{ip_address:e?.ip||this.ipAddress||"",country:e?.country_code||this.locationInfo?.country_code||"",region:e?.region||this.locationInfo?.region||"",city:e?.city||this.locationInfo?.city||""},user_id:this.userId||this.getUserIdFromCookie()||"",context:{session_id:this.getSessionId(),device_id:t,group_id:this.groupId||this.getGroupIdFromCookie()||"",source:this.source},timestamp:(new Date).toISOString()};this.queueEvent(i),this.visitorGeneratedSent=!0,this.lastSessionId=this.getSessionId(),this.setVisitorGeneratedSentCookie(!0),this.setLastSessionIdCookie(this.lastSessionId)}catch(t){}}constructor(t){"string"==typeof t&&(t={apiKey:t}),this.apiKey=t.apiKey,this.apiEndpoint=t.apiEndpoint||"https://azure.dev.app.thrivestack.ai/api",this.trackClicks=!1!==t.trackClicks,this.trackForms=!0===t.trackForms,this.enableConsent=!0===t.enableConsent,this.source=t.source||"",this.geoIpServiceUrl="https://ipinfo.io/json",this.ipAddress=null,this.locationInfo=null,this.interactionHistory=[],this.maxHistoryLength=20,this.consentCategories={functional:!0,analytics:!0===t.defaultConsent,marketing:!0===t.defaultConsent},this.userId=this.getUserIdFromCookie()||"",this.groupId=this.getGroupIdFromCookie()||"",this.deviceId=null,this.deviceIdReady=!1,this.fpPromise=null,this.locationReady=!1,this.firstPageVisitCaptured=!1,this.sessionTimeout=t.sessionTimeout||18e5,this.debounceDelay=t.debounceDelay??200,this.sessionUpdateTimer=null,this.lastClickTime=null,this.debugMode=t.debug||!1,this.enableDeveloperLogs=t.enableDeveloperLogs||!1,this._hierarchyCache=new WeakMap,this.subscriptionStatus=null,this.subscriptionCheckPromise=null,this.isTrackingEnabled=!0,this.capturePageVisit().catch(t=>{}),this.visitorGeneratedSent=this.getVisitorGeneratedSentFromCookie(),this.lastSessionId=this.getLastSessionIdFromCookie(),this.initializeDeviceId(),this.locationReady=!0,this.ipAddress=null,this.locationInfo=null,this.checkSubscriptionStatus().catch(t=>{console.warn("Failed to check subscription status, proceeding with tracking:",t)}),this.shouldTrack()&&(this.trackClicks&&this.autoCaptureClickEvents(),this.trackForms&&this.autoCaptureFormEvents())}async checkSubscriptionStatus(){try{if(this.subscriptionCheckPromise)return this.subscriptionCheckPromise;const t=this.getSubscriptionStatusFromCache();if(null!==t)return this.subscriptionStatus=t.status,this.isTrackingEnabled=!["cancelled","suspended"].includes(t.status),Promise.resolve(this.isTrackingEnabled);this.subscriptionCheckPromise=this.fetchSubscriptionStatus();const e=await this.subscriptionCheckPromise;return this.subscriptionCheckPromise=null,e}catch(t){return this.isTrackingEnabled=!0,this.subscriptionCheckPromise=null,!0}}async fetchSubscriptionStatus(){try{const t=await fetch(`${this.apiEndpoint}/subscriptionStatus`,{method:"GET",headers:{"Content-Type":"application/json","x-api-key":`${this.apiKey}`}});if(!t.ok)throw new Error(`HTTP error ${t.status}`);const e=await t.json();return this.subscriptionStatus=e.status,this.setSubscriptionStatusCache(e.status),this.isTrackingEnabled=!["cancelled","suspended"].includes(e.status),this.isTrackingEnabled}catch(t){return this.isTrackingEnabled=!0,!0}}getSubscriptionStatusFromCache(){try{const t="thrivestack_subscription_status",e=localStorage.getItem(t);if(!e)return null;const i=JSON.parse(e),o=Date.now()-i.timestamp;return o<864e5?i:(localStorage.removeItem(t),null)}catch(t){return null}}setSubscriptionStatusCache(t){try{const e="thrivestack_subscription_status",i={status:t,timestamp:Date.now()};localStorage.setItem(e,JSON.stringify(i))}catch(t){}}generateSimpleFingerprint(){this.startSimpleFingerprintGeneration()}async generateFingerprintInBackground(){try{await this.generateSimpleFingerprintHash()}catch(t){}}async initializeDeviceId(){try{const t=this.getSessionId(),e=this.getDeviceIdFromCookie();if(e&&""!==e.trim()){this.deviceId=e.trim(),this.deviceIdReady=!0;return this.shouldSendVisitorGeneratedEvent(t)&&(this.sendFingerprintEventFromCache(this.deviceId),this.visitorGeneratedSent=!0),void(this.lastSessionId=t)}this.generateFingerprintInBackground()}catch(t){this.deviceId=this.getSessionId(),this.deviceIdReady=!0}}startSimpleFingerprintGeneration(){this.generateSimpleFingerprintHash().catch(t=>{})}async generateSimpleFingerprintHash(){try{const t=performance.now(),e=performance.now(),i=await this.fetchIPAddress(),o=performance.now()-e,s=navigator.userAgent;i.locationData&&(this.storeLocationDataInCookie(i.locationData),this.ipAddress=i.locationData.ip,this.locationInfo={city:i.locationData.city||null,region:i.locationData.region||null,country:i.locationData.country||null,country_code:i.locationData.country_code||null,postal:i.locationData.postal||null,loc:i.locationData.latitude&&i.locationData.longitude?`${i.locationData.latitude},${i.locationData.longitude}`:null,timezone:i.locationData.timezone||null},this.locationReady=!0);const n={ip_address:i.ip,ip_api_used:i.apiUsed,ip_fetch_time_ms:Math.round(100*o)/100,user_agent:s,location_data_available:!!i.locationData,location_source:i.locationData?.source_api||null,timestamp:Date.now()},r=`${i.ip}${s}`,a=this.createMurmurHash(r),c=performance.now()-t;this.deviceId;this.deviceId=a,this.deviceIdReady=!0,this.setDeviceIdCookie(a),this.sendFingerprintEvent(a,n,c)}catch(t){throw this.locationReady||this.initializeLocationData(),t}}async fetchIPAddress(){const t=[{url:"https://ipwho.is/?output=json",extractIp:t=>t.ip,extractLocation:t=>this.extractIPWHOLocationData(t),name:"IPWHO",hasLocationData:!0},{url:"https://ipinfo.io/json",extractIp:t=>t.ip,extractLocation:t=>this.extractIPInfoLocationData(t),name:"IPinfo Lite",hasLocationData:!0},{url:"https://api.ipify.org/?format=json",extractIp:t=>t.ip,extractLocation:()=>null,name:"ipify",hasLocationData:!1}];for(const e of t)try{const t=await fetch(e.url,{method:"GET",headers:{Accept:"application/json"}});if(!t.ok)throw new Error(`HTTP error! status: ${t.status}`);const i=await t.json(),o=e.extractIp(i);if(!o||"string"!=typeof o)throw new Error(`Invalid IP address response from ${e.name}`);const s=e.hasLocationData?e.extractLocation(i):null;return{ip:o.trim(),apiUsed:e.name,locationData:s,fullResponse:i}}catch(t){console.warn(`Failed to fetch IP from ${e.name}:`,t)}throw new Error("Failed to fetch IP address from all available APIs")}extractIPWHOLocationData(t){try{return{ip:t.ip,country:t.country,country_code:t.country_code,region:t.region,region_code:t.region_code,city:t.city,postal:t.postal,latitude:t.latitude,longitude:t.longitude,timezone:t.timezone?.id||t.timezone,timezone_offset:t.timezone?.utc,isp:t.connection?.isp,org:t.connection?.org,asn:t.connection?.asn,continent:t.continent,continent_code:t.continent_code,source_api:"IPWHO",timestamp:Date.now()}}catch(t){return console.warn("Failed to extract IPWHO location data:",t),null}}extractIPInfoLocationData(t){try{const[e,i]=t.loc?t.loc.split(",").map(Number):[null,null];return{ip:t.ip,country:null,country_code:t.country,region:t.region,region_code:null,city:t.city,postal:t.postal,latitude:e,longitude:i,timezone:t.timezone,timezone_offset:null,isp:null,org:t.org,asn:null,continent:null,continent_code:null,source_api:"IPinfo Lite",timestamp:Date.now()}}catch(t){return console.warn("Failed to extract IPinfo location data:",t),null}}storeLocationDataInCookie(t){try{this.setLocationInfoCookie(t)}catch(t){}}createMurmurHash(t){return this.murmurHash3(t,2538058380).toString(16).padStart(8,"0")}murmurHash3(t,e=2538058380){let i,o,s=e;const n=3&t.length,r=t.length-n;let a=0;const c=3432918353,d=461845907;for(;a<r;)o=255&t.charCodeAt(a)|(255&t.charCodeAt(++a))<<8|(255&t.charCodeAt(++a))<<16|(255&t.charCodeAt(++a))<<24,++a,o=(65535&o)*c+(((o>>>16)*c&65535)<<16)&4294967295,o=o<<15|o>>>17,o=(65535&o)*d+(((o>>>16)*d&65535)<<16)&4294967295,s^=o,s=s<<13|s>>>19,i=5*(65535&s)+((5*(s>>>16)&65535)<<16)&4294967295,s=27492+(65535&i)+(((i>>>16)+58964&65535)<<16);switch(o=0,n){case 3:o^=(255&t.charCodeAt(a+2))<<16;case 2:o^=(255&t.charCodeAt(a+1))<<8;case 1:o^=255&t.charCodeAt(a),o=(65535&o)*c+(((o>>>16)*c&65535)<<16)&4294967295,o=o<<15|o>>>17,o=(65535&o)*d+(((o>>>16)*d&65535)<<16)&4294967295,s^=o}return s^=t.length,s^=s>>>16,s=2246822507*(65535&s)+((2246822507*(s>>>16)&65535)<<16)&4294967295,s^=s>>>13,s=3266489909*(65535&s)+((3266489909*(s>>>16)&65535)<<16)&4294967295,s^=s>>>16,s>>>0}setDeviceIdCookie(t){if(!t)return;const e=new Date;e.setTime(e.getTime()+63072e6);const i=`thrivestack_device_id=${t};expires=${e.toUTCString()};path=/;SameSite=Lax`;try{document.cookie=i}catch(t){}}async initializeLocationData(){try{const t=this.getLocationInfoFromCookie();if(t)return this.ipAddress=t.ip||null,this.locationInfo={city:t.city||null,region:t.region||null,country:t.country||null,postal:t.postal||null,loc:t.loc||null,timezone:t.timezone||null},void(this.locationReady=!0);let e=0;const i=20,o=()=>{this.locationReady&&this.ipAddress&&this.locationInfo||(e++,e<i?setTimeout(o,100):this.fetchIpAndLocationInfo())};setTimeout(o,100)}catch(t){this.locationReady=!0}}async fetchIpAndLocationInfo(){try{if(this.locationReady&&this.ipAddress&&this.locationInfo)return;const t=this.getLocationInfoFromCookie();if(t)return this.ipAddress=t.ip||null,this.locationInfo={city:t.city||null,region:t.region||null,country:t.country||null,postal:t.postal||null,loc:t.loc||null,timezone:t.timezone||null},void(this.locationReady=!0);const e=await fetch(this.geoIpServiceUrl);if(!e.ok)throw new Error(`HTTP error ${e.status}`);const i=await e.json();this.ipAddress=i.ip||null,this.locationInfo={city:i.city||null,region:i.region||null,country:i.country||null,postal:i.postal||null,loc:i.loc||null,timezone:i.timezone||null},this.setLocationInfoCookie(i),this.locationReady=!0}catch(t){this.ipAddress=null,this.locationInfo=null,this.locationReady=!0}}setLocationInfoCookie(t){if(!t)return;try{const e=btoa(JSON.stringify(t)),i=new Date;i.setTime(i.getTime()+63072e6);const o=`thrivestack_location_info=${e};expires=${i.toUTCString()};path=/;SameSite=Lax`;document.cookie=o}catch(t){}}getLocationInfoFromCookie(){const t="thrivestack_location_info";try{const e=document.cookie.split(";");for(let i=0;i<e.length;i++){const o=e[i].trim();if(0===o.indexOf(t+"=")){const t=o.substring(26),e=atob(t);return JSON.parse(e)}}}catch(t){this.removeLocationInfoCookie()}return null}removeLocationInfoCookie(){try{document.cookie="thrivestack_location_info=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;SameSite=Lax"}catch(t){}}async init(t="",e=""){try{t&&this.setUserId(t),e&&(this.source=e)}catch(t){}}shouldTrack(){return!0}setConsent(t,e){this.consentCategories.hasOwnProperty(t)&&(this.consentCategories[t]=e)}setUserId(t){this.userId=t,this.setUserIdCookie(t)}setGroupId(t){this.groupId=t,this.setGroupIdCookie(t)}setSource(t){this.source=t}setUserIdCookie(t){if(!t)return;const e=new Date;e.setTime(e.getTime()+31536e6);const i=`thrivestack_user_id=${encodeURIComponent(t)};expires=${e.toUTCString()};path=/;SameSite=Lax`;try{document.cookie=i}catch(t){}}getUserIdFromCookie(){const t="thrivestack_user_id",e=document.cookie.split(";");for(let i=0;i<e.length;i++){const o=e[i].trim();if(0===o.indexOf(t+"=")){const t=o.substring(20);return decodeURIComponent(t)}}return null}setGroupIdCookie(t){if(!t)return;const e=new Date;e.setTime(e.getTime()+31536e6);const i=`thrivestack_group_id=${encodeURIComponent(t)};expires=${e.toUTCString()};path=/;SameSite=Lax`;try{document.cookie=i}catch(t){}}getGroupIdFromCookie(){const t="thrivestack_group_id",e=document.cookie.split(";");for(let i=0;i<e.length;i++){const o=e[i].trim();if(0===o.indexOf(t+"=")){const t=o.substring(21);return decodeURIComponent(t)}}return null}getVisitorGeneratedSentFromCookie(){const t="thrivestack_visitor_generated_sent",e=document.cookie.split(";");for(let i=0;i<e.length;i++){const o=e[i].trim();if(0===o.indexOf(t+"=")){return"true"===o.substring(35)}}return!1}setVisitorGeneratedSentCookie(t){const e=new Date;e.setTime(e.getTime()+31536e6);const i=`thrivestack_visitor_generated_sent=${t};expires=${e.toUTCString()};path=/;SameSite=Lax`;try{document.cookie=i}catch(t){}}getLastSessionIdFromCookie(){const t="thrivestack_last_session_id",e=document.cookie.split(";");for(let i=0;i<e.length;i++){const o=e[i].trim();if(0===o.indexOf(t+"=")){const t=o.substring(28);return decodeURIComponent(t)}}return null}setLastSessionIdCookie(t){const e=new Date;e.setTime(e.getTime()+31536e6);const i=`thrivestack_last_session_id=${encodeURIComponent(t)};expires=${e.toUTCString()};path=/;SameSite=Lax`;try{document.cookie=i}catch(t){}}queueEvent(t){Array.isArray(t)||(t=[t]),this.track(t).catch(t=>{console.error("Failed to send events:",t)})}async track(t){if(!this.apiKey)throw Error("Initialize the ThriveStack instance before sending telemetry data.");const e=t.filter(t=>this.isTrackingEnabled||"visitor_generated"===t.event_name);if(0===e.length)return console.debug("Tracking disabled due to subscription status, skipping events"),{success:!1,reason:"tracking_disabled"};t=e;const i=this.getSessionId(),o=this.getDeviceIdFromCookie()||i,s=t.map(t=>({...t,context:{...t.context||{},device_id:t.context&&t.context.device_id?t.context.device_id:o,session_id:t.context&&t.context.session_id?t.context.session_id:i},timestamp:(new Date).toISOString()})).map(t=>this.cleanPIIFromEventData(t));try{const t=await fetch(`${this.apiEndpoint}/track`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":`${this.apiKey}`},body:JSON.stringify(s)});if(!t.ok)throw Error(`HTTP error ${t.status}: ${await t.text()}`);if(204===t.status)return{success:!0,status:"no_content"};return await t.json()}catch(t){throw t}}async identify(t){if(!this.apiKey)throw Error("Initialize the ThriveStack instance before sending telemetry data.");if(!this.isTrackingEnabled)return console.debug("Tracking disabled due to subscription status, skipping events"),{success:!1,reason:"tracking_disabled"};try{let e="";if(Array.isArray(t)&&t.length>0){e=t[t.length-1].user_id||""}else{e=t.user_id||""}e&&this.setUserId(e);const i=this.getSessionId(),o=this.deviceId||this.getDeviceIdFromCookie(),s=this.groupId||this.getGroupIdFromCookie()||"",n=Array.isArray(t)?t.map(t=>({...t,context:{...t.context||{},device_id:t.context&&t.context.device_id||o,session_id:t.context&&t.context.session_id||i,group_id:t.context&&t.context.group_id||s,source:t.context&&t.context.source||this.source}})):{...t,context:{...t.context||{},device_id:t.context&&t.context.device_id||o,session_id:t.context&&t.context.session_id||i,group_id:t.context&&t.context.group_id||s,source:t.context&&t.context.source||this.source}},r=await fetch(`${this.apiEndpoint}/identify`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":`${this.apiKey}`},body:JSON.stringify(n)});if(!r.ok)throw Error(`HTTP error ${r.status}: ${await r.text()}`);return await r.json()}catch(t){throw console.error("Failed to send identification data:",t instanceof Error?t.message:"Unknown error"),t}}async group(t){if(!this.apiKey)throw Error("Initialize the ThriveStack instance before sending telemetry data.");if(!this.isTrackingEnabled)return console.debug("Tracking disabled due to subscription status, skipping events"),{success:!1,reason:"tracking_disabled"};try{let e="";if(Array.isArray(t)&&t.length>0){e=t[t.length-1].group_id||""}else{e=t.group_id||""}e&&this.setGroupId(e);const i=this.getSessionId(),o=this.deviceId||this.getDeviceIdFromCookie(),s=this.groupId||this.getGroupIdFromCookie()||"",n=Array.isArray(t)?t.map(t=>({...t,context:{...t.context||{},device_id:t.context&&t.context.device_id||o,session_id:t.context&&t.context.session_id||i,group_id:t.context&&t.context.group_id||s,source:t.context&&t.context.source||this.source}})):{...t,context:{...t.context||{},device_id:t.context&&t.context.device_id||o,session_id:t.context&&t.context.session_id||i,group_id:t.context&&t.context.group_id||s,source:t.context&&t.context.source||this.source}},r=await fetch(`${this.apiEndpoint}/group`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":`${this.apiKey}`},body:JSON.stringify(n)});if(!r.ok)throw Error(`HTTP error ${r.status}: ${await r.text()}`);return await r.json()}catch(t){throw console.error("Failed to send group data:",t instanceof Error?t.message:"Unknown error"),t}}getUtmParameters(){const t=new URLSearchParams(window.location.search);return{utm_campaign:t.get("utm_campaign")||null,utm_medium:t.get("utm_medium")||null,utm_source:t.get("utm_source")||null,utm_term:t.get("utm_term")||null,utm_content:t.get("utm_content")||null}}getDeviceId(){return this.deviceIdReady&&this.deviceId?this.deviceId:null}getDeviceIdFromCookie(){const t="thrivestack_device_id",e=document.cookie.split(";");for(let i=0;i<e.length;i++){const o=e[i].trim();if(0===o.indexOf(t+"="))return o.substring(22)}return null}getSessionId(){const t="thrivestack_session";try{const e=document.cookie.split(";");let i=null;for(let o=0;o<e.length;o++){const s=e[o].trim();if(0===s.indexOf(t+"=")){i=s.substring(20);break}}if(!i)return this.createNewSession();try{const t=JSON.parse(atob(i));if(t.sessionId&&t.lastActivity){const e=new Date(t.lastActivity),i=new Date;return i.getTime()-e.getTime()<this.sessionTimeout?t.sessionId:(console.debug("Session expired, creating new session"),this.createNewSession())}return this.createNewSession()}catch(t){return console.debug("Migrating old session format to new format"),this.migrateOldSession(i)}}catch(t){return console.warn("Error getting session ID:",t),this.createNewSession()}}createNewSession(){const t="session_"+Math.random().toString(36).substring(2,15),e=(new Date).toISOString(),i={sessionId:t,startTime:e,lastActivity:e};return this.setSessionCookie(i),t}migrateOldSession(t){const e=(new Date).toISOString(),i={sessionId:t,startTime:e,lastActivity:e};return this.setSessionCookie(i),t}setSessionCookie(t){try{const e=btoa(JSON.stringify(t)),i=new Date;i.setTime(i.getTime()+31536e6);const o=`thrivestack_session=${e};expires=${i.toUTCString()};path=/;SameSite=Lax`;document.cookie=o}catch(t){console.warn("Could not store session in cookie:",t)}}updateSessionActivity(){this.sessionUpdateTimer&&clearTimeout(this.sessionUpdateTimer),this.sessionUpdateTimer=setTimeout(()=>{this.updateSessionActivityImmediate()},this.debounceDelay)}updateSessionActivityImmediate(){const t="thrivestack_session";try{const e=document.cookie.split(";");let i=null;for(let o=0;o<e.length;o++){const s=e[o].trim();if(0===s.indexOf(t+"=")){i=s.substring(20);break}}if(i)try{const t=JSON.parse(atob(i));t.lastActivity=(new Date).toISOString(),this.setSessionCookie(t)}catch(t){this.createNewSession()}else this.createNewSession()}catch(t){console.warn("Could not update session activity:",t)}}hasThriveStackEventClass(t){if(!t||!t.className)return;return("string"==typeof t.className?t.className.split(/\s+/):Array.from(t.classList)).find(t=>t.includes("thrivestack-event"))}async capturePageVisit(){if(this.shouldSendVisitorGeneratedForNewSession()){const t=this.getDeviceIdFromCookie();t&&this.sendFingerprintEventFromCache(t)}if(this.firstPageVisitCaptured&&!this.isTrackingEnabled)return void console.debug("Subsequent page visit tracking disabled due to subscription status");const t=this.getUtmParameters(),e=this.getSessionId();this.updateSessionActivity();const i=this.getDeviceIdFromCookie()||e,o=this.userId||this.getUserIdFromCookie()||"",s=this.groupId||this.getGroupIdFromCookie()||"",n=[{event_name:"page_visit",properties:{page_title:document.title,page_url:window.location.href,page_path:window.location.pathname,page_referrer:document.referrer||"",language:navigator.language||"",ip_address:this.ipAddress||"",city:this.locationInfo?.city||"",region:this.locationInfo?.region||"",country:this.locationInfo?.country||"",postal:this.locationInfo?.postal||"",loc:this.locationInfo?.loc||"",timezone:this.locationInfo?.timezone||"",...t},user_id:o,context:{group_id:s,device_id:i,session_id:e,source:this.source},timestamp:(new Date).toISOString()}];this.queueEvent(n),this.firstPageVisitCaptured=!0;const r=this.getSessionId();this.lastSessionId=r,this.setLastSessionIdCookie(r)}captureClickEvent(t){if(!this.isTrackingEnabled)return void console.debug("Click event tracking disabled due to subscription status");const e=t.target,i=this.hasThriveStackEventClass(e);if(!i)return void console.debug("Element does not have 'thrivestack-event' class, skipping click tracking");const o=Date.now();if(this.lastClickTime&&o-this.lastClickTime<300)return;this.lastClickTime=o;const s=e.getBoundingClientRect(),n=this.getUtmParameters(),r=this.getSessionId();this.updateSessionActivity();const a=this.getDeviceIdFromCookie()||r,c=this.userId||this.getUserIdFromCookie()||"",d=this.groupId||this.getGroupIdFromCookie()||"",l=[{event_name:"element_click",properties:{page_title:document.title,page_url:window.location.href,page_path:window.location.pathname,element_text:e.textContent?.trim()||null,element_tag:e.tagName||null,element_id:e.id||null,element_href:e.getAttribute("href")||null,element_aria_label:e.getAttribute("aria-label")||null,element_class:e.className||null,element_position_left:s.left||null,element_position_top:s.top||null,viewport_height:window.innerHeight,viewport_width:window.innerWidth,page_referrer:document.referrer||null,language:navigator.language||null,ip_address:this.ipAddress,city:this.locationInfo?.city||null,region:this.locationInfo?.region||null,country:this.locationInfo?.country||null,postal:this.locationInfo?.postal||null,loc:this.locationInfo?.loc||null,timezone:this.locationInfo?.timezone||null,activity:i.split(":")[1]||i||null,...n},user_id:c,context:{group_id:d,device_id:a,session_id:r,source:this.source},timestamp:(new Date).toISOString()}];this.queueEvent(l)}captureFormEvent(t,e){if(!this.isTrackingEnabled)return void console.debug("Form event tracking disabled due to subscription status");const i=t.target,o=this.getSessionId();this.updateSessionActivity();const s=this.getDeviceIdFromCookie()||o,n=this.userId||this.getUserIdFromCookie()||"",r=this.groupId||this.getGroupIdFromCookie()||"",a=i._trackingData||{filledFields:new Set},c=Array.from(i.elements).filter(t=>!["submit","button","reset"].includes(t.type)).length,d=Math.round(a.filledFields.size/Math.max(c,1)*100),l=[{event_name:`form_${e}`,properties:{page_title:document.title,page_url:window.location.href,form_id:i.id||null,form_name:i.name||null,form_action:i.action||null,form_fields:c,form_completion:d,interaction_time:a.startTime?Date.now()-a.startTime:null},user_id:n,context:{group_id:r,device_id:s,session_id:o,source:this.source},timestamp:(new Date).toISOString()}];this.queueEvent(l)}autoCapturePageVisit(){if(!this.isTrackingEnabled)return void console.debug("Auto page visit tracking disabled due to subscription status");window.addEventListener("popstate",()=>this.capturePageVisit());const t=history.pushState;history.pushState=(...e)=>{t.apply(history,e),this.capturePageVisit()}}autoCaptureClickEvents(){this.isTrackingEnabled?document.addEventListener("click",t=>this.captureClickEvent(t)):console.debug("Auto click tracking disabled due to subscription status")}autoCaptureFormEvents(){this.isTrackingEnabled?(document.addEventListener("submit",t=>{this.captureFormEvent(t,"submit")}),document.addEventListener("input",t=>{const e=t.target;if(e.form){const i=e.form;i._trackingData||(i._trackingData={startTime:Date.now(),filledFields:new Set});const o=t.target;""!==o.value.trim()?i._trackingData.filledFields.add(o.name||o.id):i._trackingData.filledFields.delete(o.name||o.id)}}),document.addEventListener("visibilitychange",()=>{"hidden"===document.visibilityState&&document.querySelectorAll("form").forEach(t=>{if(t._trackingData&&t._trackingData.filledFields.size>0){const e={target:t};this.captureFormEvent(e,"abandoned")}})})):console.debug("Auto form tracking disabled due to subscription status")}cleanPIIFromEventData(t){return JSON.parse(JSON.stringify(t))}async setUser(t,e,i={}){if(!t)return console.warn("setUser: userId is required"),null;const o=this.getUserIdFromCookie(),s=!o||o!==t;if(this.setUserId(t),!s)return console.debug("Skipping identify API call - user already set in cookie:",t),null;try{const o=[{user_id:t,context:{device_id:"",session_id:"",group_id:"",source:""},traits:{user_email:e,user_name:e,...i},timestamp:(new Date).toISOString()}];console.debug("Making identify API call for user:",t);const s=await this.identify(o);return console.debug("Identify API call successful"),s}catch(t){throw console.error("Failed to make identify API call:",t),t}}async setGroup(t,e,i,o={}){if(!t)return console.warn("setGroup: groupId is required"),null;const s=this.getGroupIdFromCookie(),n=!s||s!==t;if(this.setGroupId(t),!n)return console.debug("Skipping group API call - group already set in cookie:",t),null;try{const s=[{group_id:t,user_id:this.userId||this.getUserIdFromCookie()||void 0,context:{device_id:"",session_id:"",group_id:"",source:""},traits:{group_type:"Account",account_domain:e,account_name:i,...o},timestamp:(new Date).toISOString()}];console.debug("Making group API call for group:",t);const n=await this.group(s);return console.debug("Group API call successful"),n}catch(t){throw console.error("Failed to make group API call:",t),t}}async reportEmailAbuse(t){try{if(!t||"string"!=typeof t||""===t.trim())throw new Error("Email address is required and must be a non-empty string");const e=t.trim();if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e))throw new Error("Invalid email address format");let i;i=this.apiEndpoint.endsWith("/api/track")?this.apiEndpoint.replace("/api/track","/api/email-abuse"):this.apiEndpoint.endsWith("/api")?this.apiEndpoint+"/email-abuse":this.apiEndpoint.replace(/\/$/,"")+"/api/email-abuse";const o=await fetch(i,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":this.apiKey},body:JSON.stringify({email:e})});if(!o.ok){const t=await o.text();throw new Error(`Email abuse API failed: ${o.status} ${o.statusText} - ${t}`)}const s=await o.json(),n=1===s.isAbuse,r=s.reasonForAbuse;return{success:!0,message:n?`Email marked as abuse. Reasons: ${Object.entries(r).filter(([t,e])=>!0===e).map(([t,e])=>t).join(", ")||"General abuse detected"}`:"Email is not flagged as abuse",data:s}}catch(t){return{success:!1,message:t instanceof Error?t.message:"Unknown error occurred"}}}enableDebugMode(){this.debugMode=!0;const t=this.track.bind(this);this.track=async function(e){return console.group("ThriveStack Debug: Sending Events"),console.log("Events:",JSON.parse(JSON.stringify(e))),console.groupEnd(),t(e)},console.log("ThriveStack debug mode enabled")}}!function(){if("undefined"==typeof document||"undefined"==typeof window)return;const t=document.currentScript||Array.from(document.getElementsByTagName("script")).pop();if(!t)return;const i=t?.getAttribute("data-api-key")||t?.getAttribute("api-key"),o=t?.getAttribute("data-source")||t?.getAttribute("source");if(!i||!o)return i||console.error("Missing required API key for ThriveStack initialization. Use data-api-key or api-key attribute."),void(o||console.error("Missing required source for ThriveStack initialization. Use data-source or source attribute."));console.debug("Creating ThriveStack instance with fingerprint priority...");const s=new e({apiKey:i,trackClicks:"false"!==t?.getAttribute("data-track-clicks")&&"false"!==t?.getAttribute("track-clicks"),trackForms:"true"===t?.getAttribute("data-track-forms")||"true"===t?.getAttribute("track-forms"),enableDeveloperLogs:"true"===t?.getAttribute("data-enable-developer-logs")||"true"===t?.getAttribute("enable-developer-logs"),source:o});s.init().catch(t=>{console.error("Failed to initialize ThriveStack:",t)}),window.thriveStack=s,console.debug("ThriveStack initialization complete (fingerprint generation started)")}();let i=null;t.getInstance=function(){return i},t.init=async function(t,o,s){if(i)return void console.warn("ThriveStack already initialized");const n={apiKey:t,source:o||"",...s};i=new e(n),await i.init()},t.isInitialized=function(){return null!==i},t.setGroup=async function(t,e,o,s={}){if(!i)throw new Error("ThriveStack not initialized. Call init() first.");return await i.setGroup(t,e,o,s)},t.setUser=async function(t,e,o={}){if(!i)throw new Error("ThriveStack not initialized. Call init() first.");return await i.setUser(e,t,o)},t.track=async function(t,e={}){if(!i)throw new Error("ThriveStack not initialized. Call init() first.");const o={event_name:t,properties:e,context:{device_id:i.getDeviceId(),group_id:"",session_id:"",source:""},timestamp:(new Date).toISOString()};return await i.track([o])},t.trackEvents=async function(t){if(!i)throw new Error("ThriveStack not initialized. Call init() first.");return await i.track(t)}});
//# sourceMappingURL=thrivestack.js.map