@twinalyze/web-analytics 1.0.18 → 1.0.19
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/cdn.global.min.js +4 -4
- package/dist/cdn.global.min.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var E=Object.create;var f=Object.defineProperty;var
|
|
2
|
-
`),
|
|
1
|
+
var E=Object.create;var f=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var U=Object.getPrototypeOf,z=Object.prototype.hasOwnProperty;var P=(l,e)=>{for(var s in e)f(l,s,{get:e[s],enumerable:!0})},S=(l,e,s,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of L(e))!z.call(l,i)&&i!==s&&f(l,i,{get:()=>e[i],enumerable:!(t=C(e,i))||t.enumerable});return l};var p=(l,e,s)=>(s=l!=null?E(U(l)):{},S(e||!l||!l.__esModule?f(s,"default",{value:l,enumerable:!0}):s,l)),R=l=>S(f({},"__esModule",{value:!0}),l);var B={};P(B,{TwinalyzeAnalytics:()=>A,default:()=>O});module.exports=R(B);var g=p(require("crypto-js")),I=p(require("html2canvas")),T=p(require("ua-parser-js")),b=T.UAParser;function m(){return typeof crypto!="undefined"&&crypto.randomUUID?crypto.randomUUID():Math.random().toString(16).slice(2)+Date.now().toString(16)}var D=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,l=>{let e=Math.random()*16|0;return(l==="x"?e:e&3|8).toString(16)}),_=l=>l===null||typeof l!="object"?JSON.stringify(l):Array.isArray(l)?"["+l.map(_).join(",")+"]":"{"+Object.keys(l).filter(s=>l[s]!==void 0).sort().map(s=>JSON.stringify(s)+":"+_(l[s])).join(",")+"}",y=class{constructor(){this.cfg=null,this.deviceId=null,this.sessionId=null,this.sessionStartTime=null,this.sessionReady=!1,this._booting=!1,this._disabled=!1,this.pendingEvents=[],this.batchQueue=[],this.scrollFired=!1,this._initialized=!1,this._flushTimer=null,this.indexId=0,this._indexKey=null,this._lastShotAt=0,this._shotInFlight=!1,this.batchConfig={eventsBatchSize:10,flushIntervalMs:5e3,sessionTimeout:3e5}}_getAppPackageName(){try{return window.location.hostname.replace(/^www\./i,"").toLowerCase()}catch{return"unknown"}}_getLanguageCode(e){return e?e.split("-")[0].toLowerCase():"not_found"}_isLikelyBot(){try{let e=navigator.userAgent||"",s=/bot|crawler|spider|crawling|googlebot|bingbot|yandexbot|duckduckbot|baiduspider|facebookexternalhit|twitterbot|linkedinbot|whatsapp|telegrambot|pinterest|ahrefs|semrush|mj12bot|dotbot|petalbot|bytespider|gptbot|ccbot|claudebot|perplexitybot|headlesschrome|puppeteer|playwright|phantomjs|lighthouse|pagespeed|gtmetrix|pingdom/i;return!!(!e||s.test(e)||navigator.webdriver===!0)}catch{return!1}}init(e){var c,a,r,d;if(typeof window=="undefined"||this._initialized)return;this._initialized=!0;let s=e.apiBaseUrl||e.endpoint||e.socketUrl||"https://api.twinalyze.com",t=s.endsWith("/")?s.slice(0,-1):s,i={enabled:!1,apiUrl:(a=(c=e.screenActivity)==null?void 0:c.apiUrl)!=null?a:`${t}/api/app/screenActivity/screenActivityDetails_post`,captureOn:["pageView","elementClick","formStart","formSubmit","scrollDepth","searchResultsView","customEvent"],throttleMs:2e3,jpegQuality:.7},o=typeof e.screenActivity=="object"?{...i,...e.screenActivity}:{...i};if(this.cfg={apiKey:e.apiKey,secretKey:e.secretKey,version:e.version,apiBaseUrl:t,debug:e.debug!==!1,encrypt:e.encrypt!==!1,persistSession:e.persistSession!==!1,sessionKey:"twinalyze_session_id",source:e.source||"web",indexId:(r=e.indexId)!=null?r:null,enhancedMeasurement:{pageViews:!0,scrolls:!0,outboundClicks:!0,siteSearch:{params:["q","s","search","query"]},formInteractions:!0,fileDownloads:{extensions:["pdf","zip","apk","doc","docx","xls","xlsx","ppt","pptx"]},...e.enhancedMeasurement||{}},screenActivity:o},!this.cfg.apiKey){console.log("[Twinalyze] \u274C Missing apiKey");return}if(this._isLikelyBot()){this._disabled=!0,this.sessionReady=!1,this.pendingEvents=[],this.batchQueue=[],(d=this.cfg)!=null&&d.debug&&console.log("[Twinalyze] \u{1F916} Bot detected. SDK stopped.",{userAgent:navigator.userAgent,webdriver:navigator.webdriver===!0});return}this.deviceId=this._getOrCreateDeviceId();let n="twinalyze_session_start_time";this.cfg.persistSession&&(this.sessionId=sessionStorage.getItem(this.cfg.sessionKey)||null,this.sessionStartTime=sessionStorage.getItem(n)||null),this.sessionId?this.sessionStartTime||(this.sessionStartTime=new Date().toISOString(),this.cfg.persistSession&&sessionStorage.setItem(n,this.sessionStartTime)):(this.sessionId=`sess_${m()}`,this.sessionStartTime=new Date().toISOString(),this.cfg.persistSession&&(sessionStorage.setItem(this.cfg.sessionKey,this.sessionId),sessionStorage.setItem(n,this.sessionStartTime))),this._setupEnhancedMeasurement(),this._loadAndSendUnsentEvents(),this._setupVisibilityAndUnloadHandlers(),this._startFlow()}track(e,s={}){var t;if(!this._disabled&&!(this.cfg&&this.cfg.manualEventStatus===!1)){if((t=this.cfg)!=null&&t.debug&&console.log("[Twinalyze][CUSTOM]",e,s),!this.sessionReady){this.pendingEvents.push({eventName:e,properties:{...this._pageContext(),...s},ts:Date.now(),eventType:"manual"});return}this._emitEventAdd(e,{...this._pageContext(),...s},"manual")}}async _startFlow(){if(!this._disabled&&!this._booting){this._booting=!0;try{let e=await this._fetchApi("/api/web/sdk/init",{});if(this.cfg.debug&&console.log("[Twinalyze][FLOW] initRes: HAR HAR MAHADEV",e),!e||e.success===!1){this._disabled=!0,this.sessionReady=!1,this.cfg.screenActivity.enabled=!1,this.cfg.debug&&console.log("[Twinalyze][FLOW] \u{1F6D1} STOP (init failed)");return}let s=e.data||{};this.cfg.screenActivity.enabled=!1,s.eventData?(this.cfg.autoEventStatus=s.eventData.autoEventStatus!==!1,this.cfg.manualEventStatus=s.eventData.manualEventStatus!==!1):(this.cfg.autoEventStatus=!0,this.cfg.manualEventStatus=!0),s.eventsBatchConfig&&(this.batchConfig={eventsBatchSize:s.eventsBatchConfig.eventsBatchSize||10,flushIntervalMs:s.eventsBatchConfig.flushIntervalMs||5e3,sessionTimeout:s.eventsBatchConfig.sessionTimeout||3e5}),this._initIndexId();let t={properties:this._userProperties(),deviceProperties:this._deviceProperties()},i=await this._fetchApi("/api/web/sdk/identify",t);if(this.cfg.debug&&console.log("[Twinalyze][FLOW] identifyRes:",i),!i||i.success===!1){this._disabled=!0,this.sessionReady=!1,this.cfg.debug&&console.log("[Twinalyze][FLOW] \u{1F6D1} STOP (identify failed)");return}this.sessionReady=!0,this.pendingEvents.length>0&&this.pendingEvents.splice(0,this.pendingEvents.length).forEach(n=>this._emitEventAdd(n.eventName,n.properties||{},n.eventType||"auto"))}finally{this._booting=!1}}}async _fetchApi(e,s){var v;if(this._disabled||this._isLikelyBot())return(v=this.cfg)!=null&&v.debug&&console.log("[Twinalyze][API] skipped because bot/disabled",e),null;let t=`${this.cfg.apiBaseUrl}${e}`,i=D(),o=new Date().toISOString(),n=s?JSON.stringify(s):"{}",c=_(JSON.parse(n)),a=g.default.SHA256(c).toString(g.default.enc.Hex),d=["POST",e,o,i,this.deviceId,a].join(`
|
|
2
|
+
`),h=this.cfg.secretKey||"",u=g.default.HmacSHA256(d,h).toString(g.default.enc.Hex);this.cfg.debug&&console.log("[Twinalyze][API] Request signing trace:",{endpoint:e,nonce:i,timestamp:o,canonicalBody:c,bodyHash:a,baseString:d,signature:u});let k={"Content-Type":"application/json","x-deviceId":this.deviceId,"x-api-key":this.cfg.apiKey,"x-analytics-sdk-version":this.cfg.version||"1.0.0","x-ad-analytics-sdk-version":"","x-app-package-name":this._getAppPackageName(),"x-platform":"javascript","x-app-version-name":"1.0.0","x-app-version-mode":this.cfg.debug?"debug":"release","x-sdk-version":"latest","x-timestamp":o,"x-nonce":i,"x-signature":u};try{return await(await fetch(t,{method:"POST",headers:k,body:n,keepalive:!0})).json()}catch(w){return this.cfg.debug&&console.log(`[Twinalyze][API] Error calling ${e}:`,w),null}}async _waitForRenderStable(){var s;await new Promise(t=>requestAnimationFrame(()=>requestAnimationFrame(t)));try{(s=document.fonts)!=null&&s.ready&&await document.fonts.ready}catch{}let e=Array.from(document.images||[]).filter(t=>!t.complete);await Promise.allSettled(e.map(t=>new Promise(i=>{t.addEventListener("load",i,{once:!0}),t.addEventListener("error",i,{once:!0})})))}async _captureViewportJpegBlob(){var o,n,c;await this._waitForRenderStable();let e=(((o=this.cfg.screenActivity)==null?void 0:o.capture)||"viewport")==="viewport",s=Math.min(2,window.devicePixelRatio||1),t=await(0,I.default)(document.documentElement,{useCORS:!0,allowTaint:!1,backgroundColor:"#ffffff",logging:!1,...e?{x:window.scrollX,y:window.scrollY,width:window.innerWidth,height:window.innerHeight}:{x:0,y:0,width:document.documentElement.scrollWidth,height:document.documentElement.scrollHeight},scale:s,onclone:a=>{let r=a.createElement("style");r.textContent=`
|
|
3
3
|
* { animation: none !important; transition: none !important; caret-color: transparent !important; }
|
|
4
|
-
`,r.head.appendChild(c)}}),i=(a=(n=this.cfg.screenActivity)==null?void 0:n.maxWidth)!=null?a:1280;if(e.width>i){let r=i/e.width,c=document.createElement("canvas");return c.width=Math.round(e.width*r),c.height=Math.round(e.height*r),c.getContext("2d").drawImage(e,0,0,c.width,c.height),await new Promise(h=>{var u,d;return c.toBlob(h,"image/jpeg",(d=(u=this.cfg.screenActivity)==null?void 0:u.jpegQuality)!=null?d:.7)})}return await new Promise(r=>{var c,h;return e.toBlob(r,"image/jpeg",(h=(c=this.cfg.screenActivity)==null?void 0:c.jpegQuality)!=null?h:.7)})}async _uploadScreenActivity({eventName:t,properties:s}){var o,n,a;if(!((o=this.cfg.screenActivity)!=null&&o.enabled)||!this.sessionReady)return;let e=Date.now(),i=(a=(n=this.cfg.screenActivity)==null?void 0:n.throttleMs)!=null?a:2e3;if(!(e-this._lastShotAt<i)&&!this._shotInFlight){this._shotInFlight=!0,this._lastShotAt=e;try{let r=await this._captureViewportJpegBlob();if(!r)return;let c=new FormData;c.append("apiKey",this.cfg.apiKey),c.append("screenName",document.title||"unknown"),c.append("identifier",JSON.stringify(s||{})),c.append("appInfo",JSON.stringify({appVersion:this.cfg.version||"1.0.0",platform:"javascript"})),c.append("description",t||"event"),c.append("image",r,`shot_${Date.now()}.jpg`);let h={"x-deviceId":this.deviceId,"x-api-key":this.cfg.apiKey,"x-analytics-sdk-version":this.cfg.version||"1.0.0","x-ad-analytics-sdk-version":"","x-app-package-name":this._getAppPackageName(),"x-platform":"javascript","x-app-version-name":"1.0.0","x-app-version-mode":this.cfg.debug?"debug":"release","x-sdk-version":"latest"};await fetch(this.cfg.screenActivity.apiUrl,{method:"POST",headers:h,body:c})}catch(r){this.cfg.debug&&console.log("[Twinalyze][SCREENSHOT] error:",(r==null?void 0:r.message)||r)}finally{this._shotInFlight=!1}}}_emitEventAdd(t,s={},e="auto"){if(!this.sessionReady)return;this._checkSessionTimeoutBeforeEvent(),this._indexKey==null&&this._initIndexId();let i=this._nextIndexId(),o={uniqueSessionId:this.sessionId,date:new Date().toISOString(),eventType:e,eventName:t,properties:{eventName:t,...s},indexId:i,source:this.cfg.source};this.batchQueue.length===0&&(this._flushTimer&&clearTimeout(this._flushTimer),this._flushTimer=setTimeout(()=>this._flushBatchQueue(),this.batchConfig.flushIntervalMs)),this.batchQueue.push(o),this.cfg.debug&&console.log("[Twinalyze][EVENT_QUEUED]",t,s,i),this.batchQueue.length>=this.batchConfig.eventsBatchSize&&this._flushBatchQueue()}async _flushBatchQueue(){if(this._flushTimer&&(clearTimeout(this._flushTimer),this._flushTimer=null),!this.sessionReady||!this.batchQueue.length)return;let t=this.batchQueue.splice(0,this.batchQueue.length);this.cfg.debug&&console.log(`[Twinalyze][FLUSHING] ${t.length} events...`);let s={eventsBatch:[{events:t.map(i=>({appInfo:{appVersion:this.cfg.version||"1.0.0",platform:"javascript"},date:i.date,eventName:i.eventName,eventType:i.eventType,indexId:i.indexId,properties:i.properties})),startDate:this.sessionStartTime||t[0].date,uniqueSessionId:this.sessionId}]},e=await this._fetchApi("/api/web/sdk/eventsBatch",s);this.cfg.debug&&console.log("[Twinalyze][FLUSH_RES]",e),(!e||e.success===!1)&&this.cfg.debug&&console.log("[Twinalyze][FLUSH_FAIL] events dropped")}_trackAuto(t,s={}){var e;if(!this._disabled&&!(this.cfg&&this.cfg.autoEventStatus===!1)){if((e=this.cfg)!=null&&e.debug&&console.log("[Twinalyze][AUTO]",t,s),!this.sessionReady){this.pendingEvents.push({eventName:t,properties:s,ts:Date.now(),eventType:"auto"});return}this._emitEventAdd(t,s,"auto")}}_setupEnhancedMeasurement(){let t=this.cfg.enhancedMeasurement||{};if(t.pageViews){let s=this._pageViewInfo();this._trackAuto("pageView",s);let e=()=>{this.scrollFired=!1;let n=this._pageViewInfo();this._trackAuto("pageView",n),this._fireSiteSearch()},i=history.pushState,o=history.replaceState;history.pushState=(...n)=>{i.apply(history,n),e()},history.replaceState=(...n)=>{o.apply(history,n),e()},window.addEventListener("popstate",e)}if(t.scrolls&&window.addEventListener("scroll",()=>{if(this.scrollFired)return;let s=document.documentElement,e=window.scrollY||s.scrollTop,i=s.scrollHeight-s.clientHeight;if(i<=0)return;let o=Math.round(e/i*100);o>=90&&(this.scrollFired=!0,this._trackAuto("scrollDepth",{scrollPercent:o,...this._pageContext()}))},{passive:!0}),t.outboundClicks&&document.addEventListener("click",s=>{var r,c,h;let e=this._getClickTarget(s);if(!e)return;let i=e.el,o=(i.tagName||"").toLowerCase(),n=this._getTextFromElement(i),a={clickType:e.kind,elementTag:o,elementText:n||"not_found",elementId:i.id||null,elementName:((r=i.getAttribute)==null?void 0:r.call(i,"name"))||null,elementRole:((c=i.getAttribute)==null?void 0:c.call(i,"role"))||null,...this._pageContext&&this._pageContext()||{}};if(e.kind==="link"){let u=i.getAttribute("href")||"";if(/^(javascript:|mailto:|tel:)/i.test(u))return;let d;try{d=new URL(i.href)}catch{return}a.linkUrl=d.href,a.linkDomain=d.hostname,a.linkPath=d.pathname,a.isExternalLink=d.hostname!==location.hostname}e.kind==="button"&&(a.buttonType=((h=i.getAttribute)==null?void 0:h.call(i,"type"))||null,a.disabled=!!i.disabled),this._trackAuto("elementClick",a)},!0),t.siteSearch&&this._fireSiteSearch(),t.formInteractions){let s=new WeakSet;document.addEventListener("focusin",e=>{let i=e.target&&e.target.closest?e.target.closest("form"):null;!i||s.has(i)||(s.add(i),this._trackAuto("formStart",{formId:i.id||void 0,formName:i.getAttribute("name")||void 0,formActionUrl:i.action||void 0,...this._pageContext()}))}),document.addEventListener("submit",e=>{let i=e.target;i&&this._trackAuto("formSubmit",{formStatus:"success",formActionUrl:i.action||void 0,formDestinationUrl:location.href,formId:i.id||void 0,formName:i.getAttribute("name")||void 0,...this._pageContext()})},!0)}if(t.fileDownloads&&t.fileDownloads.extensions){let s=t.fileDownloads.extensions.map(e=>String(e).toLowerCase());document.addEventListener("click",e=>{let i=e.target&&e.target.closest?e.target.closest("a"):null;if(!i||!i.href)return;let o=i.href.split("?")[0].toLowerCase(),n=o.split(".").pop()||"";s.includes(n)&&this._trackAuto("fileDownload",{linkUrl:i.href,fileExtension:n,fileName:o.split("/").pop(),...this._pageContext()})},!0)}}_fireSiteSearch(){let t=this.cfg.enhancedMeasurement||{};if(!t.siteSearch)return;let s=t.siteSearch.params||["q","s","search","query"],e=new URLSearchParams(location.search),i=s.find(n=>e.get(n)),o=i?e.get(i):null;o&&o.trim()&&this._trackAuto("searchResultsView",{searchKey:i,searchTerm:o.trim(),searchUrl:location.href,...this._pageContext()})}_getTextFromElement(t){var o,n;if(!t)return null;let s=(o=t.getAttribute)==null?void 0:o.call(t,"aria-label");if(s&&s.trim())return s.trim();let e=(n=t.getAttribute)==null?void 0:n.call(t,"title");if(e&&e.trim())return e.trim();let i=(t.innerText||t.textContent||"").trim();return i?i.length>80?i.slice(0,80):i:null}_cssPath(t){if(!t||!t.tagName)return null;let s=[],e=t,i=0;for(;e&&e.nodeType===1&&i<5;){let o=e.tagName.toLowerCase();if(e.id){o+=`#${e.id}`,s.unshift(o);break}let n=e.className&&typeof e.className=="string"?e.className.trim().split(/\s+/).slice(0,2).join("."):"";n&&(o+=`.${n}`),s.unshift(o),e=e.parentElement,i++}return s.join(" > ")}_getClickTarget(t){let s=typeof t.composedPath=="function"?t.composedPath():null,e=s&&s.length?s[0]:t.target,i=(c,h)=>c&&c.closest?c.closest(h):null,o=i(e,"a[href]");if(o)return{el:o,kind:"link"};let n=i(e,'button, input[type="button"], input[type="submit"], input[type="reset"], [role="button"]');if(n)return{el:n,kind:"button"};let a=i(e,'[role="menuitem"], [role="menuitemradio"], [role="menuitemcheckbox"], [role="option"]');if(a)return{el:a,kind:"dropdown_item"};let r=i(e,'[role="link"], [tabindex]:not([tabindex="-1"]), [aria-haspopup], [contenteditable="true"]');return r?{el:r,kind:"interactive"}:null}_deviceProperties(){var o,n,a,r,c,h,u;let s=new S(navigator.userAgent).getResult(),e=navigator.connection||navigator.mozConnection||navigator.webkitConnection;return{device_screen_mode:(n=(o=window.matchMedia)==null?void 0:o.call(window,"(prefers-color-scheme: dark)"))!=null&&n.matches?"dark":"light",is_data_on:navigator.onLine?"true":"false",screen_width:String(screen.width),screen_height:String(screen.height),viewport_width:String(window.innerWidth),viewport_height:String(window.innerHeight),color_depth:String(screen.colorDepth||"not_found"),browser_name:((a=s.browser)==null?void 0:a.name)||"not_found",browser_version:((r=s.browser)==null?void 0:r.version)||"not_found",os_name:((c=s.os)==null?void 0:c.name)||"not_found",os_version:((h=s.os)==null?void 0:h.version)||"not_found",app_language:navigator.language||"not_found",cpu_cores:navigator.hardwareConcurrency!=null?String(navigator.hardwareConcurrency):"not_found",touch_points:navigator.maxTouchPoints!=null?String(navigator.maxTouchPoints):"0",network_type:(e==null?void 0:e.effectiveType)||"not_found",network_speed_mbps:(e==null?void 0:e.downlink)!=null?String(e.downlink):"not_found",network_latency_ms:(e==null?void 0:e.rtt)!=null?String(e.rtt):"not_found",data_saver_enabled:(e==null?void 0:e.saveData)!=null?String(e.saveData):"not_found",user_agent:navigator.userAgent,analytics_version:((u=this.cfg)==null?void 0:u.version)||"not_found"}}_normalizeUrl(t){if(!t)return t;try{let s=new URL(t),e=new URLSearchParams,i=["source","medium","campaign","term","content"];for(let[o,n]of s.searchParams.entries())o.toLowerCase().startsWith("utm")?e.set(o.toLowerCase(),n):i.includes(o.toLowerCase())?e.set("utm_"+o.toLowerCase(),n):e.set(o,n);return s.search=e.toString(),s.href}catch{return t}}_userProperties(){var r;let s=new S(navigator.userAgent).getResult(),e=this._normalizeUrl(location.href),o=new URL(e).searchParams,n=c=>o.get(c),a="not_found";return{device_type:((r=s.device)==null?void 0:r.type)||(matchMedia("(pointer:coarse)").matches?"mobile":"desktop"),density:String(window.devicePixelRatio||1),timeZone:Intl.DateTimeFormat().resolvedOptions().timeZone||"not_found",ram:navigator.deviceMemory!=null?String(navigator.deviceMemory):"not_found",device_language:navigator.language||"not_found",landing_url:e,utm_source:n("utm_source")||a,utm_medium:n("utm_medium")||a,utm_campaign:n("utm_campaign")||a,utm_term:n("utm_term")||a,utm_content:n("utm_content")||a,utm_gclid:n("gclid")||a,utm_fbclid:n("fbclid")||a,utm_msclkid:n("msclkid")||a}}_initIndexId(){this._indexKey=`twinalyze_index_${this.sessionId}`,this.indexId=parseInt(sessionStorage.getItem(this._indexKey)||"0",10),Number.isNaN(this.indexId)&&(this.indexId=0)}_nextIndexId(){return this.indexId+=1,this._indexKey&&sessionStorage.setItem(this._indexKey,String(this.indexId)),this.indexId}_getLastEventTime(){try{return parseInt(sessionStorage.getItem("twinalyze_last_event_time")||"0",10)}catch{return 0}}_setLastEventTime(){try{sessionStorage.setItem("twinalyze_last_event_time",String(Date.now()))}catch{}}_createNewSessionAfterIdle(){var s,e;let t="twinalyze_session_start_time";this.sessionId=`sess_${p()}`,this.sessionStartTime=new Date().toISOString(),((s=this.cfg)==null?void 0:s.persistSession)!==!1&&(sessionStorage.setItem(this.cfg.sessionKey,this.sessionId),sessionStorage.setItem(t,this.sessionStartTime)),this._indexKey=null,this.indexId=0,this._initIndexId(),(e=this.cfg)!=null&&e.debug&&console.log("[Twinalyze][SESSION] New session created after idle:",this.sessionId)}_checkSessionTimeoutBeforeEvent(){var e;let t=this._getLastEventTime(),s=((e=this.batchConfig)==null?void 0:e.sessionTimeout)||3e5;t&&Date.now()-t>s&&this._createNewSessionAfterIdle(),this._setLastEventTime()}_getOrCreateDeviceId(){let t="twinalyze_device_id",s=localStorage.getItem(t);if(s)return s;let e=p();return localStorage.setItem(t,e),e}_pageContext(){let t="twinalyze_last_page_location",s=document.title,e=this._normalizeUrl(location.href),i=location.hostname,o=location.pathname,a=sessionStorage.getItem(t)||document.referrer||null,r="direct";if(a)try{r=new URL(a).hostname===i?"internal":"external"}catch{r="external"}return{pageTitle:s,pageDomain:i,pageUrl:e,pagePath:o,previousPageUrl:a&&this._normalizeUrl(a),previousPageType:r}}_pageViewInfo(){let t="twinalyze_last_page_location",s=this._pageContext();return sessionStorage.setItem(t,s.pageUrl),{...s}}_saveUnsentEvents(){var t;try{let s=[...this.pendingEvents,...this.batchQueue];s.length>0?localStorage.setItem("twinalyze_unsent_events",JSON.stringify(s)):localStorage.removeItem("twinalyze_unsent_events")}catch(s){(t=this.cfg)!=null&&t.debug&&console.log("[Twinalyze] Error saving unsent events:",s)}}_loadAndSendUnsentEvents(){var t,s;try{let e=localStorage.getItem("twinalyze_unsent_events");if(e){let i=JSON.parse(e);localStorage.removeItem("twinalyze_unsent_events"),Array.isArray(i)&&i.length>0&&((t=this.cfg)!=null&&t.debug&&console.log(`[Twinalyze] Loaded ${i.length} unsent events from localStorage`),i.forEach(o=>{this.sessionReady?this._emitEventAdd(o.eventName,o.properties||{},o.eventType||"auto"):this.pendingEvents.push(o)}))}}catch(e){(s=this.cfg)!=null&&s.debug&&console.log("[Twinalyze] Error loading unsent events:",e)}}_setupVisibilityAndUnloadHandlers(){let t=()=>{var e,i;(e=this.cfg)!=null&&e.debug&&console.log("[Twinalyze] Visibility changed or page hiding. Flushing events...");let s=this.sessionReady;!this.sessionReady&&this.pendingEvents.length>0&&((i=this.cfg)!=null&&i.debug&&console.log("[Twinalyze] Force flushing pending events on unload..."),this.pendingEvents.splice(0,this.pendingEvents.length).forEach(n=>{this._indexKey==null&&this._initIndexId();let a=this._nextIndexId();this.batchQueue.push({uniqueSessionId:this.sessionId,date:new Date(n.ts||Date.now()).toISOString(),eventType:n.eventType||"auto",eventName:n.eventName,properties:{eventName:n.eventName,...n.properties},indexId:a,source:this.cfg.source})}),this.sessionReady=!0),this.batchQueue.length>0&&this._flushBatchQueue(),s||(this.sessionReady=s),this._saveUnsentEvents()};document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&t()}),window.addEventListener("pagehide",()=>{t()})}},x=new y,R=["init","track"],T=Object.fromEntries(R.map(l=>[l,x[l].bind(x)]));var N=T;
|
|
4
|
+
`,a.head.appendChild(r)}}),i=(c=(n=this.cfg.screenActivity)==null?void 0:n.maxWidth)!=null?c:1280;if(t.width>i){let a=i/t.width,r=document.createElement("canvas");return r.width=Math.round(t.width*a),r.height=Math.round(t.height*a),r.getContext("2d").drawImage(t,0,0,r.width,r.height),await new Promise(d=>{var h,u;return r.toBlob(d,"image/jpeg",(u=(h=this.cfg.screenActivity)==null?void 0:h.jpegQuality)!=null?u:.7)})}return await new Promise(a=>{var r,d;return t.toBlob(a,"image/jpeg",(d=(r=this.cfg.screenActivity)==null?void 0:r.jpegQuality)!=null?d:.7)})}async _uploadScreenActivity({eventName:e,properties:s}){var o,n,c;if(!((o=this.cfg.screenActivity)!=null&&o.enabled)||!this.sessionReady)return;let t=Date.now(),i=(c=(n=this.cfg.screenActivity)==null?void 0:n.throttleMs)!=null?c:2e3;if(!(t-this._lastShotAt<i)&&!this._shotInFlight){this._shotInFlight=!0,this._lastShotAt=t;try{let a=await this._captureViewportJpegBlob();if(!a)return;let r=new FormData;r.append("apiKey",this.cfg.apiKey),r.append("screenName",document.title||"unknown"),r.append("identifier",JSON.stringify(s||{})),r.append("appInfo",JSON.stringify({appVersion:this.cfg.version||"1.0.0",platform:"javascript"})),r.append("description",e||"event"),r.append("image",a,`shot_${Date.now()}.jpg`);let d={"x-deviceId":this.deviceId,"x-api-key":this.cfg.apiKey,"x-analytics-sdk-version":this.cfg.version||"1.0.0","x-ad-analytics-sdk-version":"","x-app-package-name":this._getAppPackageName(),"x-platform":"javascript","x-app-version-name":"1.0.0","x-app-version-mode":this.cfg.debug?"debug":"release","x-sdk-version":"latest"};await fetch(this.cfg.screenActivity.apiUrl,{method:"POST",headers:d,body:r})}catch(a){this.cfg.debug&&console.log("[Twinalyze][SCREENSHOT] error:",(a==null?void 0:a.message)||a)}finally{this._shotInFlight=!1}}}_emitEventAdd(e,s={},t="auto"){if(!this.sessionReady)return;this._checkSessionTimeoutBeforeEvent(),this._indexKey==null&&this._initIndexId();let i=this._nextIndexId(),o={uniqueSessionId:this.sessionId,date:new Date().toISOString(),eventType:t,eventName:e,properties:{eventName:e,...s},indexId:i,source:this.cfg.source};this.batchQueue.length===0&&(this._flushTimer&&clearTimeout(this._flushTimer),this._flushTimer=setTimeout(()=>this._flushBatchQueue(),this.batchConfig.flushIntervalMs)),this.batchQueue.push(o),this.cfg.debug&&console.log("[Twinalyze][EVENT_QUEUED]",e,s,i),this.batchQueue.length>=this.batchConfig.eventsBatchSize&&this._flushBatchQueue()}async _flushBatchQueue(){if(this._flushTimer&&(clearTimeout(this._flushTimer),this._flushTimer=null),!this.sessionReady||!this.batchQueue.length)return;let e=this.batchQueue.splice(0,this.batchQueue.length);this.cfg.debug&&console.log(`[Twinalyze][FLUSHING] ${e.length} events...`);let s={eventsBatch:[{events:e.map(i=>({appInfo:{appVersion:this.cfg.version||"1.0.0",platform:"javascript"},date:i.date,eventName:i.eventName,eventType:i.eventType,indexId:i.indexId,properties:i.properties})),startDate:this.sessionStartTime||e[0].date,uniqueSessionId:this.sessionId}]},t=await this._fetchApi("/api/web/sdk/eventsBatch",s);this.cfg.debug&&console.log("[Twinalyze][FLUSH_RES]",t),(!t||t.success===!1)&&this.cfg.debug&&console.log("[Twinalyze][FLUSH_FAIL] events dropped")}_trackAuto(e,s={}){var t;if(!this._disabled&&!(this.cfg&&this.cfg.autoEventStatus===!1)){if((t=this.cfg)!=null&&t.debug&&console.log("[Twinalyze][AUTO]",e,s),!this.sessionReady){this.pendingEvents.push({eventName:e,properties:s,ts:Date.now(),eventType:"auto"});return}this._emitEventAdd(e,s,"auto")}}_setupEnhancedMeasurement(){let e=this.cfg.enhancedMeasurement||{};if(e.pageViews){let s=this._pageViewInfo();this._trackAuto("pageView",s);let t=()=>{this.scrollFired=!1;let n=this._pageViewInfo();this._trackAuto("pageView",n),this._fireSiteSearch()},i=history.pushState,o=history.replaceState;history.pushState=(...n)=>{i.apply(history,n),t()},history.replaceState=(...n)=>{o.apply(history,n),t()},window.addEventListener("popstate",t)}if(e.scrolls&&window.addEventListener("scroll",()=>{if(this.scrollFired)return;let s=document.documentElement,t=window.scrollY||s.scrollTop,i=s.scrollHeight-s.clientHeight;if(i<=0)return;let o=Math.round(t/i*100);o>=90&&(this.scrollFired=!0,this._trackAuto("scrollDepth",{scrollPercent:o,...this._pageContext()}))},{passive:!0}),e.outboundClicks&&document.addEventListener("click",s=>{var a,r,d;let t=this._getClickTarget(s);if(!t)return;let i=t.el,o=(i.tagName||"").toLowerCase(),n=this._getTextFromElement(i),c={clickType:t.kind,elementTag:o,elementText:n||"not_found",elementId:i.id||null,elementName:((a=i.getAttribute)==null?void 0:a.call(i,"name"))||null,elementRole:((r=i.getAttribute)==null?void 0:r.call(i,"role"))||null,...this._pageContext&&this._pageContext()||{}};if(t.kind==="link"){let h=i.getAttribute("href")||"";if(/^(javascript:|mailto:|tel:)/i.test(h))return;let u;try{u=new URL(i.href)}catch{return}c.linkUrl=u.href,c.linkDomain=u.hostname,c.linkPath=u.pathname,c.isExternalLink=u.hostname!==location.hostname}t.kind==="button"&&(c.buttonType=((d=i.getAttribute)==null?void 0:d.call(i,"type"))||null,c.disabled=!!i.disabled),this._trackAuto("elementClick",c)},!0),e.siteSearch&&this._fireSiteSearch(),e.formInteractions){let s=new WeakSet;document.addEventListener("focusin",t=>{let i=t.target&&t.target.closest?t.target.closest("form"):null;!i||s.has(i)||(s.add(i),this._trackAuto("formStart",{formId:i.id||void 0,formName:i.getAttribute("name")||void 0,formActionUrl:i.action||void 0,...this._pageContext()}))}),document.addEventListener("submit",t=>{let i=t.target;i&&this._trackAuto("formSubmit",{formStatus:"success",formActionUrl:i.action||void 0,formDestinationUrl:location.href,formId:i.id||void 0,formName:i.getAttribute("name")||void 0,...this._pageContext()})},!0)}if(e.fileDownloads&&e.fileDownloads.extensions){let s=e.fileDownloads.extensions.map(t=>String(t).toLowerCase());document.addEventListener("click",t=>{let i=t.target&&t.target.closest?t.target.closest("a"):null;if(!i||!i.href)return;let o=i.href.split("?")[0].toLowerCase(),n=o.split(".").pop()||"";s.includes(n)&&this._trackAuto("fileDownload",{linkUrl:i.href,fileExtension:n,fileName:o.split("/").pop(),...this._pageContext()})},!0)}}_fireSiteSearch(){let e=this.cfg.enhancedMeasurement||{};if(!e.siteSearch)return;let s=e.siteSearch.params||["q","s","search","query"],t=new URLSearchParams(location.search),i=s.find(n=>t.get(n)),o=i?t.get(i):null;o&&o.trim()&&this._trackAuto("searchResultsView",{searchKey:i,searchTerm:o.trim(),searchUrl:location.href,...this._pageContext()})}_getTextFromElement(e){var o,n;if(!e)return null;let s=(o=e.getAttribute)==null?void 0:o.call(e,"aria-label");if(s&&s.trim())return s.trim();let t=(n=e.getAttribute)==null?void 0:n.call(e,"title");if(t&&t.trim())return t.trim();let i=(e.innerText||e.textContent||"").trim();return i?i.length>80?i.slice(0,80):i:null}_cssPath(e){if(!e||!e.tagName)return null;let s=[],t=e,i=0;for(;t&&t.nodeType===1&&i<5;){let o=t.tagName.toLowerCase();if(t.id){o+=`#${t.id}`,s.unshift(o);break}let n=t.className&&typeof t.className=="string"?t.className.trim().split(/\s+/).slice(0,2).join("."):"";n&&(o+=`.${n}`),s.unshift(o),t=t.parentElement,i++}return s.join(" > ")}_getClickTarget(e){let s=typeof e.composedPath=="function"?e.composedPath():null,t=s&&s.length?s[0]:e.target,i=(r,d)=>r&&r.closest?r.closest(d):null,o=i(t,"a[href]");if(o)return{el:o,kind:"link"};let n=i(t,'button, input[type="button"], input[type="submit"], input[type="reset"], [role="button"]');if(n)return{el:n,kind:"button"};let c=i(t,'[role="menuitem"], [role="menuitemradio"], [role="menuitemcheckbox"], [role="option"]');if(c)return{el:c,kind:"dropdown_item"};let a=i(t,'[role="link"], [tabindex]:not([tabindex="-1"]), [aria-haspopup], [contenteditable="true"]');return a?{el:a,kind:"interactive"}:null}_deviceProperties(){var o,n,c,a,r,d,h;let s=new b(navigator.userAgent).getResult(),t=navigator.connection||navigator.mozConnection||navigator.webkitConnection;return{device_screen_mode:(n=(o=window.matchMedia)==null?void 0:o.call(window,"(prefers-color-scheme: dark)"))!=null&&n.matches?"dark":"light",is_data_on:navigator.onLine?"true":"false",screen_width:String(screen.width),screen_height:String(screen.height),viewport_width:String(window.innerWidth),viewport_height:String(window.innerHeight),color_depth:String(screen.colorDepth||"not_found"),browser_name:((c=s.browser)==null?void 0:c.name)||"not_found",browser_version:((a=s.browser)==null?void 0:a.version)||"not_found",os_name:((r=s.os)==null?void 0:r.name)||"not_found",os_version:((d=s.os)==null?void 0:d.version)||"not_found",app_language:this._getLanguageCode(navigator.language),cpu_cores:navigator.hardwareConcurrency!=null?String(navigator.hardwareConcurrency):"not_found",touch_points:navigator.maxTouchPoints!=null?String(navigator.maxTouchPoints):"0",network_type:(t==null?void 0:t.effectiveType)||"not_found",network_speed_mbps:(t==null?void 0:t.downlink)!=null?String(t.downlink):"not_found",network_latency_ms:(t==null?void 0:t.rtt)!=null?String(t.rtt):"not_found",data_saver_enabled:(t==null?void 0:t.saveData)!=null?String(t.saveData):"not_found",user_agent:navigator.userAgent,analytics_version:((h=this.cfg)==null?void 0:h.version)||"not_found"}}_normalizeUrl(e){if(!e)return e;try{let s=new URL(e),t=new URLSearchParams,i=["source","medium","campaign","term","content"];for(let[o,n]of s.searchParams.entries())o.toLowerCase().startsWith("utm")?t.set(o.toLowerCase(),n):i.includes(o.toLowerCase())?t.set("utm_"+o.toLowerCase(),n):t.set(o,n);return s.search=t.toString(),s.href}catch{return e}}_userProperties(){var r;let s=new b(navigator.userAgent).getResult(),t=this._normalizeUrl(location.href),o=new URL(t).searchParams,n=d=>o.get(d),c={device_type:((r=s.device)==null?void 0:r.type)||(matchMedia("(pointer:coarse)").matches?"mobile":"desktop"),density:String(window.devicePixelRatio||1),ram:navigator.deviceMemory!=null?String(navigator.deviceMemory):"not_found",device_language:this._getLanguageCode(navigator.language),landing_url:t},a=(d,h)=>{h!=null&&String(h).trim()!==""&&(c[d]=h)};return a("utm_source",n("utm_source")),a("utm_medium",n("utm_medium")),a("utm_campaign",n("utm_campaign")),a("utm_term",n("utm_term")),a("utm_content",n("utm_content")),a("utm_gclid",n("gclid")),a("utm_fbclid",n("fbclid")),a("utm_msclkid",n("msclkid")),c}_initIndexId(){this._indexKey=`twinalyze_index_${this.sessionId}`,this.indexId=parseInt(sessionStorage.getItem(this._indexKey)||"0",10),Number.isNaN(this.indexId)&&(this.indexId=0)}_nextIndexId(){return this.indexId+=1,this._indexKey&&sessionStorage.setItem(this._indexKey,String(this.indexId)),this.indexId}_getLastEventTime(){try{return parseInt(sessionStorage.getItem("twinalyze_last_event_time")||"0",10)}catch{return 0}}_setLastEventTime(){try{sessionStorage.setItem("twinalyze_last_event_time",String(Date.now()))}catch{}}_createNewSessionAfterIdle(){var s,t;let e="twinalyze_session_start_time";this.sessionId=`sess_${m()}`,this.sessionStartTime=new Date().toISOString(),((s=this.cfg)==null?void 0:s.persistSession)!==!1&&(sessionStorage.setItem(this.cfg.sessionKey,this.sessionId),sessionStorage.setItem(e,this.sessionStartTime)),this._indexKey=null,this.indexId=0,this._initIndexId(),(t=this.cfg)!=null&&t.debug&&console.log("[Twinalyze][SESSION] New session created after idle:",this.sessionId)}_checkSessionTimeoutBeforeEvent(){var t;let e=this._getLastEventTime(),s=((t=this.batchConfig)==null?void 0:t.sessionTimeout)||3e5;e&&Date.now()-e>s&&this._createNewSessionAfterIdle(),this._setLastEventTime()}_getOrCreateDeviceId(){let e="twinalyze_device_id",s=localStorage.getItem(e);if(s)return s;let t=m();return localStorage.setItem(e,t),t}_pageContext(){let e="twinalyze_last_page_location",s=document.title,t=this._normalizeUrl(location.href),i=location.hostname,o=location.pathname,c=sessionStorage.getItem(e)||document.referrer||null,a="direct";if(c)try{a=new URL(c).hostname===i?"internal":"external"}catch{a="external"}return{pageTitle:s,pageDomain:i,pageUrl:t,pagePath:o,previousPageUrl:c&&this._normalizeUrl(c),previousPageType:a}}_pageViewInfo(){let e="twinalyze_last_page_location",s=this._pageContext();return sessionStorage.setItem(e,s.pageUrl),{...s}}_saveUnsentEvents(){var e;try{let s=[...this.pendingEvents,...this.batchQueue];s.length>0?localStorage.setItem("twinalyze_unsent_events",JSON.stringify(s)):localStorage.removeItem("twinalyze_unsent_events")}catch(s){(e=this.cfg)!=null&&e.debug&&console.log("[Twinalyze] Error saving unsent events:",s)}}_loadAndSendUnsentEvents(){var e,s;try{let t=localStorage.getItem("twinalyze_unsent_events");if(t){let i=JSON.parse(t);localStorage.removeItem("twinalyze_unsent_events"),Array.isArray(i)&&i.length>0&&((e=this.cfg)!=null&&e.debug&&console.log(`[Twinalyze] Loaded ${i.length} unsent events from localStorage`),i.forEach(o=>{this.sessionReady?this._emitEventAdd(o.eventName,o.properties||{},o.eventType||"auto"):this.pendingEvents.push(o)}))}}catch(t){(s=this.cfg)!=null&&s.debug&&console.log("[Twinalyze] Error loading unsent events:",t)}}_setupVisibilityAndUnloadHandlers(){let e=()=>{var t,i;(t=this.cfg)!=null&&t.debug&&console.log("[Twinalyze] Visibility changed or page hiding. Flushing events...");let s=this.sessionReady;!this.sessionReady&&this.pendingEvents.length>0&&((i=this.cfg)!=null&&i.debug&&console.log("[Twinalyze] Force flushing pending events on unload..."),this.pendingEvents.splice(0,this.pendingEvents.length).forEach(n=>{this._indexKey==null&&this._initIndexId();let c=this._nextIndexId();this.batchQueue.push({uniqueSessionId:this.sessionId,date:new Date(n.ts||Date.now()).toISOString(),eventType:n.eventType||"auto",eventName:n.eventName,properties:{eventName:n.eventName,...n.properties},indexId:c,source:this.cfg.source})}),this.sessionReady=!0),this.batchQueue.length>0&&this._flushBatchQueue(),s||(this.sessionReady=s),this._saveUnsentEvents()};document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&e()}),window.addEventListener("pagehide",()=>{e()})}},x=new y,N=["init","track"],A=Object.fromEntries(N.map(l=>[l,x[l].bind(x)]));var O=A;
|
|
5
5
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.js"],"sourcesContent":["import CryptoJS from \"crypto-js\";\nimport html2canvas from \"html2canvas\";\n// import UAParser from \"ua-parser-js\";\nimport * as UAParserPkg from \"ua-parser-js\";\nconst UAParser = UAParserPkg.UAParser;\n\nfunction uuid() {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID) return crypto.randomUUID();\n return Math.random().toString(16).slice(2) + Date.now().toString(16);\n}\n\nconst cryptoRandomNonce = () => {\n // simple uuid-like nonce (ok for test)\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n};\n\nconst stableStringify = (obj) => {\n if (obj === null || typeof obj !== \"object\") return JSON.stringify(obj);\n if (Array.isArray(obj)) return \"[\" + obj.map(stableStringify).join(\",\") + \"]\";\n const keys = Object.keys(obj).filter(k => obj[k] !== undefined).sort();\n return \"{\" + keys.map(k => JSON.stringify(k) + \":\" + stableStringify(obj[k])).join(\",\") + \"}\";\n};\n\n\n\nclass TwinalyzeAnalyticsImpl {\n constructor() {\n this.cfg = null;\n\n this.deviceId = null;\n this.sessionId = null; // uniqueSessionId (client generated)\n this.sessionStartTime = null; // persistent session start timestamp\n\n this.sessionReady = false; // sessionCreate done\n this._booting = false; // handshake in progress\n this._disabled = false; // STOP state\n\n this.pendingEvents = []; // queued until sessionReady\n this.batchQueue = []; // queued for batch events upload\n this.scrollFired = false;\n\n this._initialized = false;\n this._flushTimer = null;\n\n this.indexId = 0;\n this._indexKey = null;\n\n this._lastShotAt = 0;\n this._shotInFlight = false;\n\n this.batchConfig = { eventsBatchSize: 10, flushIntervalMs: 5000, sessionTimeout: 300000 };\n }\n\n// Function for pacakage name -it will only get us subdomain and domain name\n _getAppPackageName() {\n try {\n // Preserve subdomain, remove path/query/protocol\n return window.location.hostname\n .replace(/^www\\./i, \"\")\n .toLowerCase();\n } catch {\n return \"unknown\";\n }\n}\n // -------------------------\n // PUBLIC API\n // -------------------------\n init(cfg) {\n if (typeof window === \"undefined\") return;\n if (this._initialized) return; // strict-mode safe\n this._initialized = true;\n\n const resolvedBaseUrl = cfg.apiBaseUrl || cfg.endpoint || cfg.socketUrl || \"https://api.twinalyze.com\";\n const normalizedBaseUrl = resolvedBaseUrl.endsWith(\"/\") ? resolvedBaseUrl.slice(0, -1) : resolvedBaseUrl;\n\n const DEFAULT_SCREEN_ACTIVITY = {\n enabled: false,\n apiUrl: cfg.screenActivity?.apiUrl ?? `${normalizedBaseUrl}/api/app/screenActivity/screenActivityDetails_post`,\n captureOn: [\"pageView\", \"elementClick\", \"formStart\", \"formSubmit\", \"scrollDepth\", \"searchResultsView\", \"customEvent\"],\n throttleMs: 2000,\n jpegQuality: 0.7,\n };\n\n // support: screenshots: true OR screenActivity: true OR screenActivity: { ... }\n const sa =\n typeof cfg.screenActivity === \"object\"\n ? { ...DEFAULT_SCREEN_ACTIVITY, ...cfg.screenActivity }\n : { ...DEFAULT_SCREEN_ACTIVITY };\n\n this.cfg = {\n apiKey: cfg.apiKey,\n secretKey: cfg.secretKey,\n version: cfg.version,\n apiBaseUrl: normalizedBaseUrl,\n\n debug: cfg.debug !== false, // default true in debug builds\n\n // encryption\n encrypt: cfg.encrypt !== false,\n\n // session id from client side\n persistSession: cfg.persistSession !== false, // default true (sessionStorage)\n sessionKey: \"twinalyze_session_id\",\n\n // eventAdd fields\n source: cfg.source || \"web\",\n indexId: cfg.indexId ?? null,\n\n // auto tracking options\n enhancedMeasurement: {\n pageViews: true,\n scrolls: true,\n outboundClicks: true,\n siteSearch: { params: [\"q\", \"s\", \"search\", \"query\"] },\n formInteractions: true,\n fileDownloads: { extensions: [\"pdf\", \"zip\", \"apk\", \"doc\", \"docx\", \"xls\", \"xlsx\", \"ppt\", \"pptx\"] },\n ...(cfg.enhancedMeasurement || {}),\n },\n screenActivity: sa,\n };\n\n if (!this.cfg.apiKey) {\n console.log(\"[Twinalyze] ❌ Missing apiKey\");\n return;\n }\n\n this.deviceId = this._getOrCreateDeviceId();\n\n const startKey = \"twinalyze_session_start_time\";\n if (this.cfg.persistSession) {\n this.sessionId = sessionStorage.getItem(this.cfg.sessionKey) || null;\n this.sessionStartTime = sessionStorage.getItem(startKey) || null;\n }\n if (!this.sessionId) {\n this.sessionId = `sess_${uuid()}`;\n this.sessionStartTime = new Date().toISOString();\n if (this.cfg.persistSession) {\n sessionStorage.setItem(this.cfg.sessionKey, this.sessionId);\n sessionStorage.setItem(startKey, this.sessionStartTime);\n }\n } else if (!this.sessionStartTime) {\n this.sessionStartTime = new Date().toISOString();\n if (this.cfg.persistSession) {\n sessionStorage.setItem(startKey, this.sessionStartTime);\n }\n }\n\n this._setupEnhancedMeasurement();\n this._loadAndSendUnsentEvents();\n this._setupVisibilityAndUnloadHandlers();\n this._startFlow();\n }\n\n // custom event (web dev only uses this)\n track(eventName, properties = {}) {\n if (this._disabled) return;\n if (this.cfg && this.cfg.manualEventStatus === false) return;\n\n if (this.cfg?.debug) console.log(\"[Twinalyze][CUSTOM]\", eventName, properties);\n\n if (!this.sessionReady) {\n this.pendingEvents.push({ eventName, properties: { ...this._pageContext(), ...properties }, ts: Date.now(), eventType: \"manual\" });\n return;\n }\n\n this._emitEventAdd(eventName, { ...this._pageContext(), ...properties }, \"manual\");\n }\n\n // -------------------------\n // -------------------------\n // REST API FLOW\n // -------------------------\n async _startFlow() {\n if (this._disabled) return;\n if (this._booting) return;\n this._booting = true;\n\n try {\n // 1. Init API\n const initRes = await this._fetchApi(\"/api/web/sdk/init\", {});\n if (this.cfg.debug) console.log(\"[Twinalyze][FLOW] initRes: HAR HAR MAHADEV\", initRes);\n\n if (!initRes || initRes.success === false) {\n this._disabled = true;\n this.sessionReady = false;\n this.cfg.screenActivity.enabled = false;\n if (this.cfg.debug) console.log(\"[Twinalyze][FLOW] 🛑 STOP (init failed)\");\n return;\n }\n\n const data = initRes.data || {};\n\n // this.cfg.screenActivity.enabled = !!data.debugScreenshotCapture; //stopped screenshot\n this.cfg.screenActivity.enabled = false; // stops screenstop even if backend sends true\n\n if (data.eventData) {\n this.cfg.autoEventStatus = data.eventData.autoEventStatus !== false;\n this.cfg.manualEventStatus = data.eventData.manualEventStatus !== false;\n } else {\n this.cfg.autoEventStatus = true;\n this.cfg.manualEventStatus = true;\n }\n\n if (data.eventsBatchConfig) {\n this.batchConfig = {\n eventsBatchSize: data.eventsBatchConfig.eventsBatchSize || 10,\n flushIntervalMs: data.eventsBatchConfig.flushIntervalMs || 5000,\n sessionTimeout: data.eventsBatchConfig.sessionTimeout || 300000\n };\n }\n\n this._initIndexId();\n\n // 2. Identify API\n const identifyPayload = {\n properties: this._userProperties(),\n deviceProperties: this._deviceProperties()\n };\n\n const identifyRes = await this._fetchApi(\"/api/web/sdk/identify\", identifyPayload);\n if (this.cfg.debug) console.log(\"[Twinalyze][FLOW] identifyRes:\", identifyRes);\n\n if (!identifyRes || identifyRes.success === false) {\n this._disabled = true;\n this.sessionReady = false;\n if (this.cfg.debug) console.log(\"[Twinalyze][FLOW] 🛑 STOP (identify failed)\");\n return;\n }\n\n // Mark ready\n this.sessionReady = true;\n\n // Flush queued auto/custom events (move from pendingEvents to batchQueue)\n if (this.pendingEvents.length > 0) {\n const items = this.pendingEvents.splice(0, this.pendingEvents.length);\n items.forEach((e) => this._emitEventAdd(e.eventName, e.properties || {}, e.eventType || \"auto\"));\n }\n\n } finally {\n this._booting = false;\n }\n }\n\n async _fetchApi(endpoint, payloadObj) {\n const url = `${this.cfg.apiBaseUrl}${endpoint}`;\n\n const nonce = cryptoRandomNonce();\n const timestamp = new Date().toISOString();\n const bodyStr = payloadObj ? JSON.stringify(payloadObj) : \"{}\";\n\n const canonicalBody = stableStringify(JSON.parse(bodyStr));\n const bodyHash = CryptoJS.SHA256(canonicalBody).toString(CryptoJS.enc.Hex);\n\n const method = \"POST\";\n const baseString = [method, endpoint, timestamp, nonce, this.deviceId, bodyHash].join(\"\\n\");\n\n // Generate HMAC-SHA256 signature using secret key from config\n const SECRET_KEY = this.cfg.secretKey || \"\";\n const signature = CryptoJS.HmacSHA256(baseString, SECRET_KEY).toString(CryptoJS.enc.Hex);\n\n if (this.cfg.debug) {\n console.log(\"[Twinalyze][API] Request signing trace:\", {\n endpoint,\n nonce,\n timestamp,\n canonicalBody,\n bodyHash,\n baseString,\n signature\n });\n }\n\n const headers = {\n \"Content-Type\": \"application/json\",\n \"x-deviceId\": this.deviceId,\n \"x-api-key\": this.cfg.apiKey,\n \"x-analytics-sdk-version\": this.cfg.version || \"1.0.0\",\n \"x-ad-analytics-sdk-version\": \"\",\n // \"x-app-package-name\": window.location.href, \n \"x-app-package-name\": this._getAppPackageName(), \n \"x-platform\": \"javascript\",\n \"x-app-version-name\": \"1.0.0\",\n \"x-app-version-mode\": this.cfg.debug ? \"debug\" : \"release\",\n \"x-sdk-version\": \"latest\",\n \"x-timestamp\": timestamp,\n \"x-nonce\": nonce,\n \"x-signature\": signature\n };\n\n try {\n const res = await fetch(url, {\n method: \"POST\",\n headers,\n body: bodyStr,\n keepalive: true\n });\n return await res.json();\n } catch (err) {\n if (this.cfg.debug) console.log(`[Twinalyze][API] Error calling ${endpoint}:`, err);\n return null;\n }\n }\n\n async _waitForRenderStable() {\n // 2 frames\n await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));\n\n // fonts\n try { if (document.fonts?.ready) await document.fonts.ready; } catch { }\n\n // images\n const imgs = Array.from(document.images || []).filter(img => !img.complete);\n await Promise.allSettled(\n imgs.map(img => new Promise(res => {\n img.addEventListener(\"load\", res, { once: true });\n img.addEventListener(\"error\", res, { once: true });\n }))\n );\n }\n\n async _captureViewportJpegBlob() {\n await this._waitForRenderStable();\n\n const isViewport = (this.cfg.screenActivity?.capture || \"viewport\") === \"viewport\";\n const scale = Math.min(2, window.devicePixelRatio || 1);\n\n const canvas = await html2canvas(document.documentElement, {\n useCORS: true,\n allowTaint: false,\n backgroundColor: \"#ffffff\",\n logging: false,\n\n ...(isViewport\n ? { x: window.scrollX, y: window.scrollY, width: window.innerWidth, height: window.innerHeight }\n : { x: 0, y: 0, width: document.documentElement.scrollWidth, height: document.documentElement.scrollHeight }\n ),\n\n scale,\n onclone: (doc) => {\n const style = doc.createElement(\"style\");\n style.textContent = `\n * { animation: none !important; transition: none !important; caret-color: transparent !important; }\n `;\n doc.head.appendChild(style);\n },\n });\n\n // downscale big screenshots (optional but helpful)\n const maxW = this.cfg.screenActivity?.maxWidth ?? 1280;\n if (canvas.width > maxW) {\n const ratio = maxW / canvas.width;\n const c2 = document.createElement(\"canvas\");\n c2.width = Math.round(canvas.width * ratio);\n c2.height = Math.round(canvas.height * ratio);\n c2.getContext(\"2d\").drawImage(canvas, 0, 0, c2.width, c2.height);\n return await new Promise((resolve) =>\n c2.toBlob(resolve, \"image/jpeg\", this.cfg.screenActivity?.jpegQuality ?? 0.7)\n );\n }\n\n return await new Promise((resolve) =>\n canvas.toBlob(resolve, \"image/jpeg\", this.cfg.screenActivity?.jpegQuality ?? 0.7)\n );\n }\n\n async _uploadScreenActivity({ eventName, properties }) {\n if (!this.cfg.screenActivity?.enabled) return;\n if (!this.sessionReady) return;\n\n const now = Date.now();\n const throttleMs = this.cfg.screenActivity?.throttleMs ?? 2000;\n if (now - this._lastShotAt < throttleMs) return;\n if (this._shotInFlight) return;\n\n this._shotInFlight = true;\n this._lastShotAt = now;\n\n try {\n const blob = await this._captureViewportJpegBlob();\n if (!blob) return;\n\n const fd = new FormData();\n fd.append(\"apiKey\", this.cfg.apiKey);\n fd.append(\"screenName\", document.title || \"unknown\");\n fd.append(\"identifier\", JSON.stringify(properties || {})); // ✅ you asked: identifier = properties object\n fd.append(\"appInfo\", JSON.stringify({\n appVersion: this.cfg.version || \"1.0.0\",\n platform: \"javascript\"\n }));\n fd.append(\"description\", eventName || \"event\");\n fd.append(\"image\", blob, `shot_${Date.now()}.jpg`);\n\n const headers = {\n \"x-deviceId\": this.deviceId,\n \"x-api-key\": this.cfg.apiKey,\n \"x-analytics-sdk-version\": this.cfg.version || \"1.0.0\",\n \"x-ad-analytics-sdk-version\": \"\",\n // \"x-app-package-name\": window.location.href,\n \"x-app-package-name\": this._getAppPackageName(), \n \"x-platform\": \"javascript\",\n \"x-app-version-name\": \"1.0.0\",\n \"x-app-version-mode\": this.cfg.debug ? \"debug\" : \"release\",\n \"x-sdk-version\": \"latest\",\n };\n\n await fetch(this.cfg.screenActivity.apiUrl, {\n method: \"POST\",\n headers,\n body: fd,\n // keepalive: true, (commented because only 1 screenshot was coming and other were going in pending)\n });\n } catch (e) {\n if (this.cfg.debug) console.log(\"[Twinalyze][SCREENSHOT] error:\", e?.message || e);\n } finally {\n this._shotInFlight = false;\n }\n }\n\n // -------------------------\n // -------------------------\n // EMITS (REST API BATCHING)\n // -------------------------\n _emitEventAdd(eventName, properties = {}, eventType = \"auto\") {\n if (!this.sessionReady) return;\n\n this._checkSessionTimeoutBeforeEvent(); //function for session timeout check before emitting event and creating new session if needed\n\n if (this._indexKey == null) this._initIndexId();\n const indexId = this._nextIndexId();\n\n const eventObj = {\n uniqueSessionId: this.sessionId,\n date: new Date().toISOString(),\n eventType: eventType,\n eventName: eventName,\n properties: { eventName: eventName , ...properties}, //changed sequence so that eventName can be been first\n indexId: indexId,\n source: this.cfg.source\n };\n\n // If this is the first event in a new batch, schedule a flush timer\n if (this.batchQueue.length === 0) {\n if (this._flushTimer) clearTimeout(this._flushTimer);\n this._flushTimer = setTimeout(() => this._flushBatchQueue(), this.batchConfig.flushIntervalMs);\n }\n\n this.batchQueue.push(eventObj);\n\n // this._uploadScreenActivity({ eventName, properties }); // commented to stop screenshot\n\n if (this.cfg.debug) console.log(\"[Twinalyze][EVENT_QUEUED]\", eventName, properties, indexId);\n\n // Flush immediately if we hit the batch size limit\n if (this.batchQueue.length >= this.batchConfig.eventsBatchSize) {\n this._flushBatchQueue();\n }\n }\n\n async _flushBatchQueue() {\n // Clear any active timeout timer to prevent duplicate flushes\n if (this._flushTimer) {\n clearTimeout(this._flushTimer);\n this._flushTimer = null;\n }\n\n if (!this.sessionReady) return;\n if (!this.batchQueue.length) return;\n\n // slice to handle new events coming in while flushing\n const batch = this.batchQueue.splice(0, this.batchQueue.length);\n\n if (this.cfg.debug) console.log(`[Twinalyze][FLUSHING] ${batch.length} events...`);\n\n const payload = {\n eventsBatch: [\n {\n events: batch.map(e => ({\n appInfo: {\n appVersion: this.cfg.version || \"1.0.0\",\n platform: \"javascript\"\n },\n date: e.date,\n eventName: e.eventName,\n eventType: e.eventType,\n indexId: e.indexId,\n properties: e.properties\n })),\n startDate: this.sessionStartTime || batch[0].date,\n uniqueSessionId: this.sessionId\n }\n ]\n };\n const res = await this._fetchApi(\"/api/web/sdk/eventsBatch\", payload);\n\n if (this.cfg.debug) console.log(\"[Twinalyze][FLUSH_RES]\", res);\n\n if (!res || res.success === false) {\n if (this.cfg.debug) console.log(\"[Twinalyze][FLUSH_FAIL] events dropped\");\n // Could re-queue here if desired: this.batchQueue.unshift(...batch);\n }\n }\n\n // -------------------------\n // AUTO EVENTS (GA-style)\n // -------------------------\n _trackAuto(name, props = {}) {\n if (this._disabled) return;\n if (this.cfg && this.cfg.autoEventStatus === false) return;\n if (this.cfg?.debug) console.log(\"[Twinalyze][AUTO]\", name, props);\n\n if (!this.sessionReady) {\n this.pendingEvents.push({ eventName: name, properties: props, ts: Date.now(), eventType: \"auto\" });\n return;\n }\n\n this._emitEventAdd(name, props, \"auto\");\n }\n\n _setupEnhancedMeasurement() {\n const em = this.cfg.enhancedMeasurement || {};\n\n // pageView (normal + SPA)\n if (em.pageViews) {\n const pv = this._pageViewInfo();\n this._trackAuto(\"pageView\", pv);\n\n const fire = () => {\n this.scrollFired = false;\n const pv2 = this._pageViewInfo();\n this._trackAuto(\"pageView\", pv2);\n this._fireSiteSearch();\n };\n\n const _push = history.pushState;\n const _replace = history.replaceState;\n\n history.pushState = (...args) => { _push.apply(history, args); fire(); };\n history.replaceState = (...args) => { _replace.apply(history, args); fire(); };\n window.addEventListener(\"popstate\", fire);\n }\n\n // if user scrolls above 90% than event will come\n if (em.scrolls) {\n window.addEventListener(\"scroll\", () => {\n if (this.scrollFired) return;\n\n const doc = document.documentElement;\n const scrollTop = window.scrollY || doc.scrollTop;\n const scrollHeight = doc.scrollHeight - doc.clientHeight;\n if (scrollHeight <= 0) return;\n\n const percent = Math.round((scrollTop / scrollHeight) * 100);\n if (percent >= 90) {\n this.scrollFired = true;\n this._trackAuto(\"scrollDepth\", { scrollPercent: percent, ...this._pageContext() });\n }\n }, { passive: true });\n }\n\n // // outbound clicks\n // if (em.outboundClicks) {\n // document.addEventListener(\"click\", (e) => {\n // const a = e.target && e.target.closest ? e.target.closest(\"a\") : null;\n // if (!a || !a.href) return;\n // if (/^(javascript:|mailto:|tel:)/i.test(a.href)) return;\n\n // let url;\n // try { url = new URL(a.href); } catch { return; }\n // if (url.hostname !== location.hostname) {\n // console.log(\"click\", { link_url: url.href, link_domain: url.hostname, outbound: true });\n // this._trackAuto(\"click\", { link_url: url.href, link_domain: url.hostname, outbound: true });\n // }\n // }, true);\n // }\n\n // clicks (links + buttons + dropdown items + generic interactive)\n if (em.outboundClicks) {\n document.addEventListener(\"click\", (e) => {\n const hit = this._getClickTarget(e);\n if (!hit) return;\n\n const el = hit.el;\n\n const tag = (el.tagName || \"\").toLowerCase();\n const text = this._getTextFromElement(el);\n\n const props = {\n clickType: hit.kind, // link | button | dropdown_item | interactive\n elementTag: tag,\n elementText: text || \"not_found\", // ✅ innerText/aria-label/title\n elementId: el.id || null,\n elementName: el.getAttribute?.(\"name\") || null,\n elementRole: el.getAttribute?.(\"role\") || null,\n /* elementClass:\n (el.className && typeof el.className === \"string\") ? el.className.slice(0, 120) : null,\n elementSelector: this._cssPath(el), */ \n ...((this._pageContext && this._pageContext()) || {}), // if you added _pageContext(). \n };\n\n // If link, enrich with URL + outbound\n if (hit.kind === \"link\") {\n const href = el.getAttribute(\"href\") || \"\";\n if (/^(javascript:|mailto:|tel:)/i.test(href)) return;\n\n let url;\n try { url = new URL(el.href); } catch { return; }\n\n props.linkUrl = url.href;\n props.linkDomain = url.hostname;\n props.linkPath = url.pathname;\n props.isExternalLink = url.hostname !== location.hostname; // changed name from isOutbound to isExternalLink\n }\n\n // Button details\n if (hit.kind === \"button\") {\n props.buttonType = el.getAttribute?.(\"type\") || null;\n props.disabled = !!el.disabled;\n }\n\n this._trackAuto(\"elementClick\", props);\n }, true);\n\n }\n\n // site search\n if (em.siteSearch) this._fireSiteSearch();\n\n // form interactions\n if (em.formInteractions) {\n const started = new WeakSet();\n\n document.addEventListener(\"focusin\", (e) => {\n const form = e.target && e.target.closest ? e.target.closest(\"form\") : null;\n if (!form || started.has(form)) return;\n started.add(form);\n\n this._trackAuto(\"formStart\", {\n formId: form.id || undefined,\n formName: form.getAttribute(\"name\") || undefined,\n formActionUrl: form.action || undefined,\n ...this._pageContext(),\n });\n });\n\n document.addEventListener(\"submit\", (e) => {\n const form = e.target;\n if (!form) return;\n\n this._trackAuto(\"formSubmit\", {\n formStatus: \"success\", // changed sequence\n formActionUrl: form.action || undefined,\n formDestinationUrl: location.href,\n formId: form.id || undefined,\n formName: form.getAttribute(\"name\") || undefined,\n ...this._pageContext(),\n });\n }, true);\n }\n\n // file downloads\n if (em.fileDownloads && em.fileDownloads.extensions) {\n const exts = em.fileDownloads.extensions.map((x) => String(x).toLowerCase());\n\n document.addEventListener(\"click\", (e) => {\n const a = e.target && e.target.closest ? e.target.closest(\"a\") : null;\n if (!a || !a.href) return;\n\n const clean = a.href.split(\"?\")[0].toLowerCase();\n const ext = clean.split(\".\").pop() || \"\";\n if (!exts.includes(ext)) return;\n\n this._trackAuto(\"fileDownload\", {\n linkUrl: a.href,\n fileExtension: ext,\n fileName: clean.split(\"/\").pop(),\n ...this._pageContext(),\n });\n }, true);\n }\n }\n\n _fireSiteSearch() {\n const em = this.cfg.enhancedMeasurement || {};\n if (!em.siteSearch) return;\n\n const params = em.siteSearch.params || [\"q\", \"s\", \"search\", \"query\"];\n const usp = new URLSearchParams(location.search);\n const key = params.find((k) => usp.get(k));\n const term = key ? usp.get(key) : null;\n\n if (term && term.trim()) {\n this._trackAuto(\"searchResultsView\", {\n searchKey: key, //changed sequence\n searchTerm: term.trim(),\n searchUrl: location.href,\n ...this._pageContext()\n });\n }\n }\n\n _getTextFromElement(el) {\n if (!el) return null;\n\n // common places (MUI/AntD often store label in aria-label)\n const aria = el.getAttribute?.(\"aria-label\");\n if (aria && aria.trim()) return aria.trim();\n\n const title = el.getAttribute?.(\"title\");\n if (title && title.trim()) return title.trim();\n\n // visible text\n const txt = (el.innerText || el.textContent || \"\").trim();\n if (!txt) return null;\n\n // limit length (avoid huge HTML)\n return txt.length > 80 ? txt.slice(0, 80) : txt;\n }\n\n _cssPath(el) {\n if (!el || !el.tagName) return null;\n const parts = [];\n let node = el;\n let depth = 0;\n while (node && node.nodeType === 1 && depth < 5) {\n let part = node.tagName.toLowerCase();\n if (node.id) {\n part += `#${node.id}`;\n parts.unshift(part);\n break;\n }\n const cls = (node.className && typeof node.className === \"string\")\n ? node.className.trim().split(/\\s+/).slice(0, 2).join(\".\")\n : \"\";\n if (cls) part += `.${cls}`;\n parts.unshift(part);\n node = node.parentElement;\n depth++;\n }\n return parts.join(\" > \");\n }\n\n _getClickTarget(e) {\n // Shadow DOM safe path\n const path = typeof e.composedPath === \"function\" ? e.composedPath() : null;\n const start = (path && path.length ? path[0] : e.target);\n\n const closest = (el, sel) => (el && el.closest ? el.closest(sel) : null);\n\n // 1) Links\n const a = closest(start, 'a[href]');\n if (a) return { el: a, kind: \"link\" };\n\n // 2) Buttons + button-like\n const btn = closest(\n start,\n 'button, input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"], [role=\"button\"]'\n );\n if (btn) return { el: btn, kind: \"button\" };\n\n // 3) Menu / dropdown item (generic roles)\n const menuItem = closest(\n start,\n '[role=\"menuitem\"], [role=\"menuitemradio\"], [role=\"menuitemcheckbox\"], [role=\"option\"]'\n );\n if (menuItem) return { el: menuItem, kind: \"dropdown_item\" };\n\n // 4) Generic \"interactive\" fallback:\n // - role link\n // - tabindex (focusable)\n // - aria-haspopup (opens menu)\n // - contenteditable\n const interactive = closest(\n start,\n '[role=\"link\"], [tabindex]:not([tabindex=\"-1\"]), [aria-haspopup], [contenteditable=\"true\"]'\n );\n if (interactive) return { el: interactive, kind: \"interactive\" };\n\n return null;\n }\n\n\n // -------------------------\n // PROPERTIES (AUTO)\n // -------------------------\n // _deviceProperties() {\n // return {\n // ua: navigator.userAgent,\n // lang: navigator.language,\n // tz: Intl.DateTimeFormat().resolvedOptions().timeZone,\n // screen: { w: screen.width, h: screen.height },\n // viewport: { w: window.innerWidth, h: window.innerHeight },\n // dpr: window.devicePixelRatio || 1,\n // };\n // }\n\n _deviceProperties() {\n const parser = new UAParser(navigator.userAgent);\n const ua = parser.getResult();\n\n const conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection;\n\n const screenMode =\n window.matchMedia?.(\"(prefers-color-scheme: dark)\")?.matches ? \"dark\" : \"light\";\n\n return {\n // device/browser\n device_screen_mode: screenMode,\n is_data_on: navigator.onLine ? \"true\" : \"false\",\n screen_width: String(screen.width),\n screen_height: String(screen.height),\n viewport_width: String(window.innerWidth),\n viewport_height: String(window.innerHeight),\n color_depth: String(screen.colorDepth || \"not_found\"),\n browser_name: ua.browser?.name || \"not_found\",\n browser_version: ua.browser?.version || \"not_found\",\n os_name: ua.os?.name || \"not_found\",\n os_version: ua.os?.version || \"not_found\",\n app_language: navigator.language || \"not_found\",\n cpu_cores: navigator.hardwareConcurrency != null ? String(navigator.hardwareConcurrency) : \"not_found\",\n touch_points: navigator.maxTouchPoints != null ? String(navigator.maxTouchPoints) : \"0\",\n network_type: conn?.effectiveType || \"not_found\",\n network_speed_mbps: conn?.downlink != null ? String(conn.downlink) : \"not_found\",\n network_latency_ms: conn?.rtt != null ? String(conn.rtt) : \"not_found\",\n data_saver_enabled: conn?.saveData != null ? String(conn.saveData) : \"not_found\",\n user_agent: navigator.userAgent,\n analytics_version: this.cfg?.version || \"not_found\",\n };\n }\n\n _normalizeUrl(urlStr) {\n if (!urlStr) return urlStr;\n try {\n const url = new URL(urlStr);\n const newParams = new URLSearchParams();\n const campaignKeys = [\"source\", \"medium\", \"campaign\", \"term\", \"content\"];\n for (const [key, val] of url.searchParams.entries()) {\n if (key.toLowerCase().startsWith(\"utm\")) {\n newParams.set(key.toLowerCase(), val);\n } else if (campaignKeys.includes(key.toLowerCase())) {\n newParams.set(\"utm_\" + key.toLowerCase(), val);\n } else {\n newParams.set(key, val);\n }\n }\n url.search = newParams.toString();\n return url.href;\n } catch (e) {\n return urlStr;\n }\n }\n\n _userProperties() {\n const parser = new UAParser(navigator.userAgent);\n const ua = parser.getResult();\n\n const normalizedUrlStr = this._normalizeUrl(location.href);\n const url = new URL(normalizedUrlStr);\n const usp = url.searchParams;\n\n const get = (k) => usp.get(k);\n const nf = \"not_found\";\n\n return {\n // From GSheet User Properties (Left Side) - Web Platform\n device_type: ua.device?.type || (matchMedia(\"(pointer:coarse)\").matches ? \"mobile\" : \"desktop\"),\n density: String(window.devicePixelRatio || 1),\n timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || \"not_found\",\n ram: navigator.deviceMemory != null ? String(navigator.deviceMemory) : \"not_found\",\n device_language: navigator.language || \"not_found\",\n\n // acquisition\n landing_url: normalizedUrlStr,\n utm_source: get(\"utm_source\") || nf,\n utm_medium: get(\"utm_medium\") || nf,\n utm_campaign: get(\"utm_campaign\") || nf,\n utm_term: get(\"utm_term\") || nf,\n utm_content: get(\"utm_content\") || nf,\n\n // ads click ids\n utm_gclid: get(\"gclid\") || nf,\n utm_fbclid: get(\"fbclid\") || nf,\n utm_msclkid: get(\"msclkid\") || nf,\n\n // identity (optional, only if you have it)\n // user_id: this.userId || nf,\n // email: this.email || nf,\n // plan: \"free\" / \"pro\" etc.\n };\n }\n\n\n // -------------------------\n // IDs\n // -------------------------\n _initIndexId() {\n this._indexKey = `twinalyze_index_${this.sessionId}`;\n this.indexId = parseInt(sessionStorage.getItem(this._indexKey) || \"0\", 10);\n if (Number.isNaN(this.indexId)) this.indexId = 0;\n }\n\n _nextIndexId() {\n this.indexId += 1;\n if (this._indexKey) {\n sessionStorage.setItem(this._indexKey, String(this.indexId));\n }\n return this.indexId;\n }\n\n // added below section for session timeout and new session creation after idle time\n\n_getLastEventTime() {\n try {\n return parseInt(sessionStorage.getItem(\"twinalyze_last_event_time\") || \"0\", 10);\n } catch {\n return 0;\n }\n}\n\n_setLastEventTime() {\n try {\n sessionStorage.setItem(\"twinalyze_last_event_time\", String(Date.now()));\n } catch {\n // ignore\n }\n}\n\n_createNewSessionAfterIdle() {\n const startKey = \"twinalyze_session_start_time\";\n\n this.sessionId = `sess_${uuid()}`;\n this.sessionStartTime = new Date().toISOString();\n\n if (this.cfg?.persistSession !== false) {\n sessionStorage.setItem(this.cfg.sessionKey, this.sessionId);\n sessionStorage.setItem(startKey, this.sessionStartTime);\n }\n\n // reset event index for new session\n this._indexKey = null;\n this.indexId = 0;\n this._initIndexId();\n\n if (this.cfg?.debug) {\n console.log(\"[Twinalyze][SESSION] New session created after idle:\", this.sessionId);\n }\n}\n\n_checkSessionTimeoutBeforeEvent() {\n const lastEventTime = this._getLastEventTime();\n const timeout = this.batchConfig?.sessionTimeout || 300000;\n\n if (lastEventTime && Date.now() - lastEventTime > timeout) {\n this._createNewSessionAfterIdle();\n }\n\n this._setLastEventTime();\n}\n// added above section for sessiontime \n _getOrCreateDeviceId() {\n const key = \"twinalyze_device_id\";\n const old = localStorage.getItem(key);\n if (old) return old;\n\n const id = uuid();\n // const id = `dev_${uuid()}`;\n localStorage.setItem(key, id);\n return id;\n }\n\n _pageContext() {\n const KEY_LAST = \"twinalyze_last_page_location\";\n\n const pageTitle = document.title;\n const pageLocation = this._normalizeUrl(location.href);\n const pageDomain = location.hostname;\n const pagePath = location.pathname;\n \n\n const storedPrev = sessionStorage.getItem(KEY_LAST);\n const prevLocation = storedPrev || (document.referrer || null);\n\n let prevType = \"direct\";\n if (prevLocation) {\n try {\n const prevHost = new URL(prevLocation).hostname;\n prevType = prevHost === pageDomain ? \"internal\" : \"external\";\n } catch {\n prevType = \"external\";\n }\n }\n\n return {\n pageTitle: pageTitle, //path changed from 4th to first position\n pageDomain: pageDomain,\n pageUrl: pageLocation,\n pagePath: pagePath,\n \n previousPageUrl: prevLocation ? this._normalizeUrl(prevLocation) : prevLocation, // PreviousName = referrerUrl\n previousPageType: prevType, // PreviousName = referrerType\n };\n }\n\n _pageViewInfo() {\n const KEY_LAST = \"twinalyze_last_page_location\";\n // const KEY_COUNT = \"twinalyze_page_counter\";\n // const KEY_LAST_COUNTED = \"twinalyze_last_counted_path\"; // ✅ new\n\n const ctx = this._pageContext(); // uses current location + previous page\n\n /*\n const currentKey = ctx.pagePath; // ✅ count only on path change\n // If you want count only on full URL change, use: const currentKey = ctx.pageUrl;\n\n const lastCounted = sessionStorage.getItem(KEY_LAST_COUNTED);\n\n // If same page refreshed -> DO NOT increment\n let pageCounter = parseInt(sessionStorage.getItem(KEY_COUNT) || \"0\", 10);\n if (Number.isNaN(pageCounter)) pageCounter = 0;\n\n if (lastCounted !== currentKey) {\n pageCounter += 1;\n sessionStorage.setItem(KEY_COUNT, String(pageCounter));\n sessionStorage.setItem(KEY_LAST_COUNTED, currentKey);\n } else {\n // keep same counter\n sessionStorage.setItem(KEY_COUNT, String(pageCounter));\n }\n*/\n // update last page location (for prev page logic)\n sessionStorage.setItem(KEY_LAST, ctx.pageUrl);\n\n return {\n // pageCount: pageCounter,\n ...ctx,\n };\n }\n\n _saveUnsentEvents() {\n try {\n const unsent = [\n ...this.pendingEvents,\n ...this.batchQueue\n ];\n if (unsent.length > 0) {\n localStorage.setItem(\"twinalyze_unsent_events\", JSON.stringify(unsent));\n } else {\n localStorage.removeItem(\"twinalyze_unsent_events\");\n }\n } catch (e) {\n if (this.cfg?.debug) console.log(\"[Twinalyze] Error saving unsent events:\", e);\n }\n }\n\n _loadAndSendUnsentEvents() {\n try {\n const stored = localStorage.getItem(\"twinalyze_unsent_events\");\n if (stored) {\n const events = JSON.parse(stored);\n localStorage.removeItem(\"twinalyze_unsent_events\");\n if (Array.isArray(events) && events.length > 0) {\n if (this.cfg?.debug) console.log(`[Twinalyze] Loaded ${events.length} unsent events from localStorage`);\n events.forEach(e => {\n if (this.sessionReady) {\n this._emitEventAdd(e.eventName, e.properties || {}, e.eventType || \"auto\");\n } else {\n this.pendingEvents.push(e);\n }\n });\n }\n }\n } catch (e) {\n if (this.cfg?.debug) console.log(\"[Twinalyze] Error loading unsent events:\", e);\n }\n }\n\n _setupVisibilityAndUnloadHandlers() {\n const flushAndSave = () => {\n if (this.cfg?.debug) console.log(\"[Twinalyze] Visibility changed or page hiding. Flushing events...\");\n\n const wasReady = this.sessionReady;\n if (!this.sessionReady && this.pendingEvents.length > 0) {\n if (this.cfg?.debug) console.log(\"[Twinalyze] Force flushing pending events on unload...\");\n const items = this.pendingEvents.splice(0, this.pendingEvents.length);\n items.forEach((e) => {\n if (this._indexKey == null) this._initIndexId();\n const indexId = this._nextIndexId();\n this.batchQueue.push({\n uniqueSessionId: this.sessionId,\n date: new Date(e.ts || Date.now()).toISOString(),\n eventType: e.eventType || \"auto\",\n eventName: e.eventName,\n properties: { eventName: e.eventName , ...e.properties}, // changed sequence\n indexId: indexId,\n source: this.cfg.source\n });\n });\n this.sessionReady = true;\n }\n\n if (this.batchQueue.length > 0) {\n this._flushBatchQueue();\n }\n\n if (!wasReady) {\n this.sessionReady = wasReady;\n }\n\n this._saveUnsentEvents();\n };\n\n document.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n flushAndSave();\n }\n });\n\n window.addEventListener(\"pagehide\", () => {\n flushAndSave();\n });\n }\n}\n\nconst _instance = new TwinalyzeAnalyticsImpl();\n\nconst PUBLIC_METHODS = [\n \"init\",\n \"track\"\n];\n\nconst TwinalyzeAnalytics = Object.fromEntries(\n PUBLIC_METHODS.map((m) => [m, _instance[m].bind(_instance)])\n);\nexport { TwinalyzeAnalytics };\nexport default TwinalyzeAnalytics;"],"mappings":"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","names":["src_exports","__export","TwinalyzeAnalytics","src_default","__toCommonJS","import_crypto_js","import_html2canvas","UAParserPkg","UAParser","uuid","cryptoRandomNonce","c","r","stableStringify","obj","k","TwinalyzeAnalyticsImpl","cfg","_a","_b","_c","resolvedBaseUrl","normalizedBaseUrl","DEFAULT_SCREEN_ACTIVITY","sa","startKey","eventName","properties","initRes","data","identifyPayload","identifyRes","e","endpoint","payloadObj","url","nonce","timestamp","bodyStr","canonicalBody","bodyHash","CryptoJS","baseString","SECRET_KEY","signature","headers","err","imgs","img","res","isViewport","scale","canvas","html2canvas","doc","style","maxW","ratio","c2","resolve","now","throttleMs","blob","fd","eventType","indexId","eventObj","batch","payload","name","props","em","pv","fire","pv2","_push","_replace","args","scrollTop","scrollHeight","percent","hit","el","tag","text","href","started","form","exts","x","a","clean","ext","params","usp","key","term","aria","title","txt","parts","node","depth","part","cls","path","start","closest","sel","btn","menuItem","interactive","_d","_e","_f","_g","ua","conn","urlStr","newParams","campaignKeys","val","normalizedUrlStr","get","nf","lastEventTime","timeout","old","id","KEY_LAST","pageTitle","pageLocation","pageDomain","pagePath","prevLocation","prevType","ctx","unsent","stored","events","flushAndSave","wasReady","_instance","PUBLIC_METHODS","m","src_default","TwinalyzeAnalytics"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.js"],"sourcesContent":["import CryptoJS from \"crypto-js\";\nimport html2canvas from \"html2canvas\";\n// import UAParser from \"ua-parser-js\";\nimport * as UAParserPkg from \"ua-parser-js\";\nconst UAParser = UAParserPkg.UAParser;\n\nfunction uuid() {\n if (typeof crypto !== \"undefined\" && crypto.randomUUID)\n return crypto.randomUUID();\n return Math.random().toString(16).slice(2) + Date.now().toString(16);\n}\n\nconst cryptoRandomNonce = () => {\n // simple uuid-like nonce (ok for test)\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n};\n\nconst stableStringify = (obj) => {\n if (obj === null || typeof obj !== \"object\") return JSON.stringify(obj);\n if (Array.isArray(obj)) return \"[\" + obj.map(stableStringify).join(\",\") + \"]\";\n const keys = Object.keys(obj)\n .filter((k) => obj[k] !== undefined)\n .sort();\n return (\n \"{\" +\n keys\n .map((k) => JSON.stringify(k) + \":\" + stableStringify(obj[k]))\n .join(\",\") +\n \"}\"\n );\n};\n\nclass TwinalyzeAnalyticsImpl {\n constructor() {\n this.cfg = null;\n\n this.deviceId = null;\n this.sessionId = null; // uniqueSessionId (client generated)\n this.sessionStartTime = null; // persistent session start timestamp\n\n this.sessionReady = false; // sessionCreate done\n this._booting = false; // handshake in progress\n this._disabled = false; // STOP state\n\n this.pendingEvents = []; // queued until sessionReady\n this.batchQueue = []; // queued for batch events upload\n this.scrollFired = false;\n\n this._initialized = false;\n this._flushTimer = null;\n\n this.indexId = 0;\n this._indexKey = null;\n\n this._lastShotAt = 0;\n this._shotInFlight = false;\n\n this.batchConfig = {\n eventsBatchSize: 10,\n flushIntervalMs: 5000,\n sessionTimeout: 300000,\n };\n }\n\n // Function for pacakage name -it will only get us subdomain and domain name\n _getAppPackageName() {\n try {\n // Preserve subdomain, remove path/query/protocol\n return window.location.hostname.replace(/^www\\./i, \"\").toLowerCase();\n } catch {\n return \"unknown\";\n }\n }\n\n // Function for getting app_language and device_language before \"-\" from fetched version\n _getLanguageCode(lang) {\n if (!lang) return \"not_found\";\n return lang.split(\"-\")[0].toLowerCase();\n }\n\n //check if it is bot\n _isLikelyBot() {\n try {\n const userAgent = navigator.userAgent || \"\";\n\n \n const botRegex =\n /bot|crawler|spider|crawling|googlebot|bingbot|yandexbot|duckduckbot|baiduspider|facebookexternalhit|twitterbot|linkedinbot|whatsapp|telegrambot|pinterest|ahrefs|semrush|mj12bot|dotbot|petalbot|bytespider|gptbot|ccbot|claudebot|perplexitybot|headlesschrome|puppeteer|playwright|phantomjs|lighthouse|pagespeed|gtmetrix|pingdom/i;\n\n // If userAgent missing, treat as suspicious\n if (!userAgent) return true;\n\n // Example: Googlebot/2.1 will match here\n if (botRegex.test(userAgent)) return true;\n\n // Automation browsers\n if (navigator.webdriver === true) return true;\n\n return false;\n } catch (error) {\n return false;\n }\n}\n\n // -------------------------\n // PUBLIC API\n // -------------------------\n init(cfg) {\n if (typeof window === \"undefined\") return;\n if (this._initialized) return; // strict-mode safe\n this._initialized = true;\n\n const resolvedBaseUrl =\n cfg.apiBaseUrl ||\n cfg.endpoint ||\n cfg.socketUrl ||\n \"https://api.twinalyze.com\";\n const normalizedBaseUrl = resolvedBaseUrl.endsWith(\"/\")\n ? resolvedBaseUrl.slice(0, -1)\n : resolvedBaseUrl;\n\n const DEFAULT_SCREEN_ACTIVITY = {\n enabled: false,\n apiUrl:\n cfg.screenActivity?.apiUrl ??\n `${normalizedBaseUrl}/api/app/screenActivity/screenActivityDetails_post`,\n captureOn: [\n \"pageView\",\n \"elementClick\",\n \"formStart\",\n \"formSubmit\",\n \"scrollDepth\",\n \"searchResultsView\",\n \"customEvent\",\n ],\n throttleMs: 2000,\n jpegQuality: 0.7,\n };\n\n // support: screenshots: true OR screenActivity: true OR screenActivity: { ... }\n const sa =\n typeof cfg.screenActivity === \"object\"\n ? { ...DEFAULT_SCREEN_ACTIVITY, ...cfg.screenActivity }\n : { ...DEFAULT_SCREEN_ACTIVITY };\n\n this.cfg = {\n apiKey: cfg.apiKey,\n secretKey: cfg.secretKey,\n version: cfg.version,\n apiBaseUrl: normalizedBaseUrl,\n\n debug: cfg.debug !== false, // default true in debug builds\n\n // encryption\n encrypt: cfg.encrypt !== false,\n\n // session id from client side\n persistSession: cfg.persistSession !== false, // default true (sessionStorage)\n sessionKey: \"twinalyze_session_id\",\n\n // eventAdd fields\n source: cfg.source || \"web\",\n indexId: cfg.indexId ?? null,\n\n // auto tracking options\n enhancedMeasurement: {\n pageViews: true,\n scrolls: true,\n outboundClicks: true,\n siteSearch: { params: [\"q\", \"s\", \"search\", \"query\"] },\n formInteractions: true,\n fileDownloads: {\n extensions: [\n \"pdf\",\n \"zip\",\n \"apk\",\n \"doc\",\n \"docx\",\n \"xls\",\n \"xlsx\",\n \"ppt\",\n \"pptx\",\n ],\n },\n ...(cfg.enhancedMeasurement || {}),\n },\n screenActivity: sa,\n };\n\n if (!this.cfg.apiKey) {\n console.log(\"[Twinalyze] ❌ Missing apiKey\");\n return;\n }\n // Stop bot before creating deviceId/session or sending API request\n if (this._isLikelyBot()) {\n this._disabled = true;\n this.sessionReady = false;\n this.pendingEvents = [];\n this.batchQueue = [];\n\n if (this.cfg?.debug) {\n console.log(\"[Twinalyze] 🤖 Bot detected. SDK stopped.\", {\n userAgent: navigator.userAgent,\n webdriver: navigator.webdriver === true,\n });\n }\n\n return;\n }\n\n\n\n this.deviceId = this._getOrCreateDeviceId();\n\n const startKey = \"twinalyze_session_start_time\";\n if (this.cfg.persistSession) {\n this.sessionId = sessionStorage.getItem(this.cfg.sessionKey) || null;\n this.sessionStartTime = sessionStorage.getItem(startKey) || null;\n }\n if (!this.sessionId) {\n this.sessionId = `sess_${uuid()}`;\n this.sessionStartTime = new Date().toISOString();\n if (this.cfg.persistSession) {\n sessionStorage.setItem(this.cfg.sessionKey, this.sessionId);\n sessionStorage.setItem(startKey, this.sessionStartTime);\n }\n } else if (!this.sessionStartTime) {\n this.sessionStartTime = new Date().toISOString();\n if (this.cfg.persistSession) {\n sessionStorage.setItem(startKey, this.sessionStartTime);\n }\n }\n\n this._setupEnhancedMeasurement();\n this._loadAndSendUnsentEvents();\n this._setupVisibilityAndUnloadHandlers();\n this._startFlow();\n }\n\n // custom event (web dev only uses this)\n track(eventName, properties = {}) {\n if (this._disabled) return;\n if (this.cfg && this.cfg.manualEventStatus === false) return;\n\n if (this.cfg?.debug)\n console.log(\"[Twinalyze][CUSTOM]\", eventName, properties);\n\n if (!this.sessionReady) {\n this.pendingEvents.push({\n eventName,\n properties: { ...this._pageContext(), ...properties },\n ts: Date.now(),\n eventType: \"manual\",\n });\n return;\n }\n\n this._emitEventAdd(\n eventName,\n { ...this._pageContext(), ...properties },\n \"manual\",\n );\n }\n\n // -------------------------\n // -------------------------\n // REST API FLOW\n // -------------------------\n async _startFlow() {\n if (this._disabled) return;\n if (this._booting) return;\n this._booting = true;\n\n try {\n // 1. Init API\n const initRes = await this._fetchApi(\"/api/web/sdk/init\", {});\n if (this.cfg.debug)\n console.log(\"[Twinalyze][FLOW] initRes: HAR HAR MAHADEV\", initRes);\n\n if (!initRes || initRes.success === false) {\n this._disabled = true;\n this.sessionReady = false;\n this.cfg.screenActivity.enabled = false;\n if (this.cfg.debug)\n console.log(\"[Twinalyze][FLOW] 🛑 STOP (init failed)\");\n return;\n }\n\n const data = initRes.data || {};\n\n // this.cfg.screenActivity.enabled = !!data.debugScreenshotCapture; //stopped screenshot\n this.cfg.screenActivity.enabled = false; // stops screenstop even if backend sends true\n\n if (data.eventData) {\n this.cfg.autoEventStatus = data.eventData.autoEventStatus !== false;\n this.cfg.manualEventStatus = data.eventData.manualEventStatus !== false;\n } else {\n this.cfg.autoEventStatus = true;\n this.cfg.manualEventStatus = true;\n }\n\n if (data.eventsBatchConfig) {\n this.batchConfig = {\n eventsBatchSize: data.eventsBatchConfig.eventsBatchSize || 10,\n flushIntervalMs: data.eventsBatchConfig.flushIntervalMs || 5000,\n sessionTimeout: data.eventsBatchConfig.sessionTimeout || 300000,\n };\n }\n\n this._initIndexId();\n\n // 2. Identify API\n const identifyPayload = {\n properties: this._userProperties(),\n deviceProperties: this._deviceProperties(),\n };\n\n const identifyRes = await this._fetchApi(\n \"/api/web/sdk/identify\",\n identifyPayload,\n );\n if (this.cfg.debug)\n console.log(\"[Twinalyze][FLOW] identifyRes:\", identifyRes);\n\n if (!identifyRes || identifyRes.success === false) {\n this._disabled = true;\n this.sessionReady = false;\n if (this.cfg.debug)\n console.log(\"[Twinalyze][FLOW] 🛑 STOP (identify failed)\");\n return;\n }\n\n // Mark ready\n this.sessionReady = true;\n\n // Flush queued auto/custom events (move from pendingEvents to batchQueue)\n if (this.pendingEvents.length > 0) {\n const items = this.pendingEvents.splice(0, this.pendingEvents.length);\n items.forEach((e) =>\n this._emitEventAdd(\n e.eventName,\n e.properties || {},\n e.eventType || \"auto\",\n ),\n );\n }\n } finally {\n this._booting = false;\n }\n }\n\n async _fetchApi(endpoint, payloadObj) {\n\n\n if (this._disabled || this._isLikelyBot()) {\n if (this.cfg?.debug) {\n console.log(\"[Twinalyze][API] skipped because bot/disabled\", endpoint);\n }\n return null;\n }\n\n const url = `${this.cfg.apiBaseUrl}${endpoint}`;\n\n const nonce = cryptoRandomNonce();\n const timestamp = new Date().toISOString();\n const bodyStr = payloadObj ? JSON.stringify(payloadObj) : \"{}\";\n\n const canonicalBody = stableStringify(JSON.parse(bodyStr));\n const bodyHash = CryptoJS.SHA256(canonicalBody).toString(CryptoJS.enc.Hex);\n\n const method = \"POST\";\n const baseString = [\n method,\n endpoint,\n timestamp,\n nonce,\n this.deviceId,\n bodyHash,\n ].join(\"\\n\");\n\n // Generate HMAC-SHA256 signature using secret key from config\n const SECRET_KEY = this.cfg.secretKey || \"\";\n const signature = CryptoJS.HmacSHA256(baseString, SECRET_KEY).toString(\n CryptoJS.enc.Hex,\n );\n\n if (this.cfg.debug) {\n console.log(\"[Twinalyze][API] Request signing trace:\", {\n endpoint,\n nonce,\n timestamp,\n canonicalBody,\n bodyHash,\n baseString,\n signature,\n });\n }\n\n const headers = {\n \"Content-Type\": \"application/json\",\n \"x-deviceId\": this.deviceId,\n \"x-api-key\": this.cfg.apiKey,\n \"x-analytics-sdk-version\": this.cfg.version || \"1.0.0\",\n \"x-ad-analytics-sdk-version\": \"\",\n // \"x-app-package-name\": window.location.href,\n \"x-app-package-name\": this._getAppPackageName(),\n \"x-platform\": \"javascript\",\n \"x-app-version-name\": \"1.0.0\",\n \"x-app-version-mode\": this.cfg.debug ? \"debug\" : \"release\",\n \"x-sdk-version\": \"latest\",\n \"x-timestamp\": timestamp,\n \"x-nonce\": nonce,\n \"x-signature\": signature,\n };\n\n try {\n const res = await fetch(url, {\n method: \"POST\",\n headers,\n body: bodyStr,\n keepalive: true,\n });\n return await res.json();\n } catch (err) {\n if (this.cfg.debug)\n console.log(`[Twinalyze][API] Error calling ${endpoint}:`, err);\n return null;\n }\n }\n\n async _waitForRenderStable() {\n // 2 frames\n await new Promise((r) =>\n requestAnimationFrame(() => requestAnimationFrame(r)),\n );\n\n // fonts\n try {\n if (document.fonts?.ready) await document.fonts.ready;\n } catch {}\n\n // images\n const imgs = Array.from(document.images || []).filter(\n (img) => !img.complete,\n );\n await Promise.allSettled(\n imgs.map(\n (img) =>\n new Promise((res) => {\n img.addEventListener(\"load\", res, { once: true });\n img.addEventListener(\"error\", res, { once: true });\n }),\n ),\n );\n }\n\n async _captureViewportJpegBlob() {\n await this._waitForRenderStable();\n\n const isViewport =\n (this.cfg.screenActivity?.capture || \"viewport\") === \"viewport\";\n const scale = Math.min(2, window.devicePixelRatio || 1);\n\n const canvas = await html2canvas(document.documentElement, {\n useCORS: true,\n allowTaint: false,\n backgroundColor: \"#ffffff\",\n logging: false,\n\n ...(isViewport\n ? {\n x: window.scrollX,\n y: window.scrollY,\n width: window.innerWidth,\n height: window.innerHeight,\n }\n : {\n x: 0,\n y: 0,\n width: document.documentElement.scrollWidth,\n height: document.documentElement.scrollHeight,\n }),\n\n scale,\n onclone: (doc) => {\n const style = doc.createElement(\"style\");\n style.textContent = `\n * { animation: none !important; transition: none !important; caret-color: transparent !important; }\n `;\n doc.head.appendChild(style);\n },\n });\n\n // downscale big screenshots (optional but helpful)\n const maxW = this.cfg.screenActivity?.maxWidth ?? 1280;\n if (canvas.width > maxW) {\n const ratio = maxW / canvas.width;\n const c2 = document.createElement(\"canvas\");\n c2.width = Math.round(canvas.width * ratio);\n c2.height = Math.round(canvas.height * ratio);\n c2.getContext(\"2d\").drawImage(canvas, 0, 0, c2.width, c2.height);\n return await new Promise((resolve) =>\n c2.toBlob(\n resolve,\n \"image/jpeg\",\n this.cfg.screenActivity?.jpegQuality ?? 0.7,\n ),\n );\n }\n\n return await new Promise((resolve) =>\n canvas.toBlob(\n resolve,\n \"image/jpeg\",\n this.cfg.screenActivity?.jpegQuality ?? 0.7,\n ),\n );\n }\n\n async _uploadScreenActivity({ eventName, properties }) {\n if (!this.cfg.screenActivity?.enabled) return;\n if (!this.sessionReady) return;\n\n const now = Date.now();\n const throttleMs = this.cfg.screenActivity?.throttleMs ?? 2000;\n if (now - this._lastShotAt < throttleMs) return;\n if (this._shotInFlight) return;\n\n this._shotInFlight = true;\n this._lastShotAt = now;\n\n try {\n const blob = await this._captureViewportJpegBlob();\n if (!blob) return;\n\n const fd = new FormData();\n fd.append(\"apiKey\", this.cfg.apiKey);\n fd.append(\"screenName\", document.title || \"unknown\");\n fd.append(\"identifier\", JSON.stringify(properties || {})); // ✅ you asked: identifier = properties object\n fd.append(\n \"appInfo\",\n JSON.stringify({\n appVersion: this.cfg.version || \"1.0.0\",\n platform: \"javascript\",\n }),\n );\n fd.append(\"description\", eventName || \"event\");\n fd.append(\"image\", blob, `shot_${Date.now()}.jpg`);\n\n const headers = {\n \"x-deviceId\": this.deviceId,\n \"x-api-key\": this.cfg.apiKey,\n \"x-analytics-sdk-version\": this.cfg.version || \"1.0.0\",\n \"x-ad-analytics-sdk-version\": \"\",\n // \"x-app-package-name\": window.location.href,\n \"x-app-package-name\": this._getAppPackageName(),\n \"x-platform\": \"javascript\",\n \"x-app-version-name\": \"1.0.0\",\n \"x-app-version-mode\": this.cfg.debug ? \"debug\" : \"release\",\n \"x-sdk-version\": \"latest\",\n };\n\n await fetch(this.cfg.screenActivity.apiUrl, {\n method: \"POST\",\n headers,\n body: fd,\n // keepalive: true, (commented because only 1 screenshot was coming and other were going in pending)\n });\n } catch (e) {\n if (this.cfg.debug)\n console.log(\"[Twinalyze][SCREENSHOT] error:\", e?.message || e);\n } finally {\n this._shotInFlight = false;\n }\n }\n\n // -------------------------\n // -------------------------\n // EMITS (REST API BATCHING)\n // -------------------------\n _emitEventAdd(eventName, properties = {}, eventType = \"auto\") {\n if (!this.sessionReady) return;\n\n this._checkSessionTimeoutBeforeEvent(); //function for session timeout check before emitting event and creating new session if needed\n\n if (this._indexKey == null) this._initIndexId();\n const indexId = this._nextIndexId();\n\n const eventObj = {\n uniqueSessionId: this.sessionId,\n date: new Date().toISOString(),\n eventType: eventType,\n eventName: eventName,\n properties: { eventName: eventName, ...properties }, //changed sequence so that eventName can be been first\n indexId: indexId,\n source: this.cfg.source,\n };\n\n // If this is the first event in a new batch, schedule a flush timer\n if (this.batchQueue.length === 0) {\n if (this._flushTimer) clearTimeout(this._flushTimer);\n this._flushTimer = setTimeout(\n () => this._flushBatchQueue(),\n this.batchConfig.flushIntervalMs,\n );\n }\n\n this.batchQueue.push(eventObj);\n\n // this._uploadScreenActivity({ eventName, properties }); // commented to stop screenshot\n\n if (this.cfg.debug)\n console.log(\"[Twinalyze][EVENT_QUEUED]\", eventName, properties, indexId);\n\n // Flush immediately if we hit the batch size limit\n if (this.batchQueue.length >= this.batchConfig.eventsBatchSize) {\n this._flushBatchQueue();\n }\n }\n\n async _flushBatchQueue() {\n // Clear any active timeout timer to prevent duplicate flushes\n if (this._flushTimer) {\n clearTimeout(this._flushTimer);\n this._flushTimer = null;\n }\n\n if (!this.sessionReady) return;\n if (!this.batchQueue.length) return;\n\n // slice to handle new events coming in while flushing\n const batch = this.batchQueue.splice(0, this.batchQueue.length);\n\n if (this.cfg.debug)\n console.log(`[Twinalyze][FLUSHING] ${batch.length} events...`);\n\n const payload = {\n eventsBatch: [\n {\n events: batch.map((e) => ({\n appInfo: {\n appVersion: this.cfg.version || \"1.0.0\",\n platform: \"javascript\",\n },\n date: e.date,\n eventName: e.eventName,\n eventType: e.eventType,\n indexId: e.indexId,\n properties: e.properties,\n })),\n startDate: this.sessionStartTime || batch[0].date,\n uniqueSessionId: this.sessionId,\n },\n ],\n };\n const res = await this._fetchApi(\"/api/web/sdk/eventsBatch\", payload);\n\n if (this.cfg.debug) console.log(\"[Twinalyze][FLUSH_RES]\", res);\n\n if (!res || res.success === false) {\n if (this.cfg.debug) console.log(\"[Twinalyze][FLUSH_FAIL] events dropped\");\n // Could re-queue here if desired: this.batchQueue.unshift(...batch);\n }\n }\n\n // -------------------------\n // AUTO EVENTS (GA-style)\n // -------------------------\n _trackAuto(name, props = {}) {\n if (this._disabled) return;\n if (this.cfg && this.cfg.autoEventStatus === false) return;\n if (this.cfg?.debug) console.log(\"[Twinalyze][AUTO]\", name, props);\n\n if (!this.sessionReady) {\n this.pendingEvents.push({\n eventName: name,\n properties: props,\n ts: Date.now(),\n eventType: \"auto\",\n });\n return;\n }\n\n this._emitEventAdd(name, props, \"auto\");\n }\n\n _setupEnhancedMeasurement() {\n const em = this.cfg.enhancedMeasurement || {};\n\n // pageView (normal + SPA)\n if (em.pageViews) {\n const pv = this._pageViewInfo();\n this._trackAuto(\"pageView\", pv);\n\n const fire = () => {\n this.scrollFired = false;\n const pv2 = this._pageViewInfo();\n this._trackAuto(\"pageView\", pv2);\n this._fireSiteSearch();\n };\n\n const _push = history.pushState;\n const _replace = history.replaceState;\n\n history.pushState = (...args) => {\n _push.apply(history, args);\n fire();\n };\n history.replaceState = (...args) => {\n _replace.apply(history, args);\n fire();\n };\n window.addEventListener(\"popstate\", fire);\n }\n\n // if user scrolls above 90% than event will come\n if (em.scrolls) {\n window.addEventListener(\n \"scroll\",\n () => {\n if (this.scrollFired) return;\n\n const doc = document.documentElement;\n const scrollTop = window.scrollY || doc.scrollTop;\n const scrollHeight = doc.scrollHeight - doc.clientHeight;\n if (scrollHeight <= 0) return;\n\n const percent = Math.round((scrollTop / scrollHeight) * 100);\n if (percent >= 90) {\n this.scrollFired = true;\n this._trackAuto(\"scrollDepth\", {\n scrollPercent: percent,\n ...this._pageContext(),\n });\n }\n },\n { passive: true },\n );\n }\n\n // // outbound clicks\n // if (em.outboundClicks) {\n // document.addEventListener(\"click\", (e) => {\n // const a = e.target && e.target.closest ? e.target.closest(\"a\") : null;\n // if (!a || !a.href) return;\n // if (/^(javascript:|mailto:|tel:)/i.test(a.href)) return;\n\n // let url;\n // try { url = new URL(a.href); } catch { return; }\n // if (url.hostname !== location.hostname) {\n // console.log(\"click\", { link_url: url.href, link_domain: url.hostname, outbound: true });\n // this._trackAuto(\"click\", { link_url: url.href, link_domain: url.hostname, outbound: true });\n // }\n // }, true);\n // }\n\n // clicks (links + buttons + dropdown items + generic interactive)\n if (em.outboundClicks) {\n document.addEventListener(\n \"click\",\n (e) => {\n const hit = this._getClickTarget(e);\n if (!hit) return;\n\n const el = hit.el;\n\n const tag = (el.tagName || \"\").toLowerCase();\n const text = this._getTextFromElement(el);\n\n const props = {\n clickType: hit.kind, // link | button | dropdown_item | interactive\n elementTag: tag,\n elementText: text || \"not_found\", // ✅ innerText/aria-label/title\n elementId: el.id || null,\n elementName: el.getAttribute?.(\"name\") || null,\n elementRole: el.getAttribute?.(\"role\") || null,\n /* elementClass:\n (el.className && typeof el.className === \"string\") ? el.className.slice(0, 120) : null,\n elementSelector: this._cssPath(el), */\n ...((this._pageContext && this._pageContext()) || {}), // if you added _pageContext().\n };\n\n // If link, enrich with URL + outbound\n if (hit.kind === \"link\") {\n const href = el.getAttribute(\"href\") || \"\";\n if (/^(javascript:|mailto:|tel:)/i.test(href)) return;\n\n let url;\n try {\n url = new URL(el.href);\n } catch {\n return;\n }\n\n props.linkUrl = url.href;\n props.linkDomain = url.hostname;\n props.linkPath = url.pathname;\n props.isExternalLink = url.hostname !== location.hostname; // changed name from isOutbound to isExternalLink\n }\n\n // Button details\n if (hit.kind === \"button\") {\n props.buttonType = el.getAttribute?.(\"type\") || null;\n props.disabled = !!el.disabled;\n }\n\n this._trackAuto(\"elementClick\", props);\n },\n true,\n );\n }\n\n // site search\n if (em.siteSearch) this._fireSiteSearch();\n\n // form interactions\n if (em.formInteractions) {\n const started = new WeakSet();\n\n document.addEventListener(\"focusin\", (e) => {\n const form =\n e.target && e.target.closest ? e.target.closest(\"form\") : null;\n if (!form || started.has(form)) return;\n started.add(form);\n\n this._trackAuto(\"formStart\", {\n formId: form.id || undefined,\n formName: form.getAttribute(\"name\") || undefined,\n formActionUrl: form.action || undefined,\n ...this._pageContext(),\n });\n });\n\n document.addEventListener(\n \"submit\",\n (e) => {\n const form = e.target;\n if (!form) return;\n\n this._trackAuto(\"formSubmit\", {\n formStatus: \"success\", // changed sequence\n formActionUrl: form.action || undefined,\n formDestinationUrl: location.href,\n formId: form.id || undefined,\n formName: form.getAttribute(\"name\") || undefined,\n ...this._pageContext(),\n });\n },\n true,\n );\n }\n\n // file downloads\n if (em.fileDownloads && em.fileDownloads.extensions) {\n const exts = em.fileDownloads.extensions.map((x) =>\n String(x).toLowerCase(),\n );\n\n document.addEventListener(\n \"click\",\n (e) => {\n const a = e.target && e.target.closest ? e.target.closest(\"a\") : null;\n if (!a || !a.href) return;\n\n const clean = a.href.split(\"?\")[0].toLowerCase();\n const ext = clean.split(\".\").pop() || \"\";\n if (!exts.includes(ext)) return;\n\n this._trackAuto(\"fileDownload\", {\n linkUrl: a.href,\n fileExtension: ext,\n fileName: clean.split(\"/\").pop(),\n ...this._pageContext(),\n });\n },\n true,\n );\n }\n }\n\n _fireSiteSearch() {\n const em = this.cfg.enhancedMeasurement || {};\n if (!em.siteSearch) return;\n\n const params = em.siteSearch.params || [\"q\", \"s\", \"search\", \"query\"];\n const usp = new URLSearchParams(location.search);\n const key = params.find((k) => usp.get(k));\n const term = key ? usp.get(key) : null;\n\n if (term && term.trim()) {\n this._trackAuto(\"searchResultsView\", {\n searchKey: key, //changed sequence\n searchTerm: term.trim(),\n searchUrl: location.href,\n ...this._pageContext(),\n });\n }\n }\n\n _getTextFromElement(el) {\n if (!el) return null;\n\n // common places (MUI/AntD often store label in aria-label)\n const aria = el.getAttribute?.(\"aria-label\");\n if (aria && aria.trim()) return aria.trim();\n\n const title = el.getAttribute?.(\"title\");\n if (title && title.trim()) return title.trim();\n\n // visible text\n const txt = (el.innerText || el.textContent || \"\").trim();\n if (!txt) return null;\n\n // limit length (avoid huge HTML)\n return txt.length > 80 ? txt.slice(0, 80) : txt;\n }\n\n _cssPath(el) {\n if (!el || !el.tagName) return null;\n const parts = [];\n let node = el;\n let depth = 0;\n while (node && node.nodeType === 1 && depth < 5) {\n let part = node.tagName.toLowerCase();\n if (node.id) {\n part += `#${node.id}`;\n parts.unshift(part);\n break;\n }\n const cls =\n node.className && typeof node.className === \"string\"\n ? node.className.trim().split(/\\s+/).slice(0, 2).join(\".\")\n : \"\";\n if (cls) part += `.${cls}`;\n parts.unshift(part);\n node = node.parentElement;\n depth++;\n }\n return parts.join(\" > \");\n }\n\n _getClickTarget(e) {\n // Shadow DOM safe path\n const path = typeof e.composedPath === \"function\" ? e.composedPath() : null;\n const start = path && path.length ? path[0] : e.target;\n\n const closest = (el, sel) => (el && el.closest ? el.closest(sel) : null);\n\n // 1) Links\n const a = closest(start, \"a[href]\");\n if (a) return { el: a, kind: \"link\" };\n\n // 2) Buttons + button-like\n const btn = closest(\n start,\n 'button, input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"], [role=\"button\"]',\n );\n if (btn) return { el: btn, kind: \"button\" };\n\n // 3) Menu / dropdown item (generic roles)\n const menuItem = closest(\n start,\n '[role=\"menuitem\"], [role=\"menuitemradio\"], [role=\"menuitemcheckbox\"], [role=\"option\"]',\n );\n if (menuItem) return { el: menuItem, kind: \"dropdown_item\" };\n\n // 4) Generic \"interactive\" fallback:\n // - role link\n // - tabindex (focusable)\n // - aria-haspopup (opens menu)\n // - contenteditable\n const interactive = closest(\n start,\n '[role=\"link\"], [tabindex]:not([tabindex=\"-1\"]), [aria-haspopup], [contenteditable=\"true\"]',\n );\n if (interactive) return { el: interactive, kind: \"interactive\" };\n\n return null;\n }\n\n // -------------------------\n // PROPERTIES (AUTO)\n // -------------------------\n // _deviceProperties() {\n // return {\n // ua: navigator.userAgent,\n // lang: navigator.language,\n // tz: Intl.DateTimeFormat().resolvedOptions().timeZone,\n // screen: { w: screen.width, h: screen.height },\n // viewport: { w: window.innerWidth, h: window.innerHeight },\n // dpr: window.devicePixelRatio || 1,\n // };\n // }\n\n _deviceProperties() {\n const parser = new UAParser(navigator.userAgent);\n const ua = parser.getResult();\n\n const conn =\n navigator.connection ||\n navigator.mozConnection ||\n navigator.webkitConnection;\n\n const screenMode = window.matchMedia?.(\"(prefers-color-scheme: dark)\")\n ?.matches\n ? \"dark\"\n : \"light\";\n\n return {\n // device/browser\n device_screen_mode: screenMode,\n is_data_on: navigator.onLine ? \"true\" : \"false\",\n screen_width: String(screen.width),\n screen_height: String(screen.height),\n viewport_width: String(window.innerWidth),\n viewport_height: String(window.innerHeight),\n color_depth: String(screen.colorDepth || \"not_found\"),\n browser_name: ua.browser?.name || \"not_found\",\n browser_version: ua.browser?.version || \"not_found\",\n os_name: ua.os?.name || \"not_found\",\n os_version: ua.os?.version || \"not_found\",\n // app_language: navigator.language || \"not_found\",\n app_language: this._getLanguageCode(navigator.language),\n cpu_cores:\n navigator.hardwareConcurrency != null\n ? String(navigator.hardwareConcurrency)\n : \"not_found\",\n touch_points:\n navigator.maxTouchPoints != null\n ? String(navigator.maxTouchPoints)\n : \"0\",\n network_type: conn?.effectiveType || \"not_found\",\n network_speed_mbps:\n conn?.downlink != null ? String(conn.downlink) : \"not_found\",\n network_latency_ms: conn?.rtt != null ? String(conn.rtt) : \"not_found\",\n data_saver_enabled:\n conn?.saveData != null ? String(conn.saveData) : \"not_found\",\n user_agent: navigator.userAgent,\n analytics_version: this.cfg?.version || \"not_found\",\n };\n }\n\n _normalizeUrl(urlStr) {\n if (!urlStr) return urlStr;\n try {\n const url = new URL(urlStr);\n const newParams = new URLSearchParams();\n const campaignKeys = [\"source\", \"medium\", \"campaign\", \"term\", \"content\"];\n for (const [key, val] of url.searchParams.entries()) {\n if (key.toLowerCase().startsWith(\"utm\")) {\n newParams.set(key.toLowerCase(), val);\n } else if (campaignKeys.includes(key.toLowerCase())) {\n newParams.set(\"utm_\" + key.toLowerCase(), val);\n } else {\n newParams.set(key, val);\n }\n }\n url.search = newParams.toString();\n return url.href;\n } catch (e) {\n return urlStr;\n }\n }\n\n // _userProperties() {\n // const parser = new UAParser(navigator.userAgent);\n // const ua = parser.getResult();\n\n // const normalizedUrlStr = this._normalizeUrl(location.href);\n // const url = new URL(normalizedUrlStr);\n // const usp = url.searchParams;\n\n // const get = (k) => usp.get(k);\n // const nf = \"not_found\";\n\n // return {\n // // From GSheet User Properties (Left Side) - Web Platform\n // device_type:\n // ua.device?.type ||\n // (matchMedia(\"(pointer:coarse)\").matches ? \"mobile\" : \"desktop\"),\n // density: String(window.devicePixelRatio || 1),\n // // timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || \"not_found\", // commented because timezone is fetched by backend\n // ram:\n // navigator.deviceMemory != null\n // ? String(navigator.deviceMemory)\n // : \"not_found\",\n // // device_language: navigator.language || \"not_found\",\n // device_language: this._getLanguageCode(navigator.language),\n // // acquisition\n // landing_url: normalizedUrlStr,\n // utm_source: get(\"utm_source\") || nf,\n // utm_medium: get(\"utm_medium\") || nf,\n // utm_campaign: get(\"utm_campaign\") || nf,\n // utm_term: get(\"utm_term\") || nf,\n // utm_content: get(\"utm_content\") || nf,\n\n // // ads click ids\n // utm_gclid: get(\"gclid\") || nf,\n // utm_fbclid: get(\"fbclid\") || nf,\n // utm_msclkid: get(\"msclkid\") || nf,\n\n // // identity (optional, only if you have it)\n // // user_id: this.userId || nf,\n // // email: this.email || nf,\n // // plan: \"free\" / \"pro\" etc.\n // };\n // }\n\n // If Utm source not found then it will not be sent to backend. This is done to avoid sending not_found values to backend for utm params and ads click ids.\n\n _userProperties() {\n const parser = new UAParser(navigator.userAgent);\n const ua = parser.getResult();\n\n const normalizedUrlStr = this._normalizeUrl(location.href);\n const url = new URL(normalizedUrlStr);\n const usp = url.searchParams;\n\n const get = (k) => usp.get(k);\n\n const properties = {\n // From GSheet User Properties (Left Side) - Web Platform\n device_type:\n ua.device?.type ||\n (matchMedia(\"(pointer:coarse)\").matches ? \"mobile\" : \"desktop\"),\n\n density: String(window.devicePixelRatio || 1),\n\n // timeZone is fetched by backend\n ram:\n navigator.deviceMemory != null\n ? String(navigator.deviceMemory)\n : \"not_found\",\n\n device_language: this._getLanguageCode(navigator.language),\n\n // acquisition\n landing_url: normalizedUrlStr,\n };\n\n const addIfFound = (key, value) => {\n if (value !== null && value !== undefined && String(value).trim() !== \"\") {\n properties[key] = value;\n }\n };\n\n // UTM params - only send if found\n addIfFound(\"utm_source\", get(\"utm_source\"));\n addIfFound(\"utm_medium\", get(\"utm_medium\"));\n addIfFound(\"utm_campaign\", get(\"utm_campaign\"));\n addIfFound(\"utm_term\", get(\"utm_term\"));\n addIfFound(\"utm_content\", get(\"utm_content\"));\n\n // Ads click ids - only send if found\n addIfFound(\"utm_gclid\", get(\"gclid\"));\n addIfFound(\"utm_fbclid\", get(\"fbclid\"));\n addIfFound(\"utm_msclkid\", get(\"msclkid\"));\n\n return properties;\n}\n\n // -------------------------\n // IDs\n // -------------------------\n _initIndexId() {\n this._indexKey = `twinalyze_index_${this.sessionId}`;\n this.indexId = parseInt(sessionStorage.getItem(this._indexKey) || \"0\", 10);\n if (Number.isNaN(this.indexId)) this.indexId = 0;\n }\n\n _nextIndexId() {\n this.indexId += 1;\n if (this._indexKey) {\n sessionStorage.setItem(this._indexKey, String(this.indexId));\n }\n return this.indexId;\n }\n\n // added below section for session timeout and new session creation after idle time\n\n _getLastEventTime() {\n try {\n return parseInt(\n sessionStorage.getItem(\"twinalyze_last_event_time\") || \"0\",\n 10,\n );\n } catch {\n return 0;\n }\n }\n\n _setLastEventTime() {\n try {\n sessionStorage.setItem(\"twinalyze_last_event_time\", String(Date.now()));\n } catch {\n // ignore\n }\n }\n\n _createNewSessionAfterIdle() {\n const startKey = \"twinalyze_session_start_time\";\n\n this.sessionId = `sess_${uuid()}`;\n this.sessionStartTime = new Date().toISOString();\n\n if (this.cfg?.persistSession !== false) {\n sessionStorage.setItem(this.cfg.sessionKey, this.sessionId);\n sessionStorage.setItem(startKey, this.sessionStartTime);\n }\n\n // reset event index for new session\n this._indexKey = null;\n this.indexId = 0;\n this._initIndexId();\n\n if (this.cfg?.debug) {\n console.log(\n \"[Twinalyze][SESSION] New session created after idle:\",\n this.sessionId,\n );\n }\n }\n\n _checkSessionTimeoutBeforeEvent() {\n const lastEventTime = this._getLastEventTime();\n const timeout = this.batchConfig?.sessionTimeout || 300000;\n\n if (lastEventTime && Date.now() - lastEventTime > timeout) {\n this._createNewSessionAfterIdle();\n }\n\n this._setLastEventTime();\n }\n // added above section for sessiontime\n _getOrCreateDeviceId() {\n const key = \"twinalyze_device_id\";\n const old = localStorage.getItem(key);\n if (old) return old;\n\n const id = uuid();\n // const id = `dev_${uuid()}`;\n localStorage.setItem(key, id);\n return id;\n }\n\n _pageContext() {\n const KEY_LAST = \"twinalyze_last_page_location\";\n\n const pageTitle = document.title;\n const pageLocation = this._normalizeUrl(location.href);\n const pageDomain = location.hostname;\n const pagePath = location.pathname;\n\n const storedPrev = sessionStorage.getItem(KEY_LAST);\n const prevLocation = storedPrev || document.referrer || null;\n\n let prevType = \"direct\";\n if (prevLocation) {\n try {\n const prevHost = new URL(prevLocation).hostname;\n prevType = prevHost === pageDomain ? \"internal\" : \"external\";\n } catch {\n prevType = \"external\";\n }\n }\n\n return {\n pageTitle: pageTitle, //path changed from 4th to first position\n pageDomain: pageDomain,\n pageUrl: pageLocation,\n pagePath: pagePath,\n\n previousPageUrl: prevLocation\n ? this._normalizeUrl(prevLocation)\n : prevLocation, // PreviousName = referrerUrl\n previousPageType: prevType, // PreviousName = referrerType\n };\n }\n\n _pageViewInfo() {\n const KEY_LAST = \"twinalyze_last_page_location\";\n // const KEY_COUNT = \"twinalyze_page_counter\";\n // const KEY_LAST_COUNTED = \"twinalyze_last_counted_path\"; // ✅ new\n\n const ctx = this._pageContext(); // uses current location + previous page\n\n /*\n const currentKey = ctx.pagePath; // ✅ count only on path change\n // If you want count only on full URL change, use: const currentKey = ctx.pageUrl;\n\n const lastCounted = sessionStorage.getItem(KEY_LAST_COUNTED);\n\n // If same page refreshed -> DO NOT increment\n let pageCounter = parseInt(sessionStorage.getItem(KEY_COUNT) || \"0\", 10);\n if (Number.isNaN(pageCounter)) pageCounter = 0;\n\n if (lastCounted !== currentKey) {\n pageCounter += 1;\n sessionStorage.setItem(KEY_COUNT, String(pageCounter));\n sessionStorage.setItem(KEY_LAST_COUNTED, currentKey);\n } else {\n // keep same counter\n sessionStorage.setItem(KEY_COUNT, String(pageCounter));\n }\n*/\n // update last page location (for prev page logic)\n sessionStorage.setItem(KEY_LAST, ctx.pageUrl);\n\n return {\n // pageCount: pageCounter,\n ...ctx,\n };\n }\n\n _saveUnsentEvents() {\n try {\n const unsent = [...this.pendingEvents, ...this.batchQueue];\n if (unsent.length > 0) {\n localStorage.setItem(\"twinalyze_unsent_events\", JSON.stringify(unsent));\n } else {\n localStorage.removeItem(\"twinalyze_unsent_events\");\n }\n } catch (e) {\n if (this.cfg?.debug)\n console.log(\"[Twinalyze] Error saving unsent events:\", e);\n }\n }\n\n _loadAndSendUnsentEvents() {\n try {\n const stored = localStorage.getItem(\"twinalyze_unsent_events\");\n if (stored) {\n const events = JSON.parse(stored);\n localStorage.removeItem(\"twinalyze_unsent_events\");\n if (Array.isArray(events) && events.length > 0) {\n if (this.cfg?.debug)\n console.log(\n `[Twinalyze] Loaded ${events.length} unsent events from localStorage`,\n );\n events.forEach((e) => {\n if (this.sessionReady) {\n this._emitEventAdd(\n e.eventName,\n e.properties || {},\n e.eventType || \"auto\",\n );\n } else {\n this.pendingEvents.push(e);\n }\n });\n }\n }\n } catch (e) {\n if (this.cfg?.debug)\n console.log(\"[Twinalyze] Error loading unsent events:\", e);\n }\n }\n\n _setupVisibilityAndUnloadHandlers() {\n const flushAndSave = () => {\n if (this.cfg?.debug)\n console.log(\n \"[Twinalyze] Visibility changed or page hiding. Flushing events...\",\n );\n\n const wasReady = this.sessionReady;\n if (!this.sessionReady && this.pendingEvents.length > 0) {\n if (this.cfg?.debug)\n console.log(\"[Twinalyze] Force flushing pending events on unload...\");\n const items = this.pendingEvents.splice(0, this.pendingEvents.length);\n items.forEach((e) => {\n if (this._indexKey == null) this._initIndexId();\n const indexId = this._nextIndexId();\n this.batchQueue.push({\n uniqueSessionId: this.sessionId,\n date: new Date(e.ts || Date.now()).toISOString(),\n eventType: e.eventType || \"auto\",\n eventName: e.eventName,\n properties: { eventName: e.eventName, ...e.properties }, // changed sequence\n indexId: indexId,\n source: this.cfg.source,\n });\n });\n this.sessionReady = true;\n }\n\n if (this.batchQueue.length > 0) {\n this._flushBatchQueue();\n }\n\n if (!wasReady) {\n this.sessionReady = wasReady;\n }\n\n this._saveUnsentEvents();\n };\n\n document.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"hidden\") {\n flushAndSave();\n }\n });\n\n window.addEventListener(\"pagehide\", () => {\n flushAndSave();\n });\n }\n}\n\nconst _instance = new TwinalyzeAnalyticsImpl();\n\nconst PUBLIC_METHODS = [\"init\", \"track\"];\n\nconst TwinalyzeAnalytics = Object.fromEntries(\n PUBLIC_METHODS.map((m) => [m, _instance[m].bind(_instance)]),\n);\nexport { TwinalyzeAnalytics };\nexport default TwinalyzeAnalytics;\n"],"mappings":"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","names":["src_exports","__export","TwinalyzeAnalytics","src_default","__toCommonJS","import_crypto_js","import_html2canvas","UAParserPkg","UAParser","uuid","cryptoRandomNonce","c","r","stableStringify","obj","k","TwinalyzeAnalyticsImpl","lang","userAgent","botRegex","cfg","_a","_b","_c","_d","resolvedBaseUrl","normalizedBaseUrl","DEFAULT_SCREEN_ACTIVITY","sa","startKey","eventName","properties","initRes","data","identifyPayload","identifyRes","e","endpoint","payloadObj","url","nonce","timestamp","bodyStr","canonicalBody","bodyHash","CryptoJS","baseString","SECRET_KEY","signature","headers","err","imgs","img","res","isViewport","scale","canvas","html2canvas","doc","style","maxW","ratio","c2","resolve","now","throttleMs","blob","fd","eventType","indexId","eventObj","batch","payload","name","props","em","pv","fire","pv2","_push","_replace","args","scrollTop","scrollHeight","percent","hit","el","tag","text","href","started","form","exts","x","a","clean","ext","params","usp","key","term","aria","title","txt","parts","node","depth","part","cls","path","start","closest","sel","btn","menuItem","interactive","_e","_f","_g","ua","conn","urlStr","newParams","campaignKeys","val","normalizedUrlStr","get","addIfFound","value","lastEventTime","timeout","old","id","KEY_LAST","pageTitle","pageLocation","pageDomain","pagePath","prevLocation","prevType","ctx","unsent","stored","events","flushAndSave","wasReady","_instance","PUBLIC_METHODS","m","src_default","TwinalyzeAnalytics"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import g from"crypto-js";import x from"html2canvas";import*as
|
|
2
|
-
`),
|
|
1
|
+
import g from"crypto-js";import x from"html2canvas";import*as S from"ua-parser-js";var v=S.UAParser;function f(){return typeof crypto!="undefined"&&crypto.randomUUID?crypto.randomUUID():Math.random().toString(16).slice(2)+Date.now().toString(16)}var I=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,d=>{let t=Math.random()*16|0;return(d==="x"?t:t&3|8).toString(16)}),p=d=>d===null||typeof d!="object"?JSON.stringify(d):Array.isArray(d)?"["+d.map(p).join(",")+"]":"{"+Object.keys(d).filter(s=>d[s]!==void 0).sort().map(s=>JSON.stringify(s)+":"+p(d[s])).join(",")+"}",m=class{constructor(){this.cfg=null,this.deviceId=null,this.sessionId=null,this.sessionStartTime=null,this.sessionReady=!1,this._booting=!1,this._disabled=!1,this.pendingEvents=[],this.batchQueue=[],this.scrollFired=!1,this._initialized=!1,this._flushTimer=null,this.indexId=0,this._indexKey=null,this._lastShotAt=0,this._shotInFlight=!1,this.batchConfig={eventsBatchSize:10,flushIntervalMs:5e3,sessionTimeout:3e5}}_getAppPackageName(){try{return window.location.hostname.replace(/^www\./i,"").toLowerCase()}catch{return"unknown"}}_getLanguageCode(t){return t?t.split("-")[0].toLowerCase():"not_found"}_isLikelyBot(){try{let t=navigator.userAgent||"",s=/bot|crawler|spider|crawling|googlebot|bingbot|yandexbot|duckduckbot|baiduspider|facebookexternalhit|twitterbot|linkedinbot|whatsapp|telegrambot|pinterest|ahrefs|semrush|mj12bot|dotbot|petalbot|bytespider|gptbot|ccbot|claudebot|perplexitybot|headlesschrome|puppeteer|playwright|phantomjs|lighthouse|pagespeed|gtmetrix|pingdom/i;return!!(!t||s.test(t)||navigator.webdriver===!0)}catch{return!1}}init(t){var c,a,r,l;if(typeof window=="undefined"||this._initialized)return;this._initialized=!0;let s=t.apiBaseUrl||t.endpoint||t.socketUrl||"https://api.twinalyze.com",e=s.endsWith("/")?s.slice(0,-1):s,i={enabled:!1,apiUrl:(a=(c=t.screenActivity)==null?void 0:c.apiUrl)!=null?a:`${e}/api/app/screenActivity/screenActivityDetails_post`,captureOn:["pageView","elementClick","formStart","formSubmit","scrollDepth","searchResultsView","customEvent"],throttleMs:2e3,jpegQuality:.7},o=typeof t.screenActivity=="object"?{...i,...t.screenActivity}:{...i};if(this.cfg={apiKey:t.apiKey,secretKey:t.secretKey,version:t.version,apiBaseUrl:e,debug:t.debug!==!1,encrypt:t.encrypt!==!1,persistSession:t.persistSession!==!1,sessionKey:"twinalyze_session_id",source:t.source||"web",indexId:(r=t.indexId)!=null?r:null,enhancedMeasurement:{pageViews:!0,scrolls:!0,outboundClicks:!0,siteSearch:{params:["q","s","search","query"]},formInteractions:!0,fileDownloads:{extensions:["pdf","zip","apk","doc","docx","xls","xlsx","ppt","pptx"]},...t.enhancedMeasurement||{}},screenActivity:o},!this.cfg.apiKey){console.log("[Twinalyze] \u274C Missing apiKey");return}if(this._isLikelyBot()){this._disabled=!0,this.sessionReady=!1,this.pendingEvents=[],this.batchQueue=[],(l=this.cfg)!=null&&l.debug&&console.log("[Twinalyze] \u{1F916} Bot detected. SDK stopped.",{userAgent:navigator.userAgent,webdriver:navigator.webdriver===!0});return}this.deviceId=this._getOrCreateDeviceId();let n="twinalyze_session_start_time";this.cfg.persistSession&&(this.sessionId=sessionStorage.getItem(this.cfg.sessionKey)||null,this.sessionStartTime=sessionStorage.getItem(n)||null),this.sessionId?this.sessionStartTime||(this.sessionStartTime=new Date().toISOString(),this.cfg.persistSession&&sessionStorage.setItem(n,this.sessionStartTime)):(this.sessionId=`sess_${f()}`,this.sessionStartTime=new Date().toISOString(),this.cfg.persistSession&&(sessionStorage.setItem(this.cfg.sessionKey,this.sessionId),sessionStorage.setItem(n,this.sessionStartTime))),this._setupEnhancedMeasurement(),this._loadAndSendUnsentEvents(),this._setupVisibilityAndUnloadHandlers(),this._startFlow()}track(t,s={}){var e;if(!this._disabled&&!(this.cfg&&this.cfg.manualEventStatus===!1)){if((e=this.cfg)!=null&&e.debug&&console.log("[Twinalyze][CUSTOM]",t,s),!this.sessionReady){this.pendingEvents.push({eventName:t,properties:{...this._pageContext(),...s},ts:Date.now(),eventType:"manual"});return}this._emitEventAdd(t,{...this._pageContext(),...s},"manual")}}async _startFlow(){if(!this._disabled&&!this._booting){this._booting=!0;try{let t=await this._fetchApi("/api/web/sdk/init",{});if(this.cfg.debug&&console.log("[Twinalyze][FLOW] initRes: HAR HAR MAHADEV",t),!t||t.success===!1){this._disabled=!0,this.sessionReady=!1,this.cfg.screenActivity.enabled=!1,this.cfg.debug&&console.log("[Twinalyze][FLOW] \u{1F6D1} STOP (init failed)");return}let s=t.data||{};this.cfg.screenActivity.enabled=!1,s.eventData?(this.cfg.autoEventStatus=s.eventData.autoEventStatus!==!1,this.cfg.manualEventStatus=s.eventData.manualEventStatus!==!1):(this.cfg.autoEventStatus=!0,this.cfg.manualEventStatus=!0),s.eventsBatchConfig&&(this.batchConfig={eventsBatchSize:s.eventsBatchConfig.eventsBatchSize||10,flushIntervalMs:s.eventsBatchConfig.flushIntervalMs||5e3,sessionTimeout:s.eventsBatchConfig.sessionTimeout||3e5}),this._initIndexId();let e={properties:this._userProperties(),deviceProperties:this._deviceProperties()},i=await this._fetchApi("/api/web/sdk/identify",e);if(this.cfg.debug&&console.log("[Twinalyze][FLOW] identifyRes:",i),!i||i.success===!1){this._disabled=!0,this.sessionReady=!1,this.cfg.debug&&console.log("[Twinalyze][FLOW] \u{1F6D1} STOP (identify failed)");return}this.sessionReady=!0,this.pendingEvents.length>0&&this.pendingEvents.splice(0,this.pendingEvents.length).forEach(n=>this._emitEventAdd(n.eventName,n.properties||{},n.eventType||"auto"))}finally{this._booting=!1}}}async _fetchApi(t,s){var _;if(this._disabled||this._isLikelyBot())return(_=this.cfg)!=null&&_.debug&&console.log("[Twinalyze][API] skipped because bot/disabled",t),null;let e=`${this.cfg.apiBaseUrl}${t}`,i=I(),o=new Date().toISOString(),n=s?JSON.stringify(s):"{}",c=p(JSON.parse(n)),a=g.SHA256(c).toString(g.enc.Hex),l=["POST",t,o,i,this.deviceId,a].join(`
|
|
2
|
+
`),h=this.cfg.secretKey||"",u=g.HmacSHA256(l,h).toString(g.enc.Hex);this.cfg.debug&&console.log("[Twinalyze][API] Request signing trace:",{endpoint:t,nonce:i,timestamp:o,canonicalBody:c,bodyHash:a,baseString:l,signature:u});let b={"Content-Type":"application/json","x-deviceId":this.deviceId,"x-api-key":this.cfg.apiKey,"x-analytics-sdk-version":this.cfg.version||"1.0.0","x-ad-analytics-sdk-version":"","x-app-package-name":this._getAppPackageName(),"x-platform":"javascript","x-app-version-name":"1.0.0","x-app-version-mode":this.cfg.debug?"debug":"release","x-sdk-version":"latest","x-timestamp":o,"x-nonce":i,"x-signature":u};try{return await(await fetch(e,{method:"POST",headers:b,body:n,keepalive:!0})).json()}catch(y){return this.cfg.debug&&console.log(`[Twinalyze][API] Error calling ${t}:`,y),null}}async _waitForRenderStable(){var s;await new Promise(e=>requestAnimationFrame(()=>requestAnimationFrame(e)));try{(s=document.fonts)!=null&&s.ready&&await document.fonts.ready}catch{}let t=Array.from(document.images||[]).filter(e=>!e.complete);await Promise.allSettled(t.map(e=>new Promise(i=>{e.addEventListener("load",i,{once:!0}),e.addEventListener("error",i,{once:!0})})))}async _captureViewportJpegBlob(){var o,n,c;await this._waitForRenderStable();let t=(((o=this.cfg.screenActivity)==null?void 0:o.capture)||"viewport")==="viewport",s=Math.min(2,window.devicePixelRatio||1),e=await x(document.documentElement,{useCORS:!0,allowTaint:!1,backgroundColor:"#ffffff",logging:!1,...t?{x:window.scrollX,y:window.scrollY,width:window.innerWidth,height:window.innerHeight}:{x:0,y:0,width:document.documentElement.scrollWidth,height:document.documentElement.scrollHeight},scale:s,onclone:a=>{let r=a.createElement("style");r.textContent=`
|
|
3
3
|
* { animation: none !important; transition: none !important; caret-color: transparent !important; }
|
|
4
|
-
`,r.head.appendChild(c)}}),i=(a=(n=this.cfg.screenActivity)==null?void 0:n.maxWidth)!=null?a:1280;if(e.width>i){let r=i/e.width,c=document.createElement("canvas");return c.width=Math.round(e.width*r),c.height=Math.round(e.height*r),c.getContext("2d").drawImage(e,0,0,c.width,c.height),await new Promise(l=>{var u,d;return c.toBlob(l,"image/jpeg",(d=(u=this.cfg.screenActivity)==null?void 0:u.jpegQuality)!=null?d:.7)})}return await new Promise(r=>{var c,l;return e.toBlob(r,"image/jpeg",(l=(c=this.cfg.screenActivity)==null?void 0:c.jpegQuality)!=null?l:.7)})}async _uploadScreenActivity({eventName:t,properties:s}){var o,n,a;if(!((o=this.cfg.screenActivity)!=null&&o.enabled)||!this.sessionReady)return;let e=Date.now(),i=(a=(n=this.cfg.screenActivity)==null?void 0:n.throttleMs)!=null?a:2e3;if(!(e-this._lastShotAt<i)&&!this._shotInFlight){this._shotInFlight=!0,this._lastShotAt=e;try{let r=await this._captureViewportJpegBlob();if(!r)return;let c=new FormData;c.append("apiKey",this.cfg.apiKey),c.append("screenName",document.title||"unknown"),c.append("identifier",JSON.stringify(s||{})),c.append("appInfo",JSON.stringify({appVersion:this.cfg.version||"1.0.0",platform:"javascript"})),c.append("description",t||"event"),c.append("image",r,`shot_${Date.now()}.jpg`);let l={"x-deviceId":this.deviceId,"x-api-key":this.cfg.apiKey,"x-analytics-sdk-version":this.cfg.version||"1.0.0","x-ad-analytics-sdk-version":"","x-app-package-name":this._getAppPackageName(),"x-platform":"javascript","x-app-version-name":"1.0.0","x-app-version-mode":this.cfg.debug?"debug":"release","x-sdk-version":"latest"};await fetch(this.cfg.screenActivity.apiUrl,{method:"POST",headers:l,body:c})}catch(r){this.cfg.debug&&console.log("[Twinalyze][SCREENSHOT] error:",(r==null?void 0:r.message)||r)}finally{this._shotInFlight=!1}}}_emitEventAdd(t,s={},e="auto"){if(!this.sessionReady)return;this._checkSessionTimeoutBeforeEvent(),this._indexKey==null&&this._initIndexId();let i=this._nextIndexId(),o={uniqueSessionId:this.sessionId,date:new Date().toISOString(),eventType:e,eventName:t,properties:{eventName:t,...s},indexId:i,source:this.cfg.source};this.batchQueue.length===0&&(this._flushTimer&&clearTimeout(this._flushTimer),this._flushTimer=setTimeout(()=>this._flushBatchQueue(),this.batchConfig.flushIntervalMs)),this.batchQueue.push(o),this.cfg.debug&&console.log("[Twinalyze][EVENT_QUEUED]",t,s,i),this.batchQueue.length>=this.batchConfig.eventsBatchSize&&this._flushBatchQueue()}async _flushBatchQueue(){if(this._flushTimer&&(clearTimeout(this._flushTimer),this._flushTimer=null),!this.sessionReady||!this.batchQueue.length)return;let t=this.batchQueue.splice(0,this.batchQueue.length);this.cfg.debug&&console.log(`[Twinalyze][FLUSHING] ${t.length} events...`);let s={eventsBatch:[{events:t.map(i=>({appInfo:{appVersion:this.cfg.version||"1.0.0",platform:"javascript"},date:i.date,eventName:i.eventName,eventType:i.eventType,indexId:i.indexId,properties:i.properties})),startDate:this.sessionStartTime||t[0].date,uniqueSessionId:this.sessionId}]},e=await this._fetchApi("/api/web/sdk/eventsBatch",s);this.cfg.debug&&console.log("[Twinalyze][FLUSH_RES]",e),(!e||e.success===!1)&&this.cfg.debug&&console.log("[Twinalyze][FLUSH_FAIL] events dropped")}_trackAuto(t,s={}){var e;if(!this._disabled&&!(this.cfg&&this.cfg.autoEventStatus===!1)){if((e=this.cfg)!=null&&e.debug&&console.log("[Twinalyze][AUTO]",t,s),!this.sessionReady){this.pendingEvents.push({eventName:t,properties:s,ts:Date.now(),eventType:"auto"});return}this._emitEventAdd(t,s,"auto")}}_setupEnhancedMeasurement(){let t=this.cfg.enhancedMeasurement||{};if(t.pageViews){let s=this._pageViewInfo();this._trackAuto("pageView",s);let e=()=>{this.scrollFired=!1;let n=this._pageViewInfo();this._trackAuto("pageView",n),this._fireSiteSearch()},i=history.pushState,o=history.replaceState;history.pushState=(...n)=>{i.apply(history,n),e()},history.replaceState=(...n)=>{o.apply(history,n),e()},window.addEventListener("popstate",e)}if(t.scrolls&&window.addEventListener("scroll",()=>{if(this.scrollFired)return;let s=document.documentElement,e=window.scrollY||s.scrollTop,i=s.scrollHeight-s.clientHeight;if(i<=0)return;let o=Math.round(e/i*100);o>=90&&(this.scrollFired=!0,this._trackAuto("scrollDepth",{scrollPercent:o,...this._pageContext()}))},{passive:!0}),t.outboundClicks&&document.addEventListener("click",s=>{var r,c,l;let e=this._getClickTarget(s);if(!e)return;let i=e.el,o=(i.tagName||"").toLowerCase(),n=this._getTextFromElement(i),a={clickType:e.kind,elementTag:o,elementText:n||"not_found",elementId:i.id||null,elementName:((r=i.getAttribute)==null?void 0:r.call(i,"name"))||null,elementRole:((c=i.getAttribute)==null?void 0:c.call(i,"role"))||null,...this._pageContext&&this._pageContext()||{}};if(e.kind==="link"){let u=i.getAttribute("href")||"";if(/^(javascript:|mailto:|tel:)/i.test(u))return;let d;try{d=new URL(i.href)}catch{return}a.linkUrl=d.href,a.linkDomain=d.hostname,a.linkPath=d.pathname,a.isExternalLink=d.hostname!==location.hostname}e.kind==="button"&&(a.buttonType=((l=i.getAttribute)==null?void 0:l.call(i,"type"))||null,a.disabled=!!i.disabled),this._trackAuto("elementClick",a)},!0),t.siteSearch&&this._fireSiteSearch(),t.formInteractions){let s=new WeakSet;document.addEventListener("focusin",e=>{let i=e.target&&e.target.closest?e.target.closest("form"):null;!i||s.has(i)||(s.add(i),this._trackAuto("formStart",{formId:i.id||void 0,formName:i.getAttribute("name")||void 0,formActionUrl:i.action||void 0,...this._pageContext()}))}),document.addEventListener("submit",e=>{let i=e.target;i&&this._trackAuto("formSubmit",{formStatus:"success",formActionUrl:i.action||void 0,formDestinationUrl:location.href,formId:i.id||void 0,formName:i.getAttribute("name")||void 0,...this._pageContext()})},!0)}if(t.fileDownloads&&t.fileDownloads.extensions){let s=t.fileDownloads.extensions.map(e=>String(e).toLowerCase());document.addEventListener("click",e=>{let i=e.target&&e.target.closest?e.target.closest("a"):null;if(!i||!i.href)return;let o=i.href.split("?")[0].toLowerCase(),n=o.split(".").pop()||"";s.includes(n)&&this._trackAuto("fileDownload",{linkUrl:i.href,fileExtension:n,fileName:o.split("/").pop(),...this._pageContext()})},!0)}}_fireSiteSearch(){let t=this.cfg.enhancedMeasurement||{};if(!t.siteSearch)return;let s=t.siteSearch.params||["q","s","search","query"],e=new URLSearchParams(location.search),i=s.find(n=>e.get(n)),o=i?e.get(i):null;o&&o.trim()&&this._trackAuto("searchResultsView",{searchKey:i,searchTerm:o.trim(),searchUrl:location.href,...this._pageContext()})}_getTextFromElement(t){var o,n;if(!t)return null;let s=(o=t.getAttribute)==null?void 0:o.call(t,"aria-label");if(s&&s.trim())return s.trim();let e=(n=t.getAttribute)==null?void 0:n.call(t,"title");if(e&&e.trim())return e.trim();let i=(t.innerText||t.textContent||"").trim();return i?i.length>80?i.slice(0,80):i:null}_cssPath(t){if(!t||!t.tagName)return null;let s=[],e=t,i=0;for(;e&&e.nodeType===1&&i<5;){let o=e.tagName.toLowerCase();if(e.id){o+=`#${e.id}`,s.unshift(o);break}let n=e.className&&typeof e.className=="string"?e.className.trim().split(/\s+/).slice(0,2).join("."):"";n&&(o+=`.${n}`),s.unshift(o),e=e.parentElement,i++}return s.join(" > ")}_getClickTarget(t){let s=typeof t.composedPath=="function"?t.composedPath():null,e=s&&s.length?s[0]:t.target,i=(c,l)=>c&&c.closest?c.closest(l):null,o=i(e,"a[href]");if(o)return{el:o,kind:"link"};let n=i(e,'button, input[type="button"], input[type="submit"], input[type="reset"], [role="button"]');if(n)return{el:n,kind:"button"};let a=i(e,'[role="menuitem"], [role="menuitemradio"], [role="menuitemcheckbox"], [role="option"]');if(a)return{el:a,kind:"dropdown_item"};let r=i(e,'[role="link"], [tabindex]:not([tabindex="-1"]), [aria-haspopup], [contenteditable="true"]');return r?{el:r,kind:"interactive"}:null}_deviceProperties(){var o,n,a,r,c,l,u;let s=new y(navigator.userAgent).getResult(),e=navigator.connection||navigator.mozConnection||navigator.webkitConnection;return{device_screen_mode:(n=(o=window.matchMedia)==null?void 0:o.call(window,"(prefers-color-scheme: dark)"))!=null&&n.matches?"dark":"light",is_data_on:navigator.onLine?"true":"false",screen_width:String(screen.width),screen_height:String(screen.height),viewport_width:String(window.innerWidth),viewport_height:String(window.innerHeight),color_depth:String(screen.colorDepth||"not_found"),browser_name:((a=s.browser)==null?void 0:a.name)||"not_found",browser_version:((r=s.browser)==null?void 0:r.version)||"not_found",os_name:((c=s.os)==null?void 0:c.name)||"not_found",os_version:((l=s.os)==null?void 0:l.version)||"not_found",app_language:navigator.language||"not_found",cpu_cores:navigator.hardwareConcurrency!=null?String(navigator.hardwareConcurrency):"not_found",touch_points:navigator.maxTouchPoints!=null?String(navigator.maxTouchPoints):"0",network_type:(e==null?void 0:e.effectiveType)||"not_found",network_speed_mbps:(e==null?void 0:e.downlink)!=null?String(e.downlink):"not_found",network_latency_ms:(e==null?void 0:e.rtt)!=null?String(e.rtt):"not_found",data_saver_enabled:(e==null?void 0:e.saveData)!=null?String(e.saveData):"not_found",user_agent:navigator.userAgent,analytics_version:((u=this.cfg)==null?void 0:u.version)||"not_found"}}_normalizeUrl(t){if(!t)return t;try{let s=new URL(t),e=new URLSearchParams,i=["source","medium","campaign","term","content"];for(let[o,n]of s.searchParams.entries())o.toLowerCase().startsWith("utm")?e.set(o.toLowerCase(),n):i.includes(o.toLowerCase())?e.set("utm_"+o.toLowerCase(),n):e.set(o,n);return s.search=e.toString(),s.href}catch{return t}}_userProperties(){var r;let s=new y(navigator.userAgent).getResult(),e=this._normalizeUrl(location.href),o=new URL(e).searchParams,n=c=>o.get(c),a="not_found";return{device_type:((r=s.device)==null?void 0:r.type)||(matchMedia("(pointer:coarse)").matches?"mobile":"desktop"),density:String(window.devicePixelRatio||1),timeZone:Intl.DateTimeFormat().resolvedOptions().timeZone||"not_found",ram:navigator.deviceMemory!=null?String(navigator.deviceMemory):"not_found",device_language:navigator.language||"not_found",landing_url:e,utm_source:n("utm_source")||a,utm_medium:n("utm_medium")||a,utm_campaign:n("utm_campaign")||a,utm_term:n("utm_term")||a,utm_content:n("utm_content")||a,utm_gclid:n("gclid")||a,utm_fbclid:n("fbclid")||a,utm_msclkid:n("msclkid")||a}}_initIndexId(){this._indexKey=`twinalyze_index_${this.sessionId}`,this.indexId=parseInt(sessionStorage.getItem(this._indexKey)||"0",10),Number.isNaN(this.indexId)&&(this.indexId=0)}_nextIndexId(){return this.indexId+=1,this._indexKey&&sessionStorage.setItem(this._indexKey,String(this.indexId)),this.indexId}_getLastEventTime(){try{return parseInt(sessionStorage.getItem("twinalyze_last_event_time")||"0",10)}catch{return 0}}_setLastEventTime(){try{sessionStorage.setItem("twinalyze_last_event_time",String(Date.now()))}catch{}}_createNewSessionAfterIdle(){var s,e;let t="twinalyze_session_start_time";this.sessionId=`sess_${f()}`,this.sessionStartTime=new Date().toISOString(),((s=this.cfg)==null?void 0:s.persistSession)!==!1&&(sessionStorage.setItem(this.cfg.sessionKey,this.sessionId),sessionStorage.setItem(t,this.sessionStartTime)),this._indexKey=null,this.indexId=0,this._initIndexId(),(e=this.cfg)!=null&&e.debug&&console.log("[Twinalyze][SESSION] New session created after idle:",this.sessionId)}_checkSessionTimeoutBeforeEvent(){var e;let t=this._getLastEventTime(),s=((e=this.batchConfig)==null?void 0:e.sessionTimeout)||3e5;t&&Date.now()-t>s&&this._createNewSessionAfterIdle(),this._setLastEventTime()}_getOrCreateDeviceId(){let t="twinalyze_device_id",s=localStorage.getItem(t);if(s)return s;let e=f();return localStorage.setItem(t,e),e}_pageContext(){let t="twinalyze_last_page_location",s=document.title,e=this._normalizeUrl(location.href),i=location.hostname,o=location.pathname,a=sessionStorage.getItem(t)||document.referrer||null,r="direct";if(a)try{r=new URL(a).hostname===i?"internal":"external"}catch{r="external"}return{pageTitle:s,pageDomain:i,pageUrl:e,pagePath:o,previousPageUrl:a&&this._normalizeUrl(a),previousPageType:r}}_pageViewInfo(){let t="twinalyze_last_page_location",s=this._pageContext();return sessionStorage.setItem(t,s.pageUrl),{...s}}_saveUnsentEvents(){var t;try{let s=[...this.pendingEvents,...this.batchQueue];s.length>0?localStorage.setItem("twinalyze_unsent_events",JSON.stringify(s)):localStorage.removeItem("twinalyze_unsent_events")}catch(s){(t=this.cfg)!=null&&t.debug&&console.log("[Twinalyze] Error saving unsent events:",s)}}_loadAndSendUnsentEvents(){var t,s;try{let e=localStorage.getItem("twinalyze_unsent_events");if(e){let i=JSON.parse(e);localStorage.removeItem("twinalyze_unsent_events"),Array.isArray(i)&&i.length>0&&((t=this.cfg)!=null&&t.debug&&console.log(`[Twinalyze] Loaded ${i.length} unsent events from localStorage`),i.forEach(o=>{this.sessionReady?this._emitEventAdd(o.eventName,o.properties||{},o.eventType||"auto"):this.pendingEvents.push(o)}))}}catch(e){(s=this.cfg)!=null&&s.debug&&console.log("[Twinalyze] Error loading unsent events:",e)}}_setupVisibilityAndUnloadHandlers(){let t=()=>{var e,i;(e=this.cfg)!=null&&e.debug&&console.log("[Twinalyze] Visibility changed or page hiding. Flushing events...");let s=this.sessionReady;!this.sessionReady&&this.pendingEvents.length>0&&((i=this.cfg)!=null&&i.debug&&console.log("[Twinalyze] Force flushing pending events on unload..."),this.pendingEvents.splice(0,this.pendingEvents.length).forEach(n=>{this._indexKey==null&&this._initIndexId();let a=this._nextIndexId();this.batchQueue.push({uniqueSessionId:this.sessionId,date:new Date(n.ts||Date.now()).toISOString(),eventType:n.eventType||"auto",eventName:n.eventName,properties:{eventName:n.eventName,...n.properties},indexId:a,source:this.cfg.source})}),this.sessionReady=!0),this.batchQueue.length>0&&this._flushBatchQueue(),s||(this.sessionReady=s),this._saveUnsentEvents()};document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&t()}),window.addEventListener("pagehide",()=>{t()})}},v=new p,I=["init","track"],T=Object.fromEntries(I.map(h=>[h,v[h].bind(v)]));var k=T;export{T as TwinalyzeAnalytics,k as default};
|
|
4
|
+
`,a.head.appendChild(r)}}),i=(c=(n=this.cfg.screenActivity)==null?void 0:n.maxWidth)!=null?c:1280;if(e.width>i){let a=i/e.width,r=document.createElement("canvas");return r.width=Math.round(e.width*a),r.height=Math.round(e.height*a),r.getContext("2d").drawImage(e,0,0,r.width,r.height),await new Promise(l=>{var h,u;return r.toBlob(l,"image/jpeg",(u=(h=this.cfg.screenActivity)==null?void 0:h.jpegQuality)!=null?u:.7)})}return await new Promise(a=>{var r,l;return e.toBlob(a,"image/jpeg",(l=(r=this.cfg.screenActivity)==null?void 0:r.jpegQuality)!=null?l:.7)})}async _uploadScreenActivity({eventName:t,properties:s}){var o,n,c;if(!((o=this.cfg.screenActivity)!=null&&o.enabled)||!this.sessionReady)return;let e=Date.now(),i=(c=(n=this.cfg.screenActivity)==null?void 0:n.throttleMs)!=null?c:2e3;if(!(e-this._lastShotAt<i)&&!this._shotInFlight){this._shotInFlight=!0,this._lastShotAt=e;try{let a=await this._captureViewportJpegBlob();if(!a)return;let r=new FormData;r.append("apiKey",this.cfg.apiKey),r.append("screenName",document.title||"unknown"),r.append("identifier",JSON.stringify(s||{})),r.append("appInfo",JSON.stringify({appVersion:this.cfg.version||"1.0.0",platform:"javascript"})),r.append("description",t||"event"),r.append("image",a,`shot_${Date.now()}.jpg`);let l={"x-deviceId":this.deviceId,"x-api-key":this.cfg.apiKey,"x-analytics-sdk-version":this.cfg.version||"1.0.0","x-ad-analytics-sdk-version":"","x-app-package-name":this._getAppPackageName(),"x-platform":"javascript","x-app-version-name":"1.0.0","x-app-version-mode":this.cfg.debug?"debug":"release","x-sdk-version":"latest"};await fetch(this.cfg.screenActivity.apiUrl,{method:"POST",headers:l,body:r})}catch(a){this.cfg.debug&&console.log("[Twinalyze][SCREENSHOT] error:",(a==null?void 0:a.message)||a)}finally{this._shotInFlight=!1}}}_emitEventAdd(t,s={},e="auto"){if(!this.sessionReady)return;this._checkSessionTimeoutBeforeEvent(),this._indexKey==null&&this._initIndexId();let i=this._nextIndexId(),o={uniqueSessionId:this.sessionId,date:new Date().toISOString(),eventType:e,eventName:t,properties:{eventName:t,...s},indexId:i,source:this.cfg.source};this.batchQueue.length===0&&(this._flushTimer&&clearTimeout(this._flushTimer),this._flushTimer=setTimeout(()=>this._flushBatchQueue(),this.batchConfig.flushIntervalMs)),this.batchQueue.push(o),this.cfg.debug&&console.log("[Twinalyze][EVENT_QUEUED]",t,s,i),this.batchQueue.length>=this.batchConfig.eventsBatchSize&&this._flushBatchQueue()}async _flushBatchQueue(){if(this._flushTimer&&(clearTimeout(this._flushTimer),this._flushTimer=null),!this.sessionReady||!this.batchQueue.length)return;let t=this.batchQueue.splice(0,this.batchQueue.length);this.cfg.debug&&console.log(`[Twinalyze][FLUSHING] ${t.length} events...`);let s={eventsBatch:[{events:t.map(i=>({appInfo:{appVersion:this.cfg.version||"1.0.0",platform:"javascript"},date:i.date,eventName:i.eventName,eventType:i.eventType,indexId:i.indexId,properties:i.properties})),startDate:this.sessionStartTime||t[0].date,uniqueSessionId:this.sessionId}]},e=await this._fetchApi("/api/web/sdk/eventsBatch",s);this.cfg.debug&&console.log("[Twinalyze][FLUSH_RES]",e),(!e||e.success===!1)&&this.cfg.debug&&console.log("[Twinalyze][FLUSH_FAIL] events dropped")}_trackAuto(t,s={}){var e;if(!this._disabled&&!(this.cfg&&this.cfg.autoEventStatus===!1)){if((e=this.cfg)!=null&&e.debug&&console.log("[Twinalyze][AUTO]",t,s),!this.sessionReady){this.pendingEvents.push({eventName:t,properties:s,ts:Date.now(),eventType:"auto"});return}this._emitEventAdd(t,s,"auto")}}_setupEnhancedMeasurement(){let t=this.cfg.enhancedMeasurement||{};if(t.pageViews){let s=this._pageViewInfo();this._trackAuto("pageView",s);let e=()=>{this.scrollFired=!1;let n=this._pageViewInfo();this._trackAuto("pageView",n),this._fireSiteSearch()},i=history.pushState,o=history.replaceState;history.pushState=(...n)=>{i.apply(history,n),e()},history.replaceState=(...n)=>{o.apply(history,n),e()},window.addEventListener("popstate",e)}if(t.scrolls&&window.addEventListener("scroll",()=>{if(this.scrollFired)return;let s=document.documentElement,e=window.scrollY||s.scrollTop,i=s.scrollHeight-s.clientHeight;if(i<=0)return;let o=Math.round(e/i*100);o>=90&&(this.scrollFired=!0,this._trackAuto("scrollDepth",{scrollPercent:o,...this._pageContext()}))},{passive:!0}),t.outboundClicks&&document.addEventListener("click",s=>{var a,r,l;let e=this._getClickTarget(s);if(!e)return;let i=e.el,o=(i.tagName||"").toLowerCase(),n=this._getTextFromElement(i),c={clickType:e.kind,elementTag:o,elementText:n||"not_found",elementId:i.id||null,elementName:((a=i.getAttribute)==null?void 0:a.call(i,"name"))||null,elementRole:((r=i.getAttribute)==null?void 0:r.call(i,"role"))||null,...this._pageContext&&this._pageContext()||{}};if(e.kind==="link"){let h=i.getAttribute("href")||"";if(/^(javascript:|mailto:|tel:)/i.test(h))return;let u;try{u=new URL(i.href)}catch{return}c.linkUrl=u.href,c.linkDomain=u.hostname,c.linkPath=u.pathname,c.isExternalLink=u.hostname!==location.hostname}e.kind==="button"&&(c.buttonType=((l=i.getAttribute)==null?void 0:l.call(i,"type"))||null,c.disabled=!!i.disabled),this._trackAuto("elementClick",c)},!0),t.siteSearch&&this._fireSiteSearch(),t.formInteractions){let s=new WeakSet;document.addEventListener("focusin",e=>{let i=e.target&&e.target.closest?e.target.closest("form"):null;!i||s.has(i)||(s.add(i),this._trackAuto("formStart",{formId:i.id||void 0,formName:i.getAttribute("name")||void 0,formActionUrl:i.action||void 0,...this._pageContext()}))}),document.addEventListener("submit",e=>{let i=e.target;i&&this._trackAuto("formSubmit",{formStatus:"success",formActionUrl:i.action||void 0,formDestinationUrl:location.href,formId:i.id||void 0,formName:i.getAttribute("name")||void 0,...this._pageContext()})},!0)}if(t.fileDownloads&&t.fileDownloads.extensions){let s=t.fileDownloads.extensions.map(e=>String(e).toLowerCase());document.addEventListener("click",e=>{let i=e.target&&e.target.closest?e.target.closest("a"):null;if(!i||!i.href)return;let o=i.href.split("?")[0].toLowerCase(),n=o.split(".").pop()||"";s.includes(n)&&this._trackAuto("fileDownload",{linkUrl:i.href,fileExtension:n,fileName:o.split("/").pop(),...this._pageContext()})},!0)}}_fireSiteSearch(){let t=this.cfg.enhancedMeasurement||{};if(!t.siteSearch)return;let s=t.siteSearch.params||["q","s","search","query"],e=new URLSearchParams(location.search),i=s.find(n=>e.get(n)),o=i?e.get(i):null;o&&o.trim()&&this._trackAuto("searchResultsView",{searchKey:i,searchTerm:o.trim(),searchUrl:location.href,...this._pageContext()})}_getTextFromElement(t){var o,n;if(!t)return null;let s=(o=t.getAttribute)==null?void 0:o.call(t,"aria-label");if(s&&s.trim())return s.trim();let e=(n=t.getAttribute)==null?void 0:n.call(t,"title");if(e&&e.trim())return e.trim();let i=(t.innerText||t.textContent||"").trim();return i?i.length>80?i.slice(0,80):i:null}_cssPath(t){if(!t||!t.tagName)return null;let s=[],e=t,i=0;for(;e&&e.nodeType===1&&i<5;){let o=e.tagName.toLowerCase();if(e.id){o+=`#${e.id}`,s.unshift(o);break}let n=e.className&&typeof e.className=="string"?e.className.trim().split(/\s+/).slice(0,2).join("."):"";n&&(o+=`.${n}`),s.unshift(o),e=e.parentElement,i++}return s.join(" > ")}_getClickTarget(t){let s=typeof t.composedPath=="function"?t.composedPath():null,e=s&&s.length?s[0]:t.target,i=(r,l)=>r&&r.closest?r.closest(l):null,o=i(e,"a[href]");if(o)return{el:o,kind:"link"};let n=i(e,'button, input[type="button"], input[type="submit"], input[type="reset"], [role="button"]');if(n)return{el:n,kind:"button"};let c=i(e,'[role="menuitem"], [role="menuitemradio"], [role="menuitemcheckbox"], [role="option"]');if(c)return{el:c,kind:"dropdown_item"};let a=i(e,'[role="link"], [tabindex]:not([tabindex="-1"]), [aria-haspopup], [contenteditable="true"]');return a?{el:a,kind:"interactive"}:null}_deviceProperties(){var o,n,c,a,r,l,h;let s=new v(navigator.userAgent).getResult(),e=navigator.connection||navigator.mozConnection||navigator.webkitConnection;return{device_screen_mode:(n=(o=window.matchMedia)==null?void 0:o.call(window,"(prefers-color-scheme: dark)"))!=null&&n.matches?"dark":"light",is_data_on:navigator.onLine?"true":"false",screen_width:String(screen.width),screen_height:String(screen.height),viewport_width:String(window.innerWidth),viewport_height:String(window.innerHeight),color_depth:String(screen.colorDepth||"not_found"),browser_name:((c=s.browser)==null?void 0:c.name)||"not_found",browser_version:((a=s.browser)==null?void 0:a.version)||"not_found",os_name:((r=s.os)==null?void 0:r.name)||"not_found",os_version:((l=s.os)==null?void 0:l.version)||"not_found",app_language:this._getLanguageCode(navigator.language),cpu_cores:navigator.hardwareConcurrency!=null?String(navigator.hardwareConcurrency):"not_found",touch_points:navigator.maxTouchPoints!=null?String(navigator.maxTouchPoints):"0",network_type:(e==null?void 0:e.effectiveType)||"not_found",network_speed_mbps:(e==null?void 0:e.downlink)!=null?String(e.downlink):"not_found",network_latency_ms:(e==null?void 0:e.rtt)!=null?String(e.rtt):"not_found",data_saver_enabled:(e==null?void 0:e.saveData)!=null?String(e.saveData):"not_found",user_agent:navigator.userAgent,analytics_version:((h=this.cfg)==null?void 0:h.version)||"not_found"}}_normalizeUrl(t){if(!t)return t;try{let s=new URL(t),e=new URLSearchParams,i=["source","medium","campaign","term","content"];for(let[o,n]of s.searchParams.entries())o.toLowerCase().startsWith("utm")?e.set(o.toLowerCase(),n):i.includes(o.toLowerCase())?e.set("utm_"+o.toLowerCase(),n):e.set(o,n);return s.search=e.toString(),s.href}catch{return t}}_userProperties(){var r;let s=new v(navigator.userAgent).getResult(),e=this._normalizeUrl(location.href),o=new URL(e).searchParams,n=l=>o.get(l),c={device_type:((r=s.device)==null?void 0:r.type)||(matchMedia("(pointer:coarse)").matches?"mobile":"desktop"),density:String(window.devicePixelRatio||1),ram:navigator.deviceMemory!=null?String(navigator.deviceMemory):"not_found",device_language:this._getLanguageCode(navigator.language),landing_url:e},a=(l,h)=>{h!=null&&String(h).trim()!==""&&(c[l]=h)};return a("utm_source",n("utm_source")),a("utm_medium",n("utm_medium")),a("utm_campaign",n("utm_campaign")),a("utm_term",n("utm_term")),a("utm_content",n("utm_content")),a("utm_gclid",n("gclid")),a("utm_fbclid",n("fbclid")),a("utm_msclkid",n("msclkid")),c}_initIndexId(){this._indexKey=`twinalyze_index_${this.sessionId}`,this.indexId=parseInt(sessionStorage.getItem(this._indexKey)||"0",10),Number.isNaN(this.indexId)&&(this.indexId=0)}_nextIndexId(){return this.indexId+=1,this._indexKey&&sessionStorage.setItem(this._indexKey,String(this.indexId)),this.indexId}_getLastEventTime(){try{return parseInt(sessionStorage.getItem("twinalyze_last_event_time")||"0",10)}catch{return 0}}_setLastEventTime(){try{sessionStorage.setItem("twinalyze_last_event_time",String(Date.now()))}catch{}}_createNewSessionAfterIdle(){var s,e;let t="twinalyze_session_start_time";this.sessionId=`sess_${f()}`,this.sessionStartTime=new Date().toISOString(),((s=this.cfg)==null?void 0:s.persistSession)!==!1&&(sessionStorage.setItem(this.cfg.sessionKey,this.sessionId),sessionStorage.setItem(t,this.sessionStartTime)),this._indexKey=null,this.indexId=0,this._initIndexId(),(e=this.cfg)!=null&&e.debug&&console.log("[Twinalyze][SESSION] New session created after idle:",this.sessionId)}_checkSessionTimeoutBeforeEvent(){var e;let t=this._getLastEventTime(),s=((e=this.batchConfig)==null?void 0:e.sessionTimeout)||3e5;t&&Date.now()-t>s&&this._createNewSessionAfterIdle(),this._setLastEventTime()}_getOrCreateDeviceId(){let t="twinalyze_device_id",s=localStorage.getItem(t);if(s)return s;let e=f();return localStorage.setItem(t,e),e}_pageContext(){let t="twinalyze_last_page_location",s=document.title,e=this._normalizeUrl(location.href),i=location.hostname,o=location.pathname,c=sessionStorage.getItem(t)||document.referrer||null,a="direct";if(c)try{a=new URL(c).hostname===i?"internal":"external"}catch{a="external"}return{pageTitle:s,pageDomain:i,pageUrl:e,pagePath:o,previousPageUrl:c&&this._normalizeUrl(c),previousPageType:a}}_pageViewInfo(){let t="twinalyze_last_page_location",s=this._pageContext();return sessionStorage.setItem(t,s.pageUrl),{...s}}_saveUnsentEvents(){var t;try{let s=[...this.pendingEvents,...this.batchQueue];s.length>0?localStorage.setItem("twinalyze_unsent_events",JSON.stringify(s)):localStorage.removeItem("twinalyze_unsent_events")}catch(s){(t=this.cfg)!=null&&t.debug&&console.log("[Twinalyze] Error saving unsent events:",s)}}_loadAndSendUnsentEvents(){var t,s;try{let e=localStorage.getItem("twinalyze_unsent_events");if(e){let i=JSON.parse(e);localStorage.removeItem("twinalyze_unsent_events"),Array.isArray(i)&&i.length>0&&((t=this.cfg)!=null&&t.debug&&console.log(`[Twinalyze] Loaded ${i.length} unsent events from localStorage`),i.forEach(o=>{this.sessionReady?this._emitEventAdd(o.eventName,o.properties||{},o.eventType||"auto"):this.pendingEvents.push(o)}))}}catch(e){(s=this.cfg)!=null&&s.debug&&console.log("[Twinalyze] Error loading unsent events:",e)}}_setupVisibilityAndUnloadHandlers(){let t=()=>{var e,i;(e=this.cfg)!=null&&e.debug&&console.log("[Twinalyze] Visibility changed or page hiding. Flushing events...");let s=this.sessionReady;!this.sessionReady&&this.pendingEvents.length>0&&((i=this.cfg)!=null&&i.debug&&console.log("[Twinalyze] Force flushing pending events on unload..."),this.pendingEvents.splice(0,this.pendingEvents.length).forEach(n=>{this._indexKey==null&&this._initIndexId();let c=this._nextIndexId();this.batchQueue.push({uniqueSessionId:this.sessionId,date:new Date(n.ts||Date.now()).toISOString(),eventType:n.eventType||"auto",eventName:n.eventName,properties:{eventName:n.eventName,...n.properties},indexId:c,source:this.cfg.source})}),this.sessionReady=!0),this.batchQueue.length>0&&this._flushBatchQueue(),s||(this.sessionReady=s),this._saveUnsentEvents()};document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&t()}),window.addEventListener("pagehide",()=>{t()})}},w=new m,T=["init","track"],A=Object.fromEntries(T.map(d=>[d,w[d].bind(w)]));var C=A;export{A as TwinalyzeAnalytics,C as default};
|
|
5
5
|
//# sourceMappingURL=index.mjs.map
|