@action-x/ad-sdk 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +7 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +183 -171
- package/dist/index.umd.js +7 -7
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class K{constructor(){this.events={}}on(e,t){this.events[e]||(this.events[e]=[]),this.events[e].push(t)}off(e,t){if(!this.events[e])return;const i=this.events[e].indexOf(t);i>-1&&this.events[e].splice(i,1)}emit(e,...t){this.events[e]&&this.events[e].forEach(i=>{try{i(...t)}catch(s){console.error(`[EventEmitter] Error in event handler for "${e}":`,s)}})}removeAllListeners(e){e?delete this.events[e]:this.events={}}listenerCount(e){var t;return((t=this.events[e])==null?void 0:t.length)||0}}class w{static getClickUrl(e){return e.tracking.clickUrl||e.tracking.click_url||"#"}static getImpressionUrl(e){return e.tracking.impressionUrl||e.tracking.impression_url||""}static getCtaText(e){return e.adapted.ctaText||e.adapted.cta_text||"Learn More"}static renderActionCard(e,t={}){var f;const{variant:i="horizontal",includeWrapper:s=!0}=t,n=e.adapted,r=this.getClickUrl(e),a=this.getImpressionUrl(e),c=s?`<div class="ax-ad-card ax-ad-card-${i}" data-ad-id="${e.original.id}">`:"",d=(f=n.image)!=null&&f.url?`<img
|
|
2
2
|
src="${n.image.url}"
|
|
3
3
|
alt="${n.title}"
|
|
4
4
|
class="ax-ad-image"
|
|
5
5
|
loading="lazy"
|
|
6
|
-
/>`:"",
|
|
7
|
-
${
|
|
6
|
+
/>`:"",l=(n.brand||"").trim(),u=l&&l.toLowerCase()!=="unknown"?`<span class="ax-ad-brand">${n.brand}</span>`:"",h=`
|
|
7
|
+
${d}
|
|
8
8
|
<div class="ax-ad-content">
|
|
9
9
|
${u}
|
|
10
10
|
<h3 class="ax-ad-title">${n.title}</h3>
|
|
11
11
|
${n.body?`<p class="ax-ad-body">${n.body}</p>`:""}
|
|
12
12
|
<div class="ax-ad-footer">
|
|
13
13
|
<a
|
|
14
|
-
href="${
|
|
14
|
+
href="${r}"
|
|
15
15
|
class="ax-ad-cta"
|
|
16
16
|
target="_blank"
|
|
17
17
|
rel="sponsored noopener noreferrer"
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
</div>
|
|
76
76
|
</div>
|
|
77
77
|
`}static renderAds(e,t=w.renderActionCard.bind(w)){return e.map(t).join(`
|
|
78
|
-
`)}}const
|
|
78
|
+
`)}}const N="ad_session_id";function D(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}function H(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function _(o){if(o)return o;if(typeof window<"u"){const e=window.__AD_CONFIG__;if(e!=null&&e.apiBaseUrl)return e.apiBaseUrl}return"/api/v1"}class U{constructor(e={}){this.memoryCache=new Map,this.sessionKey=e.sessionKey||N,this.storage=e.storage||"sessionStorage",(typeof window>"u"||typeof window.sessionStorage>"u")&&(this.storage="memory")}getSessionId(){if(this.storage==="sessionStorage"){let e=sessionStorage.getItem(this.sessionKey);return e||(e=this.generateSessionId(),sessionStorage.setItem(this.sessionKey,e)),e}else{let e=this.memoryCache.get(this.sessionKey);return e||(e=this.generateSessionId(),this.memoryCache.set(this.sessionKey,e)),e}}regenerateSessionId(){const e=this.generateSessionId();return this.storage==="sessionStorage"?sessionStorage.setItem(this.sessionKey,e):this.memoryCache.set(this.sessionKey,e),e}generateSessionId(){return`session_${Date.now()}_${Math.random().toString(36).substring(2,11)}`}clearSessionId(){this.storage==="sessionStorage"?sessionStorage.removeItem(this.sessionKey):this.memoryCache.delete(this.sessionKey)}}const F=new U,m=()=>F.getSessionId();class v{constructor(e={}){this.explicitBaseUrl=e.baseUrl,this.timeout=e.timeout||1e4,this.retryAttempts=e.retryAttempts??2,this.retryDelay=e.retryDelay||1e3,this.debug=e.debug||!1}async trackImpression(e){return this.sendRequest(`${_(this.explicitBaseUrl)}/ads/impression`,e,"impression")}async trackClick(e){return this.sendRequest(`${_(this.explicitBaseUrl)}/ads/click`,e,"click")}async sendRequest(e,t,i){let s=null;for(let n=0;n<=this.retryAttempts;n++){const r=this.debug||H();try{r&&(console.log(`[Analytics] Request URL (${i}):`,e),console.log(`[Analytics] Sending ${i} event (attempt ${n+1}):`,t));const a=new AbortController,c=setTimeout(()=>a.abort(),this.timeout),d={"Content-Type":"application/json"},l=D();l&&(d["X-API-Key"]=l,r&&console.log(`[Analytics] Adding X-API-Key header for ${i} event`));const u=await fetch(e,{method:"POST",headers:d,body:JSON.stringify(t),keepalive:!0,signal:a.signal});if(clearTimeout(c),!u.ok)throw new Error(`HTTP ${u.status}: ${u.statusText}`);const h=await u.json();return r&&console.log(`[Analytics] ${i} event tracked successfully:`,h),h}catch(a){s=a,r&&console.warn(`[Analytics] ${i} tracking failed (attempt ${n+1}):`,a),n<this.retryAttempts&&await this.delay(this.retryDelay*(n+1))}}return console.error(`[Analytics] ${i} tracking failed after ${this.retryAttempts+1} attempts:`,s),null}delay(e){return new Promise(t=>setTimeout(t,e))}}const O=new v,x=(o,e)=>e!=null&&e.baseUrl?new v({baseUrl:e.baseUrl}).trackImpression(o):O.trackImpression(o),I=(o,e)=>e!=null&&e.baseUrl?new v({baseUrl:e.baseUrl}).trackClick(o):O.trackClick(o);function G(o,e){return`vt_${o}_${e}`}async function j(o){return Promise.all(o.map(e=>x(e)))}async function z(o){return Promise.all(o.map(e=>I(e)))}function W(o){const e=new v(o);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function Q(o={}){const e=new U(o);return{getSessionId:()=>e.getSessionId(),regenerateSessionId:()=>e.regenerateSessionId(),clearSessionId:()=>e.clearSessionId()}}const M=class M{static isDebugEnabled(){var e;return typeof window<"u"?!!((e=window.__AD_CONFIG__)!=null&&e.debug):!1}static getClickUrl(e){return e.tracking.clickUrl||e.tracking.click_url||""}static getViewToken(e){return e.tracking.viewToken||e.tracking.view_token}static renderActionCard(e,t,i={}){t.innerHTML=w.renderActionCard(e,i);const s=t.querySelector(".ax-ad-cta");return s&&s.addEventListener("click",n=>{n.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSuffixAd(e,t,i={}){const s=i.variant||"block";t.innerHTML=this.generateSuffixAdHTML(e,s);const n=t.querySelector(".ax-ad-suffix-link");return n&&n.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSponsoredSource(e,t,i={}){const s=i.variant||"card";t.innerHTML=this.generateSponsoredSourceHTML(e,s);const n=t.querySelector(".ax-ad-source-link");return n&&n.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderLeadGenAd(e,t,i={}){t.innerHTML=w.renderLeadGenAd(e);const s=t.querySelector(".ax-ad-leadgen-cta");return s&&s.addEventListener("click",n=>{n.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderAds(e,t,i=this.renderActionCard.bind(this),s){t.innerHTML="";const n=document.createElement("div");return n.className="ax-ads-container",e.forEach(r=>{const a=document.createElement("div");a.className="ax-ad-wrapper",i.call(this,r,a,s),n.appendChild(a)}),t.appendChild(n),t}static handleClick(e,t){const{analytics:i,onClick:s}=t,n=this.getClickUrl(e);if(!n){console.warn("[DOMRenderer] Missing click url for ad:",e.original.id);return}s&&s(e),i&&I({requestId:i.requestId,adId:e.original.id,destinationUrl:n,slotId:i.slotId,sessionId:m(),adTitle:e.adapted.title,format:e.original.type,userId:i.userId},{baseUrl:i.apiBaseUrl}).catch(r=>{console.error("[DOMRenderer] Analytics click tracking failed:",r)}),this.isDebugEnabled()&&console.log("[DOMRenderer] Redirect URL:",n),window.open(n,"_blank","noopener,noreferrer")}static trackImpression(e,t,i){const{analytics:s,onImpression:n}=i,r=s?`${s.requestId}:${s.slotId}:${e.original.id}:${this.getViewToken(e)||""}`:`no-analytics:${e.original.id}:${this.getViewToken(e)||""}`;if(this.trackedImpressionKeys.has(r))return;let a=!1;const c=()=>{a||this.trackedImpressionKeys.has(r)||(a=!0,this.trackedImpressionKeys.add(r),d())},d=()=>{s?x({requestId:s.requestId,adId:e.original.id,slotId:s.slotId,position:s.position,totalAds:s.totalAds,sessionId:m(),adTitle:e.adapted.title,format:e.original.type,source:"internal",viewToken:this.getViewToken(e),userId:s.userId},{baseUrl:s.apiBaseUrl}).then(()=>{n&&n(e)}).catch(l=>{console.error("[DOMRenderer] Analytics impression tracking failed:",l)}):n&&n(e)};if(typeof IntersectionObserver<"u"){let l=!1,u=null;const h=new IntersectionObserver(f=>{f.forEach(k=>{l=k.isIntersecting&&k.intersectionRatio>=.5,l?u||(u=setTimeout(()=>{u=null,l&&(c(),h.disconnect())},1e3)):u&&(clearTimeout(u),u=null)})},{threshold:.5}),p=t.querySelector(`[data-ad-id="${e.original.id}"]`)||t;h.observe(p)}else c()}static generateSuffixAdHTML(e,t){const i=e.adapted.title||"",s=e.adapted.body||"";return t==="inline"?`
|
|
79
79
|
<span class="ax-ad-suffix-link ax-ad-variant-inline" data-ad-id="${e.original.id}">
|
|
80
80
|
${i}
|
|
81
81
|
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-left:4px">
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
</div>
|
|
100
100
|
${s?`<p class="ax-ad-suffix-body">${s}</p>`:""}
|
|
101
101
|
</div>
|
|
102
|
-
`}static generateSponsoredSourceHTML(e,t){var
|
|
102
|
+
`}static generateSponsoredSourceHTML(e,t){var r;const i=e.adapted.title||"",s=e.adapted.body||"",n=((r=e.adapted.image)==null?void 0:r.url)||"";return t==="list-item"?`
|
|
103
103
|
<div class="ax-ad-source ax-ad-variant-list-item" data-ad-id="${e.original.id}">
|
|
104
104
|
${n?`<img src="${n}" alt="${i}" loading="lazy" />`:""}
|
|
105
105
|
<div class="ax-ad-source-info">
|
|
@@ -132,4 +132,4 @@
|
|
|
132
132
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
|
133
133
|
</svg>
|
|
134
134
|
</div>
|
|
135
|
-
`}};M.trackedImpressionKeys=new Set;let y=M;class S{constructor(){this.cachedStaticInfo=null,this.cacheTimestamp=0,this.CACHE_TTL=36e5}inferAppInfo(){if(typeof window>"u")return{bundle:"unknown",ver:"1.0.0",name:"Unknown App",publisher:{id:"unknown"}};const e=this.inferBundle(),t=this.inferAppName(),i=this.inferAppVersion(),s=this.inferPublisherId();return{bundle:e,ver:i,name:t,publisher:{id:s,domain:window.location.hostname}}}inferBundle(){var t;if(typeof window>"u")return"unknown";const e=(t=window.location)==null?void 0:t.hostname;return e?e.replace(/^www\./,"").replace(/:\d+$/,""):"unknown"}inferAppName(){var s,n;if(typeof document>"u")return"Unknown App";const e=document.title;if(e&&e!=="Loading..."&&e.length<100)return e;const t=(s=document.querySelector('meta[property="og:title"]'))==null?void 0:s.getAttribute("content");if(t)return t;const i=(n=document.querySelector('meta[name="application-name"]'))==null?void 0:n.getAttribute("content");return i||"Unknown App"}inferAppVersion(){if(typeof document>"u")return"1.0.0";const e=['meta[name="version"]','meta[name="app-version"]','meta[name="application-version"]','link[rel="manifest"]'];for(const t of e){const i=document.querySelector(t);if(i){if(t.includes("manifest"))return"1.0.0";const s=i.getAttribute("content");if(s)return s}}return"1.0.0"}inferPublisherId(){var i,s;if(typeof document>"u")return"unknown";const e=(i=document.querySelector('meta[name="publisher-id"]'))==null?void 0:i.getAttribute("content");if(e)return e;const t=(s=window.location)==null?void 0:s.hostname;return t?t.replace(/\./g,"_"):"unknown"}static getInstance(){return this.instance||(this.instance=new S),this.instance}collect(e){const t=Date.now();(!this.cachedStaticInfo||t-this.cacheTimestamp>this.CACHE_TTL)&&(this.cachedStaticInfo=this.collectStaticInfo(),this.cacheTimestamp=t);const i=this.collectDynamicInfo();return{device:{ua:this.cachedStaticInfo.ua,os:this.cachedStaticInfo.os,osv:this.cachedStaticInfo.osv,devicetype:this.cachedStaticInfo.devicetype,model:this.cachedStaticInfo.model,make:this.cachedStaticInfo.make,language:this.cachedStaticInfo.language,connectiontype:i.connectiontype??0,bandwidth:i.bandwidth,...(e==null?void 0:e.device)||{}},app:{...this.inferAppInfo(),...(e==null?void 0:e.app)||{}},user:{...this.collectUserInfo(),...(e==null?void 0:e.user)||{}},geo:{...this.collectGeoInfo(),...(e==null?void 0:e.geo)||{}},timestamp:t}}collectStaticInfo(){if(typeof navigator>"u")return this.getFallbackInfo();const e=navigator.userAgent,t=this.detectOS(e),i=this.detectOSVersion(e);return{ua:e,os:t,osv:i,devicetype:this.detectDeviceType(e),model:this.detectModel(e),make:this.detectMake(e),language:navigator.language||"en-US"}}collectDynamicInfo(){if(typeof navigator>"u")return{connectiontype:0};const e=this.getConnectionInfo();return{connectiontype:(e==null?void 0:e.type)||0,bandwidth:e==null?void 0:e.downlink}}collectUserInfo(){let e;try{e=localStorage.getItem("actionx_user_id")||"",e||(e=this.generateUUID(),localStorage.setItem("actionx_user_id",e))}catch{e=this.generateUUID()}return{id:e,language:(navigator==null?void 0:navigator.language)||"en-US"}}collectGeoInfo(){if(typeof navigator>"u"||typeof Intl>"u")return{};const e=Intl.DateTimeFormat().resolvedOptions().timeZone,t=navigator.language;return{country:this.inferCountry(t,e),timezone:e}}detectOS(e){return e?/iPad|iPhone|iPod/.test(e)?"iOS":/Android/.test(e)?"Android":/Windows/.test(e)?"Windows":/Macintosh|Mac OS/.test(e)?"macOS":/Linux/.test(e)?"Linux":/CrOS/.test(e)?"Chrome OS":"Unknown":"Unknown"}detectOSVersion(e){if(!e)return"Unknown";const t=e.match(/OS (\d+)_(\d+)_?(\d+)?/);if(t)return`${t[1]}.${t[2]}${t[3]?"."+t[3]:""}`;const i=e.match(/Android (\d+)\.(\d+)/);if(i)return`${i[1]}.${i[2]}`;const s=e.match(/Windows NT (\d+\.\d+)/);if(s)return s[1];const n=e.match(/Mac OS X (\d+[._]\d+)/);return n?n[1].replace("_","."):"Unknown"}detectDeviceType(e){return e?/iPad|Tablet/i.test(e)||/Android/.test(e)&&!/Mobile/.test(e)?2:/iPhone|iPod|Mobile|Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(e)?1:/Windows|Macintosh|Linux|x86_64/i.test(e)?3:/SmartTV|TV|WebTV|GoogleTV|AppleTV/i.test(e)?4:0:0}detectModel(e){if(!e)return;const t=e.match(/iPhone(?:;\s*[^\)]*)?\s*(\d+,\d)?/);if(t)return`iPhone${t[1]||""}`;if(/iPad/.test(e))return"iPad";const i=e.match(/Samsung-.*(SM-\w+)/);if(i)return i[1];if(/Pixel/.test(e))return"Google Pixel";const s=e.match(/(BARC-|HUAWEI-)?([A-Z]{2}\-\w{4})/);if(s)return s[2];const n=e.match(/(MI|Redmi|POCO)\s([\w\s]+)/);if(n)return n[1]+" "+n[2];if(/OnePlus/.test(e)){const o=e.match(/OnePlus\s([A-Z\d]+)/);return o?"OnePlus "+o[1]:"OnePlus"}}detectMake(e){if(e){if(/iPad|iPhone|iPod/.test(e))return"Apple";if(/Samsung/.test(e))return"Samsung";if(/Pixel/.test(e))return"Google";if(/Huawei|HONOR/.test(e))return"Huawei";if(/Xiaomi|Redmi|POCO/.test(e))return"Xiaomi";if(/OnePlus/.test(e))return"OnePlus";if(/Sony/.test(e))return"Sony";if(/LG/.test(e))return"LG";if(/Motorola|Moto/.test(e))return"Motorola";if(/Nokia/.test(e))return"Nokia";if(/HTC/.test(e))return"HTC";if(/OPPO/.test(e))return"OPPO";if(/vivo/.test(e))return"vivo";if(/Realme/.test(e))return"Realme"}}getConnectionInfo(){if(typeof navigator>"u")return null;const e=navigator,t=e.connection||e.mozConnection||e.webkitConnection;return t?{type:this.mapConnectionType(t.effectiveType),downlink:t.downlink,rtt:t.rtt}:null}mapConnectionType(e){if(!e)return 0;switch(e.toLowerCase()){case"slow-2g":case"2g":return 3;case"3g":return 4;case"4g":return 5;default:return 0}}inferCountry(e,t){if(!e&&!t)return;const i={"Asia/Shanghai":"CN","Asia/Hong_Kong":"HK","Asia/Taipei":"TW","Asia/Tokyo":"JP","Asia/Seoul":"KR","Asia/Singapore":"SG","Asia/Dubai":"AE","Asia/Kolkata":"IN","Asia/Jakarta":"ID","Asia/Manila":"PH","Asia/Bangkok":"TH","Asia/Kuala_Lumpur":"MY","America/New_York":"US","America/Chicago":"US","America/Denver":"US","America/Los_Angeles":"US","America/Toronto":"CA","America/Vancouver":"CA","America/Mexico_City":"MX","America/Sao_Paulo":"BR","America/Buenos_Aires":"AR","Europe/London":"GB","Europe/Paris":"FR","Europe/Berlin":"DE","Europe/Madrid":"ES","Europe/Rome":"IT","Europe/Amsterdam":"NL","Europe/Brussels":"BE","Europe/Zurich":"CH","Europe/Vienna":"AT","Europe/Stockholm":"SE","Europe/Oslo":"NO","Europe/Copenhagen":"DK","Europe/Helsinki":"FI","Europe/Dublin":"IE","Europe/Lisbon":"PT","Europe/Athens":"GR","Europe/Prague":"CZ","Europe/Warsaw":"PL","Europe/Budapest":"HU","Europe/Bucharest":"RO","Australia/Sydney":"AU","Pacific/Auckland":"NZ"};if(t&&i[t])return i[t];if(e){const s=e.split("-")[1];if(s)return s.toUpperCase()}}generateUUID(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}getFallbackInfo(){return{ua:"Unknown",os:"Unknown",osv:"Unknown",devicetype:0,language:"en-US"}}clearCache(){this.cachedStaticInfo=null,this.cacheTimestamp=0}}const T=()=>S.getInstance();function b(r){return T().collect(r)}function Q(){return b().device}function B(){return b().user}function J(){return b().app}function X(){return b().geo}function Z(){return B().id}function Y(){T().clearCache()}function ee(r){const e=b(r);return JSON.stringify(e,null,2)}function te(){var t;const r=b();return[r.device.os,r.device.osv,r.app.name,r.user.id.slice(0,8)+"...",((t=r.geo)==null?void 0:t.country)||"Unknown"].join(" / ")}function V(){var r;return typeof window<"u"?!!((r=window.__AD_CONFIG__)!=null&&r.debug):!1}function ie(r){var e,t;return{id:r.original.id,type:r.original.type,score:r.original.score,source:"internal",content:{title:r.adapted.title,body:r.adapted.body,image:(e=r.adapted.image)==null?void 0:e.url,cta_text:r.adapted.ctaText,price:(t=r.adapted.price)==null?void 0:t.display,rating:r.adapted.rating,brand:r.adapted.brand},tracking:{click_url:r.tracking.clickUrl||r.tracking.click_url||"",impression_url:r.tracking.impressionUrl||r.tracking.impression_url||""},metadata:{viewToken:r.tracking.viewToken||r.tracking.view_token,styling:r.adapted.styling}}}async function q(r,e={}){const t=e.apiBaseUrl||"/api/v1",s=T().collect(),n={...r,clientInfo:s},o={"Content-Type":"application/json"};e.apiKey&&(o["X-API-Key"]=e.apiKey),V()&&(console.log("[fetchAds] Request URL:",`${t}/ads/request`),console.log("[fetchAds] Request params:",r),console.log("[fetchAds] Request body:",n));const a=await fetch(`${t}/ads/request`,{method:"POST",headers:o,body:JSON.stringify(n)});if(!a.ok)throw new Error(`Ad request failed: ${a.status} ${a.statusText}`);const c=await a.json();if(V()&&console.log("[fetchAds] Response:",c),!c.success)throw new Error(c.error||"Ad request failed");return c.data}const se={width:400,height:200},C={variant:"horizontal",count:1,preferences:{}};function E(r={}){const e={variant:r.variant??C.variant,count:r.count??C.count,preferences:{...C.preferences,...r.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:se,count:e.count,preferences:e.preferences,placement:{position:"below_fold",context:"post_response"}}]}const g=class g{constructor(e){this.slots={},this.isLoading=!1,this.currentRequestId=null,this.adsAnalyticsMap=new Map,this.config=e,this.enabled=e.enabled!==!1,this.eventBus=new R,this.slots_config=E(e.cardOption),typeof window<"u"&&(window.__AD_CONFIG__={...window.__AD_CONFIG__||{},apiKey:e.apiKey,apiBaseUrl:e.apiBaseUrl,debug:!!e.debug}),this.config.debug&&console.log("[AdManager] Initialized with config:",{apiBaseUrl:e.apiBaseUrl,hasApiKey:!!e.apiKey,enabled:this.enabled})}async requestAds(e){var o;const t="conversationContext"in e?e:{conversationContext:e};this.currentUserId=(o=t.userContext)==null?void 0:o.userId;const i=this.buildRequestKey(t),s=g.inFlightRequests.get(i);if(s)return this.config.debug&&console.log("[AdManager] Reusing in-flight request for identical context"),s;if(!this.enabled)throw this.config.debug&&console.warn("[AdManager] Ads are disabled, skipping request"),new Error("Ads are disabled");if(this.isLoading)throw this.config.debug&&console.warn("[AdManager] Request already in progress"),new Error("Request already in progress");this.isLoading=!0,this.eventBus.emit("adsLoading"),this.config.debug&&console.log("[AdManager] Starting ad request...");const n=(async()=>{try{const a=await q({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,sessionId:t.userContext.sessionId??m()}:{sessionId:m()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=a.requestId,this.adsAnalyticsMap.clear(),a.slots.forEach(l=>{l.ads.forEach((d,u)=>{const h=d.original.id;this.adsAnalyticsMap.set(h,{requestId:a.requestId,slotId:l.slotId,position:u,totalAds:l.ads.length,apiBaseUrl:this.config.apiBaseUrl,userId:this.currentUserId})})});const c={};return a.slots.forEach(l=>{c[l.slotId]=l}),this.slots=c,this.eventBus.emit("adsUpdated",this.slots),this.config.debug&&console.log("[AdManager] Ads received:",{slotCount:Object.keys(this.slots).length,slots:Object.keys(this.slots)}),a}catch(a){const c=a;throw this.eventBus.emit("adsError",c),c}finally{this.isLoading=!1,g.inFlightRequests.delete(i)}})();return g.inFlightRequests.set(i,n),n}buildRequestKey(e){return JSON.stringify({apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey,conversationContext:e.conversationContext,userContext:e.userContext||null,slots:this.slots_config})}render(e,t,i={}){const s=typeof e!="string",n=s?g.DEFAULT_SLOT_ID:e,o=s?e:t,a=(s?t:i)||{};if(!o)return this.config.debug&&console.warn("[AdManager] Render container is required"),null;const c=this.slots[n];if(!c||!c.ads||c.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",n),null;const l=a.adIndex??0,d=c.ads[l];if(!d)return this.config.debug&&console.warn(`[AdManager] Ad at index ${l} not found in slot:`,n),null;const u=this.adsAnalyticsMap.get(d.original.id),h=this.slots_config.find(k=>k.slotId===n),p=(h==null?void 0:h.format)||"action_card";o.innerHTML="";const f={analytics:u,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return p==="suffix"?y.renderSuffixAd(d,o,f):p==="source"?y.renderSponsoredSource(d,o,f):p==="lead_gen"?y.renderLeadGenAd(d,o,f):y.renderActionCard(d,o,f)}getSlots(e){return e?this.slots[e]:this.slots}hasAds(e){var i;const t=this.slots[e];return(t==null?void 0:t.status)==="filled"&&((i=t.ads)==null?void 0:i.length)>0}getAds(e){var t;return((t=this.slots[e])==null?void 0:t.ads)||[]}getLoadingStatus(){return this.isLoading}setEnabled(e){this.enabled=e,this.config.debug&&console.log("[AdManager] Ads",e?"enabled":"disabled")}updateConfig(e){this.config={...this.config,...e},e.cardOption!==void 0&&(this.slots_config=E(this.config.cardOption)),this.config.debug&&console.log("[AdManager] Config updated:",e)}clearSlots(){this.slots={},this.currentRequestId=null,this.adsAnalyticsMap.clear(),this.config.debug&&console.log("[AdManager] Slots and analytics cleared")}on(e,t){this.eventBus.on(e,t)}off(e,t){this.eventBus.off(e,t)}removeAllListeners(e){this.eventBus.removeAllListeners(e)}getCurrentRequestId(){return this.currentRequestId}getAdAnalytics(e){return this.adsAnalyticsMap.get(e)||null}getAdsAnalytics(e){const t=new Map;return e.forEach(i=>{const s=this.adsAnalyticsMap.get(i);s&&t.set(i,s)}),t}async trackAdClick(e,t,i){const s=this.adsAnalyticsMap.get(e);if(!s||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const n=m(),o=await I({requestId:this.currentRequestId,adId:e,destinationUrl:t,sessionId:n,slotId:s.slotId,adTitle:i==null?void 0:i.title,format:i==null?void 0:i.format,source:i==null?void 0:i.source,userId:s.userId??this.currentUserId},{baseUrl:s.apiBaseUrl});return o&&(this.eventBus.emit("adClicked",e,s.slotId),this.config.debug&&console.log("[AdManager] Click tracked via Analytics API:",o.eventId)),o}async trackAdImpressionAPI(e,t){const i=this.adsAnalyticsMap.get(e);if(!i||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const s=m(),n=await A({requestId:this.currentRequestId,adId:e,slotId:i.slotId,position:i.position,totalAds:i.totalAds,sessionId:s,adTitle:t==null?void 0:t.title,format:t==null?void 0:t.format,source:t==null?void 0:t.source,viewToken:t==null?void 0:t.viewToken,userId:i.userId??this.currentUserId},{baseUrl:i.apiBaseUrl});return n&&(this.eventBus.emit("adImpression",e,i.slotId),this.config.debug&&console.log("[AdManager] Impression tracked via Analytics API:",n.eventId)),n}destroy(){this.removeAllListeners(),this.clearSlots(),this.config.debug&&console.log("[AdManager] Destroyed")}};g.DEFAULT_SLOT_ID="action_card",g.inFlightRequests=new Map;let $=g;function P(){if(typeof window<"u"){const r=window.__AD_CONFIG__;if(r!=null&&r.apiKey)return r.apiKey}}class L{constructor(e={},t="/api/v1"){var i,s;this.observers=new Map,this.metrics=new Map,this.timers=new Map,this.batchTimers=new Map,this.eventQueue=new Map,this.isTracking=new Map,this.config={minVisiblePercentage:e.minVisiblePercentage??50,minViewableDuration:e.minViewableDuration??1e3,maxTrackingDuration:e.maxTrackingDuration??6e4,batchConfig:{maxBatchSize:((i=e.batchConfig)==null?void 0:i.maxBatchSize)??5,maxBatchWaitMs:((s=e.batchConfig)==null?void 0:s.maxBatchWaitMs)??1e4},debug:e.debug??!1},this.baseUrl=t}startTracking(e,t,i,s,n){if(this.isTracking.get(e)){this.log(`[ViewabilityTracker] Already tracking ${e}, skipping`);return}this.log(`[ViewabilityTracker] Starting tracking for ${e}`);const o={visiblePercentage:0,maxVisiblePercentage:0,totalVisibleTimeMs:0,currentVisibleTimeMs:0,isViewable:!1,viewableAt:null,enteredViewportAt:null,enterCount:0};this.metrics.set(e,o),this.isTracking.set(e,!0),this.eventQueue.set(e,[]);const a=new IntersectionObserver(l=>this.handleIntersection(e,l[0],i,s,n),{threshold:this.createThresholds()});a.observe(t),this.observers.set(e,a),this.startMonitoring(e,i,s,n);const c=setTimeout(()=>{this.endTracking(e,i,s,n)},this.config.maxTrackingDuration);this.timers.set(e,c)}stopTracking(e){this.log(`[ViewabilityTracker] Manual stop tracking for ${e}`),this.cleanup(e)}getMetrics(e){return this.metrics.get(e)}handleIntersection(e,t,i,s,n){const o=this.metrics.get(e);if(!o||!this.isTracking.get(e))return;const a=o.isViewable,c=Math.round(t.intersectionRatio*100);o.visiblePercentage=c,o.maxVisiblePercentage=Math.max(o.maxVisiblePercentage,c);const l=Date.now(),d=c>=this.config.minVisiblePercentage;if(d&&!o.enteredViewportAt&&(o.enteredViewportAt=l,o.enterCount++,this.log(`[ViewabilityTracker] ${e} entered viewport at ${l}`),this.queueEvent(e,{adId:e,sessionId:i,requestId:s,viewToken:n,eventType:"enter_viewport",visiblePercentage:c,maxVisiblePercentage:o.maxVisiblePercentage,totalVisibleTimeMs:o.totalVisibleTimeMs,isViewable:!1,timestamp:l})),!d&&o.enteredViewportAt){const u=l-o.enteredViewportAt;o.totalVisibleTimeMs+=u,o.enteredViewportAt=null,o.currentVisibleTimeMs=0,this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${o.totalVisibleTimeMs}ms`),this.queueEvent(e,{adId:e,sessionId:i,requestId:s,viewToken:n,eventType:"exit_viewport",visiblePercentage:c,maxVisiblePercentage:o.maxVisiblePercentage,totalVisibleTimeMs:o.totalVisibleTimeMs,isViewable:o.isViewable,timestamp:l})}o.isViewable!==a&&o.isViewable&&this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`),this.metrics.set(e,o)}startMonitoring(e,t,i,s){const o=setInterval(()=>{const a=this.metrics.get(e);if(!a||!this.isTracking.get(e)){clearInterval(o);return}const c=Date.now(),l=a.isViewable;if(a.enteredViewportAt){const d=c-a.enteredViewportAt;a.currentVisibleTimeMs=d,d>=this.config.minViewableDuration&&a.visiblePercentage>=this.config.minVisiblePercentage&&(a.isViewable=!0,l||(a.viewableAt=a.enteredViewportAt,this.log(`[ViewabilityTracker] ${e} BECAME VIEWABLE at ${a.viewableAt}`),this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:s,eventType:"become_viewable",visiblePercentage:a.visiblePercentage,maxVisiblePercentage:a.maxVisiblePercentage,totalVisibleTimeMs:a.totalVisibleTimeMs,isViewable:!0,timestamp:c})))}a.enteredViewportAt&&(a.totalVisibleTimeMs+=100),this.metrics.set(e,a)},100);this.timers.set(`${e}_monitoring`,o)}queueEvent(e,t){var n,o;const i=this.eventQueue.get(e);if(!i){this.log(`[ViewabilityTracker] No queue found for ${e}, skipping event`);return}i.push(t),this.log(`[ViewabilityTracker] Queued ${t.eventType} for ${e} (queue size: ${i.length})`);const s=((n=this.config.batchConfig)==null?void 0:n.maxBatchSize)??5;if(i.length>=s)this.flushQueue(e);else{const a=this.batchTimers.get(e);a&&clearTimeout(a);const c=((o=this.config.batchConfig)==null?void 0:o.maxBatchWaitMs)??1e4,l=setTimeout(()=>{this.flushQueue(e)},c);this.batchTimers.set(e,l)}}async flushQueue(e){const t=this.eventQueue.get(e);if(!t||t.length===0)return;const i=[...t];t.length=0,this.log(`[ViewabilityTracker] Flushing ${i.length} events for ${e}`);try{const s={"Content-Type":"application/json"},n=P();n&&(s["x-api-key"]=n),await fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:s,body:JSON.stringify({events:i})}),this.log(`[ViewabilityTracker] Successfully sent ${i.length} events for ${e}`)}catch(s){this.log(`[ViewabilityTracker] Failed to send events for ${e}:`,s),t.unshift(...i)}}endTracking(e,t,i,s){const n=this.metrics.get(e);if(!n)return;this.log(`[ViewabilityTracker] Ending tracking for ${e}`),this.flushQueue(e);const o=Date.now();this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:s,eventType:"end_tracking",visiblePercentage:n.visiblePercentage,maxVisiblePercentage:n.maxVisiblePercentage,totalVisibleTimeMs:n.totalVisibleTimeMs,isViewable:n.isViewable,timestamp:o}),this.flushQueue(e),this.cleanup(e)}cleanup(e){const t=this.observers.get(e);t&&(t.disconnect(),this.observers.delete(e));const i=this.timers.get(e);i&&(clearTimeout(i),this.timers.delete(e));const s=this.timers.get(`${e}_monitoring`);s&&(clearInterval(s),this.timers.delete(`${e}_monitoring`));const n=this.batchTimers.get(e);n&&(clearTimeout(n),this.batchTimers.delete(e)),this.flushQueue(e),this.isTracking.delete(e),this.metrics.delete(e),this.eventQueue.delete(e)}createThresholds(){return[0,.25,.5,.75,.9,1]}stopAll(){const e=Array.from(this.isTracking.keys());for(const t of e)this.cleanup(t)}log(...e){this.config.debug&&console.log(...e)}setupBeforeUnload(){typeof window<"u"&&window.addEventListener("beforeunload",()=>{for(const e of this.eventQueue.keys()){const t=this.eventQueue.get(e);if(t&&t.length>0){const i={"Content-Type":"application/json"},s=P();s&&(i["x-api-key"]=s),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let x=null;function ne(r,e){return x||(x=new L(r,e),x.setupBeforeUnload()),x}const re="0.1.0";exports.AdManager=$;exports.AnalyticsSender=v;exports.ClientInfoCollector=S;exports.DOMRenderer=y;exports.HTMLRenderer=w;exports.SDK_VERSION=re;exports.SessionManager=U;exports.ViewabilityTracker=L;exports.adaptAdToKoahAd=ie;exports.clearClientInfoCache=Y;exports.createAnalytics=z;exports.createSession=W;exports.fetchAds=q;exports.generateViewToken=F;exports.getAppInfo=J;exports.getClientInfo=b;exports.getClientInfoCollector=T;exports.getClientInfoJSON=ee;exports.getClientInfoSummary=te;exports.getDeviceInfo=Q;exports.getGeoInfo=X;exports.getSessionId=m;exports.getUserId=Z;exports.getUserInfo=B;exports.getViewabilityTracker=ne;exports.trackAdClick=I;exports.trackAdImpression=A;exports.trackClicksBatch=j;exports.trackImpressionsBatch=G;
|
|
135
|
+
`}};M.trackedImpressionKeys=new Set;let y=M;class S{constructor(){this.cachedStaticInfo=null,this.cacheTimestamp=0,this.CACHE_TTL=36e5}inferAppInfo(){if(typeof window>"u")return{bundle:"unknown",ver:"1.0.0",name:"Unknown App",publisher:{id:"unknown"}};const e=this.inferBundle(),t=this.inferAppName(),i=this.inferAppVersion(),s=this.inferPublisherId();return{bundle:e,ver:i,name:t,publisher:{id:s,domain:window.location.hostname}}}inferBundle(){var t;if(typeof window>"u")return"unknown";const e=(t=window.location)==null?void 0:t.hostname;return e?e.replace(/^www\./,"").replace(/:\d+$/,""):"unknown"}inferAppName(){var s,n;if(typeof document>"u")return"Unknown App";const e=document.title;if(e&&e!=="Loading..."&&e.length<100)return e;const t=(s=document.querySelector('meta[property="og:title"]'))==null?void 0:s.getAttribute("content");if(t)return t;const i=(n=document.querySelector('meta[name="application-name"]'))==null?void 0:n.getAttribute("content");return i||"Unknown App"}inferAppVersion(){if(typeof document>"u")return"1.0.0";const e=['meta[name="version"]','meta[name="app-version"]','meta[name="application-version"]','link[rel="manifest"]'];for(const t of e){const i=document.querySelector(t);if(i){if(t.includes("manifest"))return"1.0.0";const s=i.getAttribute("content");if(s)return s}}return"1.0.0"}inferPublisherId(){var i,s;if(typeof document>"u")return"unknown";const e=(i=document.querySelector('meta[name="publisher-id"]'))==null?void 0:i.getAttribute("content");if(e)return e;const t=(s=window.location)==null?void 0:s.hostname;return t?t.replace(/\./g,"_"):"unknown"}static getInstance(){return this.instance||(this.instance=new S),this.instance}collect(e){const t=Date.now();(!this.cachedStaticInfo||t-this.cacheTimestamp>this.CACHE_TTL)&&(this.cachedStaticInfo=this.collectStaticInfo(),this.cacheTimestamp=t);const i=this.collectDynamicInfo();return{device:{ua:this.cachedStaticInfo.ua,os:this.cachedStaticInfo.os,osv:this.cachedStaticInfo.osv,devicetype:this.cachedStaticInfo.devicetype,model:this.cachedStaticInfo.model,make:this.cachedStaticInfo.make,language:this.cachedStaticInfo.language,connectiontype:i.connectiontype??0,bandwidth:i.bandwidth,...(e==null?void 0:e.device)||{}},app:{...this.inferAppInfo(),...(e==null?void 0:e.app)||{}},user:{...this.collectUserInfo(),...(e==null?void 0:e.user)||{}},geo:{...this.collectGeoInfo(),...(e==null?void 0:e.geo)||{}},timestamp:t}}collectStaticInfo(){if(typeof navigator>"u")return this.getFallbackInfo();const e=navigator.userAgent,t=this.detectOS(e),i=this.detectOSVersion(e);return{ua:e,os:t,osv:i,devicetype:this.detectDeviceType(e),model:this.detectModel(e),make:this.detectMake(e),language:navigator.language||"en-US"}}collectDynamicInfo(){if(typeof navigator>"u")return{connectiontype:0};const e=this.getConnectionInfo();return{connectiontype:(e==null?void 0:e.type)||0,bandwidth:e==null?void 0:e.downlink}}collectUserInfo(){let e;try{e=localStorage.getItem("actionx_user_id")||"",e||(e=this.generateUUID(),localStorage.setItem("actionx_user_id",e))}catch{e=this.generateUUID()}return{id:e,language:(navigator==null?void 0:navigator.language)||"en-US"}}collectGeoInfo(){if(typeof navigator>"u"||typeof Intl>"u")return{};const e=Intl.DateTimeFormat().resolvedOptions().timeZone,t=navigator.language;return{country:this.inferCountry(t,e),timezone:e}}detectOS(e){return e?/iPad|iPhone|iPod/.test(e)?"iOS":/Android/.test(e)?"Android":/Windows/.test(e)?"Windows":/Macintosh|Mac OS/.test(e)?"macOS":/Linux/.test(e)?"Linux":/CrOS/.test(e)?"Chrome OS":"Unknown":"Unknown"}detectOSVersion(e){if(!e)return"Unknown";const t=e.match(/OS (\d+)_(\d+)_?(\d+)?/);if(t)return`${t[1]}.${t[2]}${t[3]?"."+t[3]:""}`;const i=e.match(/Android (\d+)\.(\d+)/);if(i)return`${i[1]}.${i[2]}`;const s=e.match(/Windows NT (\d+\.\d+)/);if(s)return s[1];const n=e.match(/Mac OS X (\d+[._]\d+)/);return n?n[1].replace("_","."):"Unknown"}detectDeviceType(e){return e?/iPad|Tablet/i.test(e)||/Android/.test(e)&&!/Mobile/.test(e)?2:/iPhone|iPod|Mobile|Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(e)?1:/Windows|Macintosh|Linux|x86_64/i.test(e)?3:/SmartTV|TV|WebTV|GoogleTV|AppleTV/i.test(e)?4:0:0}detectModel(e){if(!e)return;const t=e.match(/iPhone(?:;\s*[^\)]*)?\s*(\d+,\d)?/);if(t)return`iPhone${t[1]||""}`;if(/iPad/.test(e))return"iPad";const i=e.match(/Samsung-.*(SM-\w+)/);if(i)return i[1];if(/Pixel/.test(e))return"Google Pixel";const s=e.match(/(BARC-|HUAWEI-)?([A-Z]{2}\-\w{4})/);if(s)return s[2];const n=e.match(/(MI|Redmi|POCO)\s([\w\s]+)/);if(n)return n[1]+" "+n[2];if(/OnePlus/.test(e)){const r=e.match(/OnePlus\s([A-Z\d]+)/);return r?"OnePlus "+r[1]:"OnePlus"}}detectMake(e){if(e){if(/iPad|iPhone|iPod/.test(e))return"Apple";if(/Samsung/.test(e))return"Samsung";if(/Pixel/.test(e))return"Google";if(/Huawei|HONOR/.test(e))return"Huawei";if(/Xiaomi|Redmi|POCO/.test(e))return"Xiaomi";if(/OnePlus/.test(e))return"OnePlus";if(/Sony/.test(e))return"Sony";if(/LG/.test(e))return"LG";if(/Motorola|Moto/.test(e))return"Motorola";if(/Nokia/.test(e))return"Nokia";if(/HTC/.test(e))return"HTC";if(/OPPO/.test(e))return"OPPO";if(/vivo/.test(e))return"vivo";if(/Realme/.test(e))return"Realme"}}getConnectionInfo(){if(typeof navigator>"u")return null;const e=navigator,t=e.connection||e.mozConnection||e.webkitConnection;return t?{type:this.mapConnectionType(t.effectiveType),downlink:t.downlink,rtt:t.rtt}:null}mapConnectionType(e){if(!e)return 0;switch(e.toLowerCase()){case"slow-2g":case"2g":return 3;case"3g":return 4;case"4g":return 5;default:return 0}}inferCountry(e,t){if(!e&&!t)return;const i={"Asia/Shanghai":"CN","Asia/Hong_Kong":"HK","Asia/Taipei":"TW","Asia/Tokyo":"JP","Asia/Seoul":"KR","Asia/Singapore":"SG","Asia/Dubai":"AE","Asia/Kolkata":"IN","Asia/Jakarta":"ID","Asia/Manila":"PH","Asia/Bangkok":"TH","Asia/Kuala_Lumpur":"MY","America/New_York":"US","America/Chicago":"US","America/Denver":"US","America/Los_Angeles":"US","America/Toronto":"CA","America/Vancouver":"CA","America/Mexico_City":"MX","America/Sao_Paulo":"BR","America/Buenos_Aires":"AR","Europe/London":"GB","Europe/Paris":"FR","Europe/Berlin":"DE","Europe/Madrid":"ES","Europe/Rome":"IT","Europe/Amsterdam":"NL","Europe/Brussels":"BE","Europe/Zurich":"CH","Europe/Vienna":"AT","Europe/Stockholm":"SE","Europe/Oslo":"NO","Europe/Copenhagen":"DK","Europe/Helsinki":"FI","Europe/Dublin":"IE","Europe/Lisbon":"PT","Europe/Athens":"GR","Europe/Prague":"CZ","Europe/Warsaw":"PL","Europe/Budapest":"HU","Europe/Bucharest":"RO","Australia/Sydney":"AU","Pacific/Auckland":"NZ"};if(t&&i[t])return i[t];if(e){const s=e.split("-")[1];if(s)return s.toUpperCase()}}generateUUID(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}getFallbackInfo(){return{ua:"Unknown",os:"Unknown",osv:"Unknown",devicetype:0,language:"en-US"}}clearCache(){this.cachedStaticInfo=null,this.cacheTimestamp=0}}const T=()=>S.getInstance();function b(o){return T().collect(o)}function J(){return b().device}function B(){return b().user}function X(){return b().app}function Z(){return b().geo}function q(){return B().id}function Y(){T().clearCache()}function ee(o){const e=b(o);return JSON.stringify(e,null,2)}function te(){var t;const o=b();return[o.device.os,o.device.osv,o.app.name,o.user.id.slice(0,8)+"...",((t=o.geo)==null?void 0:t.country)||"Unknown"].join(" / ")}function V(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function ie(o){var e,t;return{id:o.original.id,type:o.original.type,score:o.original.score,source:"internal",content:{title:o.adapted.title,body:o.adapted.body,image:(e=o.adapted.image)==null?void 0:e.url,cta_text:o.adapted.ctaText,price:(t=o.adapted.price)==null?void 0:t.display,rating:o.adapted.rating,brand:o.adapted.brand},tracking:{click_url:o.tracking.clickUrl||o.tracking.click_url||"",impression_url:o.tracking.impressionUrl||o.tracking.impression_url||""},metadata:{viewToken:o.tracking.viewToken||o.tracking.view_token,styling:o.adapted.styling}}}async function L(o,e={}){const t=e.apiBaseUrl||"/api/v1",s=T().collect(),n={...o,clientInfo:s},r={"Content-Type":"application/json"};e.apiKey&&(r["X-API-Key"]=e.apiKey),V()&&(console.log("[fetchAds] Request URL:",`${t}/ads/request`),console.log("[fetchAds] Request params:",o),console.log("[fetchAds] Request body:",n));const a=await fetch(`${t}/ads/request`,{method:"POST",headers:r,body:JSON.stringify(n)});if(!a.ok)throw new Error(`Ad request failed: ${a.status} ${a.statusText}`);const c=await a.json();if(V()&&console.log("[fetchAds] Response:",c),!c.success)throw new Error(c.error||"Ad request failed");return c.data}const se={width:400,height:200},C={variant:"horizontal",count:1,preferences:{}};function E(o={}){const e={variant:o.variant??C.variant,count:o.count??C.count,preferences:{...C.preferences,...o.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:se,count:e.count,preferences:e.preferences,placement:{position:"below_fold",context:"post_response"}}]}const g=class g{constructor(e){this.slots={},this.isLoading=!1,this.currentRequestId=null,this.adsAnalyticsMap=new Map,this.config=e,this.enabled=e.enabled!==!1,this.eventBus=new K,this.slots_config=E(e.cardOption),typeof window<"u"&&(window.__AD_CONFIG__={...window.__AD_CONFIG__||{},apiKey:e.apiKey,apiBaseUrl:e.apiBaseUrl,debug:!!e.debug}),this.config.debug&&console.log("[AdManager] Initialized with config:",{apiBaseUrl:e.apiBaseUrl,hasApiKey:!!e.apiKey,enabled:this.enabled})}async requestAds(e){const t="conversationContext"in e?e:{conversationContext:e};this.currentUserId=this.resolveUserId(t.userContext);const i=this.buildRequestKey(t),s=g.inFlightRequests.get(i);if(s)return this.config.debug&&console.log("[AdManager] Reusing in-flight request for identical context"),s;if(!this.enabled)throw this.config.debug&&console.warn("[AdManager] Ads are disabled, skipping request"),new Error("Ads are disabled");if(this.isLoading)throw this.config.debug&&console.warn("[AdManager] Request already in progress"),new Error("Request already in progress");this.isLoading=!0,this.eventBus.emit("adsLoading"),this.config.debug&&console.log("[AdManager] Starting ad request...");const n=(async()=>{try{const r=await L({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,userId:t.userContext.userId??this.currentUserId,sessionId:t.userContext.sessionId??m()}:{userId:this.currentUserId,sessionId:m()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=r.requestId,this.adsAnalyticsMap.clear(),r.slots.forEach(c=>{c.ads.forEach((d,l)=>{const u=d.original.id;this.adsAnalyticsMap.set(u,{requestId:r.requestId,slotId:c.slotId,position:l,totalAds:c.ads.length,apiBaseUrl:this.config.apiBaseUrl,userId:this.currentUserId})})});const a={};return r.slots.forEach(c=>{a[c.slotId]=c}),this.slots=a,this.eventBus.emit("adsUpdated",this.slots),this.config.debug&&console.log("[AdManager] Ads received:",{slotCount:Object.keys(this.slots).length,slots:Object.keys(this.slots)}),r}catch(r){const a=r;throw this.eventBus.emit("adsError",a),a}finally{this.isLoading=!1,g.inFlightRequests.delete(i)}})();return g.inFlightRequests.set(i,n),n}buildRequestKey(e){return JSON.stringify({apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey,conversationContext:e.conversationContext,userContext:e.userContext||null,slots:this.slots_config})}resolveUserId(e){if(e!=null&&e.userId)return e.userId;try{return q()}catch{return}}render(e,t,i={}){const s=typeof e!="string",n=s?g.DEFAULT_SLOT_ID:e,r=s?e:t,a=(s?t:i)||{};if(!r)return this.config.debug&&console.warn("[AdManager] Render container is required"),null;const c=this.slots[n];if(!c||!c.ads||c.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",n),null;const d=a.adIndex??0,l=c.ads[d];if(!l)return this.config.debug&&console.warn(`[AdManager] Ad at index ${d} not found in slot:`,n),null;const u=this.adsAnalyticsMap.get(l.original.id),h=this.slots_config.find(k=>k.slotId===n),p=(h==null?void 0:h.format)||"action_card";r.innerHTML="";const f={analytics:u,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return p==="suffix"?y.renderSuffixAd(l,r,f):p==="source"?y.renderSponsoredSource(l,r,f):p==="lead_gen"?y.renderLeadGenAd(l,r,f):y.renderActionCard(l,r,f)}getSlots(e){return e?this.slots[e]:this.slots}hasAds(e){var i;const t=this.slots[e];return(t==null?void 0:t.status)==="filled"&&((i=t.ads)==null?void 0:i.length)>0}getAds(e){var t;return((t=this.slots[e])==null?void 0:t.ads)||[]}getLoadingStatus(){return this.isLoading}setEnabled(e){this.enabled=e,this.config.debug&&console.log("[AdManager] Ads",e?"enabled":"disabled")}updateConfig(e){this.config={...this.config,...e},e.cardOption!==void 0&&(this.slots_config=E(this.config.cardOption)),this.config.debug&&console.log("[AdManager] Config updated:",e)}clearSlots(){this.slots={},this.currentRequestId=null,this.adsAnalyticsMap.clear(),this.config.debug&&console.log("[AdManager] Slots and analytics cleared")}on(e,t){this.eventBus.on(e,t)}off(e,t){this.eventBus.off(e,t)}removeAllListeners(e){this.eventBus.removeAllListeners(e)}getCurrentRequestId(){return this.currentRequestId}getAdAnalytics(e){return this.adsAnalyticsMap.get(e)||null}getAdsAnalytics(e){const t=new Map;return e.forEach(i=>{const s=this.adsAnalyticsMap.get(i);s&&t.set(i,s)}),t}async trackAdClick(e,t,i){const s=this.adsAnalyticsMap.get(e);if(!s||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const n=m(),r=await I({requestId:this.currentRequestId,adId:e,destinationUrl:t,sessionId:n,slotId:s.slotId,adTitle:i==null?void 0:i.title,format:i==null?void 0:i.format,source:i==null?void 0:i.source,userId:s.userId??this.currentUserId},{baseUrl:s.apiBaseUrl});return r&&(this.eventBus.emit("adClicked",e,s.slotId),this.config.debug&&console.log("[AdManager] Click tracked via Analytics API:",r.eventId)),r}async trackAdImpressionAPI(e,t){const i=this.adsAnalyticsMap.get(e);if(!i||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const s=m(),n=await x({requestId:this.currentRequestId,adId:e,slotId:i.slotId,position:i.position,totalAds:i.totalAds,sessionId:s,adTitle:t==null?void 0:t.title,format:t==null?void 0:t.format,source:t==null?void 0:t.source,viewToken:t==null?void 0:t.viewToken,userId:i.userId??this.currentUserId},{baseUrl:i.apiBaseUrl});return n&&(this.eventBus.emit("adImpression",e,i.slotId),this.config.debug&&console.log("[AdManager] Impression tracked via Analytics API:",n.eventId)),n}destroy(){this.removeAllListeners(),this.clearSlots(),this.config.debug&&console.log("[AdManager] Destroyed")}};g.DEFAULT_SLOT_ID="action_card",g.inFlightRequests=new Map;let $=g;function P(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}class R{constructor(e={},t="/api/v1"){var i,s;this.observers=new Map,this.metrics=new Map,this.timers=new Map,this.batchTimers=new Map,this.eventQueue=new Map,this.isTracking=new Map,this.config={minVisiblePercentage:e.minVisiblePercentage??50,minViewableDuration:e.minViewableDuration??1e3,maxTrackingDuration:e.maxTrackingDuration??6e4,batchConfig:{maxBatchSize:((i=e.batchConfig)==null?void 0:i.maxBatchSize)??5,maxBatchWaitMs:((s=e.batchConfig)==null?void 0:s.maxBatchWaitMs)??1e4},debug:e.debug??!1},this.baseUrl=t}startTracking(e,t,i,s,n){if(this.isTracking.get(e)){this.log(`[ViewabilityTracker] Already tracking ${e}, skipping`);return}this.log(`[ViewabilityTracker] Starting tracking for ${e}`);const r={visiblePercentage:0,maxVisiblePercentage:0,totalVisibleTimeMs:0,currentVisibleTimeMs:0,isViewable:!1,viewableAt:null,enteredViewportAt:null,enterCount:0};this.metrics.set(e,r),this.isTracking.set(e,!0),this.eventQueue.set(e,[]);const a=new IntersectionObserver(d=>this.handleIntersection(e,d[0],i,s,n),{threshold:this.createThresholds()});a.observe(t),this.observers.set(e,a),this.startMonitoring(e,i,s,n);const c=setTimeout(()=>{this.endTracking(e,i,s,n)},this.config.maxTrackingDuration);this.timers.set(e,c)}stopTracking(e){this.log(`[ViewabilityTracker] Manual stop tracking for ${e}`),this.cleanup(e)}getMetrics(e){return this.metrics.get(e)}handleIntersection(e,t,i,s,n){const r=this.metrics.get(e);if(!r||!this.isTracking.get(e))return;const a=r.isViewable,c=Math.round(t.intersectionRatio*100);r.visiblePercentage=c,r.maxVisiblePercentage=Math.max(r.maxVisiblePercentage,c);const d=Date.now(),l=c>=this.config.minVisiblePercentage;if(l&&!r.enteredViewportAt&&(r.enteredViewportAt=d,r.enterCount++,this.log(`[ViewabilityTracker] ${e} entered viewport at ${d}`),this.queueEvent(e,{adId:e,sessionId:i,requestId:s,viewToken:n,eventType:"enter_viewport",visiblePercentage:c,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:!1,timestamp:d})),!l&&r.enteredViewportAt){const u=d-r.enteredViewportAt;r.totalVisibleTimeMs+=u,r.enteredViewportAt=null,r.currentVisibleTimeMs=0,this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${r.totalVisibleTimeMs}ms`),this.queueEvent(e,{adId:e,sessionId:i,requestId:s,viewToken:n,eventType:"exit_viewport",visiblePercentage:c,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:r.isViewable,timestamp:d})}r.isViewable!==a&&r.isViewable&&this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`),this.metrics.set(e,r)}startMonitoring(e,t,i,s){const r=setInterval(()=>{const a=this.metrics.get(e);if(!a||!this.isTracking.get(e)){clearInterval(r);return}const c=Date.now(),d=a.isViewable;if(a.enteredViewportAt){const l=c-a.enteredViewportAt;a.currentVisibleTimeMs=l,l>=this.config.minViewableDuration&&a.visiblePercentage>=this.config.minVisiblePercentage&&(a.isViewable=!0,d||(a.viewableAt=a.enteredViewportAt,this.log(`[ViewabilityTracker] ${e} BECAME VIEWABLE at ${a.viewableAt}`),this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:s,eventType:"become_viewable",visiblePercentage:a.visiblePercentage,maxVisiblePercentage:a.maxVisiblePercentage,totalVisibleTimeMs:a.totalVisibleTimeMs,isViewable:!0,timestamp:c})))}a.enteredViewportAt&&(a.totalVisibleTimeMs+=100),this.metrics.set(e,a)},100);this.timers.set(`${e}_monitoring`,r)}queueEvent(e,t){var n,r;const i=this.eventQueue.get(e);if(!i){this.log(`[ViewabilityTracker] No queue found for ${e}, skipping event`);return}i.push(t),this.log(`[ViewabilityTracker] Queued ${t.eventType} for ${e} (queue size: ${i.length})`);const s=((n=this.config.batchConfig)==null?void 0:n.maxBatchSize)??5;if(i.length>=s)this.flushQueue(e);else{const a=this.batchTimers.get(e);a&&clearTimeout(a);const c=((r=this.config.batchConfig)==null?void 0:r.maxBatchWaitMs)??1e4,d=setTimeout(()=>{this.flushQueue(e)},c);this.batchTimers.set(e,d)}}async flushQueue(e){const t=this.eventQueue.get(e);if(!t||t.length===0)return;const i=[...t];t.length=0,this.log(`[ViewabilityTracker] Flushing ${i.length} events for ${e}`);try{const s={"Content-Type":"application/json"},n=P();n&&(s["x-api-key"]=n),await fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:s,body:JSON.stringify({events:i})}),this.log(`[ViewabilityTracker] Successfully sent ${i.length} events for ${e}`)}catch(s){this.log(`[ViewabilityTracker] Failed to send events for ${e}:`,s),t.unshift(...i)}}endTracking(e,t,i,s){const n=this.metrics.get(e);if(!n)return;this.log(`[ViewabilityTracker] Ending tracking for ${e}`),this.flushQueue(e);const r=Date.now();this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:s,eventType:"end_tracking",visiblePercentage:n.visiblePercentage,maxVisiblePercentage:n.maxVisiblePercentage,totalVisibleTimeMs:n.totalVisibleTimeMs,isViewable:n.isViewable,timestamp:r}),this.flushQueue(e),this.cleanup(e)}cleanup(e){const t=this.observers.get(e);t&&(t.disconnect(),this.observers.delete(e));const i=this.timers.get(e);i&&(clearTimeout(i),this.timers.delete(e));const s=this.timers.get(`${e}_monitoring`);s&&(clearInterval(s),this.timers.delete(`${e}_monitoring`));const n=this.batchTimers.get(e);n&&(clearTimeout(n),this.batchTimers.delete(e)),this.flushQueue(e),this.isTracking.delete(e),this.metrics.delete(e),this.eventQueue.delete(e)}createThresholds(){return[0,.25,.5,.75,.9,1]}stopAll(){const e=Array.from(this.isTracking.keys());for(const t of e)this.cleanup(t)}log(...e){this.config.debug&&console.log(...e)}setupBeforeUnload(){typeof window<"u"&&window.addEventListener("beforeunload",()=>{for(const e of this.eventQueue.keys()){const t=this.eventQueue.get(e);if(t&&t.length>0){const i={"Content-Type":"application/json"},s=P();s&&(i["x-api-key"]=s),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let A=null;function ne(o,e){return A||(A=new R(o,e),A.setupBeforeUnload()),A}const re="0.1.0";exports.AdManager=$;exports.AnalyticsSender=v;exports.ClientInfoCollector=S;exports.DOMRenderer=y;exports.HTMLRenderer=w;exports.SDK_VERSION=re;exports.SessionManager=U;exports.ViewabilityTracker=R;exports.adaptAdToKoahAd=ie;exports.clearClientInfoCache=Y;exports.createAnalytics=W;exports.createSession=Q;exports.fetchAds=L;exports.generateViewToken=G;exports.getAppInfo=X;exports.getClientInfo=b;exports.getClientInfoCollector=T;exports.getClientInfoJSON=ee;exports.getClientInfoSummary=te;exports.getDeviceInfo=J;exports.getGeoInfo=Z;exports.getSessionId=m;exports.getUserId=q;exports.getUserInfo=B;exports.getViewabilityTracker=ne;exports.trackAdClick=I;exports.trackAdImpression=x;exports.trackClicksBatch=z;exports.trackImpressionsBatch=j;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -61,20 +61,20 @@ class w {
|
|
|
61
61
|
*/
|
|
62
62
|
static renderActionCard(e, t = {}) {
|
|
63
63
|
var f;
|
|
64
|
-
const { variant: i = "horizontal", includeWrapper: s = !0 } = t, n = e.adapted,
|
|
64
|
+
const { variant: i = "horizontal", includeWrapper: s = !0 } = t, n = e.adapted, r = this.getClickUrl(e), a = this.getImpressionUrl(e), c = s ? `<div class="ax-ad-card ax-ad-card-${i}" data-ad-id="${e.original.id}">` : "", d = (f = n.image) != null && f.url ? `<img
|
|
65
65
|
src="${n.image.url}"
|
|
66
66
|
alt="${n.title}"
|
|
67
67
|
class="ax-ad-image"
|
|
68
68
|
loading="lazy"
|
|
69
|
-
/>` : "",
|
|
70
|
-
${
|
|
69
|
+
/>` : "", l = (n.brand || "").trim(), u = l && l.toLowerCase() !== "unknown" ? `<span class="ax-ad-brand">${n.brand}</span>` : "", h = `
|
|
70
|
+
${d}
|
|
71
71
|
<div class="ax-ad-content">
|
|
72
72
|
${u}
|
|
73
73
|
<h3 class="ax-ad-title">${n.title}</h3>
|
|
74
74
|
${n.body ? `<p class="ax-ad-body">${n.body}</p>` : ""}
|
|
75
75
|
<div class="ax-ad-footer">
|
|
76
76
|
<a
|
|
77
|
-
href="${
|
|
77
|
+
href="${r}"
|
|
78
78
|
class="ax-ad-cta"
|
|
79
79
|
target="_blank"
|
|
80
80
|
rel="sponsored noopener noreferrer"
|
|
@@ -186,17 +186,17 @@ class w {
|
|
|
186
186
|
const q = "ad_session_id";
|
|
187
187
|
function L() {
|
|
188
188
|
if (typeof window < "u") {
|
|
189
|
-
const
|
|
190
|
-
if (
|
|
191
|
-
return
|
|
189
|
+
const o = window.__AD_CONFIG__;
|
|
190
|
+
if (o != null && o.apiKey)
|
|
191
|
+
return o.apiKey;
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
function R() {
|
|
195
|
-
var
|
|
196
|
-
return typeof window < "u" ? !!((
|
|
195
|
+
var o;
|
|
196
|
+
return typeof window < "u" ? !!((o = window.__AD_CONFIG__) != null && o.debug) : !1;
|
|
197
197
|
}
|
|
198
|
-
function U(
|
|
199
|
-
if (
|
|
198
|
+
function U(o) {
|
|
199
|
+
if (o) return o;
|
|
200
200
|
if (typeof window < "u") {
|
|
201
201
|
const e = window.__AD_CONFIG__;
|
|
202
202
|
if (e != null && e.apiBaseUrl)
|
|
@@ -272,16 +272,16 @@ class x {
|
|
|
272
272
|
async sendRequest(e, t, i) {
|
|
273
273
|
let s = null;
|
|
274
274
|
for (let n = 0; n <= this.retryAttempts; n++) {
|
|
275
|
-
const
|
|
275
|
+
const r = this.debug || R();
|
|
276
276
|
try {
|
|
277
|
-
|
|
278
|
-
const a = new AbortController(), c = setTimeout(() => a.abort(), this.timeout),
|
|
277
|
+
r && (console.log(`[Analytics] Request URL (${i}):`, e), console.log(`[Analytics] Sending ${i} event (attempt ${n + 1}):`, t));
|
|
278
|
+
const a = new AbortController(), c = setTimeout(() => a.abort(), this.timeout), d = {
|
|
279
279
|
"Content-Type": "application/json"
|
|
280
|
-
},
|
|
281
|
-
|
|
280
|
+
}, l = L();
|
|
281
|
+
l && (d["X-API-Key"] = l, r && console.log(`[Analytics] Adding X-API-Key header for ${i} event`));
|
|
282
282
|
const u = await fetch(e, {
|
|
283
283
|
method: "POST",
|
|
284
|
-
headers:
|
|
284
|
+
headers: d,
|
|
285
285
|
body: JSON.stringify(t),
|
|
286
286
|
keepalive: !0,
|
|
287
287
|
signal: a.signal
|
|
@@ -289,9 +289,9 @@ class x {
|
|
|
289
289
|
if (clearTimeout(c), !u.ok)
|
|
290
290
|
throw new Error(`HTTP ${u.status}: ${u.statusText}`);
|
|
291
291
|
const h = await u.json();
|
|
292
|
-
return
|
|
292
|
+
return r && console.log(`[Analytics] ${i} event tracked successfully:`, h), h;
|
|
293
293
|
} catch (a) {
|
|
294
|
-
s = a,
|
|
294
|
+
s = a, r && console.warn(`[Analytics] ${i} tracking failed (attempt ${n + 1}):`, a), n < this.retryAttempts && await this.delay(this.retryDelay * (n + 1));
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
297
|
return console.error(`[Analytics] ${i} tracking failed after ${this.retryAttempts + 1} attempts:`, s), null;
|
|
@@ -303,25 +303,25 @@ class x {
|
|
|
303
303
|
return new Promise((t) => setTimeout(t, e));
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
|
-
const B = new x(), I = (
|
|
307
|
-
function
|
|
308
|
-
return `vt_${
|
|
306
|
+
const B = new x(), I = (o, e) => e != null && e.baseUrl ? new x({ baseUrl: e.baseUrl }).trackImpression(o) : B.trackImpression(o), T = (o, e) => e != null && e.baseUrl ? new x({ baseUrl: e.baseUrl }).trackClick(o) : B.trackClick(o);
|
|
307
|
+
function z(o, e) {
|
|
308
|
+
return `vt_${o}_${e}`;
|
|
309
309
|
}
|
|
310
|
-
async function
|
|
311
|
-
return Promise.all(
|
|
310
|
+
async function j(o) {
|
|
311
|
+
return Promise.all(o.map((e) => I(e)));
|
|
312
312
|
}
|
|
313
|
-
async function
|
|
314
|
-
return Promise.all(
|
|
313
|
+
async function W(o) {
|
|
314
|
+
return Promise.all(o.map((e) => T(e)));
|
|
315
315
|
}
|
|
316
|
-
function
|
|
317
|
-
const e = new x(
|
|
316
|
+
function Q(o) {
|
|
317
|
+
const e = new x(o);
|
|
318
318
|
return {
|
|
319
319
|
trackImpression: (t) => e.trackImpression(t),
|
|
320
320
|
trackClick: (t) => e.trackClick(t)
|
|
321
321
|
};
|
|
322
322
|
}
|
|
323
|
-
function
|
|
324
|
-
const e = new P(
|
|
323
|
+
function J(o = {}) {
|
|
324
|
+
const e = new P(o);
|
|
325
325
|
return {
|
|
326
326
|
getSessionId: () => e.getSessionId(),
|
|
327
327
|
regenerateSessionId: () => e.regenerateSessionId(),
|
|
@@ -356,8 +356,8 @@ const C = class C {
|
|
|
356
356
|
const s = i.variant || "block";
|
|
357
357
|
t.innerHTML = this.generateSuffixAdHTML(e, s);
|
|
358
358
|
const n = t.querySelector(".ax-ad-suffix-link");
|
|
359
|
-
return n && n.addEventListener("click", (
|
|
360
|
-
|
|
359
|
+
return n && n.addEventListener("click", (r) => {
|
|
360
|
+
r.preventDefault(), this.handleClick(e, i);
|
|
361
361
|
}), this.trackImpression(e, t, i), t;
|
|
362
362
|
}
|
|
363
363
|
/**
|
|
@@ -367,8 +367,8 @@ const C = class C {
|
|
|
367
367
|
const s = i.variant || "card";
|
|
368
368
|
t.innerHTML = this.generateSponsoredSourceHTML(e, s);
|
|
369
369
|
const n = t.querySelector(".ax-ad-source-link");
|
|
370
|
-
return n && n.addEventListener("click", (
|
|
371
|
-
|
|
370
|
+
return n && n.addEventListener("click", (r) => {
|
|
371
|
+
r.preventDefault(), this.handleClick(e, i);
|
|
372
372
|
}), this.trackImpression(e, t, i), t;
|
|
373
373
|
}
|
|
374
374
|
/**
|
|
@@ -387,9 +387,9 @@ const C = class C {
|
|
|
387
387
|
static renderAds(e, t, i = this.renderActionCard.bind(this), s) {
|
|
388
388
|
t.innerHTML = "";
|
|
389
389
|
const n = document.createElement("div");
|
|
390
|
-
return n.className = "ax-ads-container", e.forEach((
|
|
390
|
+
return n.className = "ax-ads-container", e.forEach((r) => {
|
|
391
391
|
const a = document.createElement("div");
|
|
392
|
-
a.className = "ax-ad-wrapper", i.call(this,
|
|
392
|
+
a.className = "ax-ad-wrapper", i.call(this, r, a, s), n.appendChild(a);
|
|
393
393
|
}), t.appendChild(n), t;
|
|
394
394
|
}
|
|
395
395
|
/**
|
|
@@ -411,8 +411,8 @@ const C = class C {
|
|
|
411
411
|
adTitle: e.adapted.title,
|
|
412
412
|
format: e.original.type,
|
|
413
413
|
userId: i.userId
|
|
414
|
-
}, { baseUrl: i.apiBaseUrl }).catch((
|
|
415
|
-
console.error("[DOMRenderer] Analytics click tracking failed:",
|
|
414
|
+
}, { baseUrl: i.apiBaseUrl }).catch((r) => {
|
|
415
|
+
console.error("[DOMRenderer] Analytics click tracking failed:", r);
|
|
416
416
|
}), this.isDebugEnabled() && console.log("[DOMRenderer] Redirect URL:", n), window.open(n, "_blank", "noopener,noreferrer");
|
|
417
417
|
}
|
|
418
418
|
/**
|
|
@@ -420,13 +420,13 @@ const C = class C {
|
|
|
420
420
|
* Falls back to immediate tracking if IntersectionObserver is unavailable.
|
|
421
421
|
*/
|
|
422
422
|
static trackImpression(e, t, i) {
|
|
423
|
-
const { analytics: s, onImpression: n } = i,
|
|
424
|
-
if (this.trackedImpressionKeys.has(
|
|
423
|
+
const { analytics: s, onImpression: n } = i, r = s ? `${s.requestId}:${s.slotId}:${e.original.id}:${this.getViewToken(e) || ""}` : `no-analytics:${e.original.id}:${this.getViewToken(e) || ""}`;
|
|
424
|
+
if (this.trackedImpressionKeys.has(r))
|
|
425
425
|
return;
|
|
426
426
|
let a = !1;
|
|
427
427
|
const c = () => {
|
|
428
|
-
a || this.trackedImpressionKeys.has(
|
|
429
|
-
},
|
|
428
|
+
a || this.trackedImpressionKeys.has(r) || (a = !0, this.trackedImpressionKeys.add(r), d());
|
|
429
|
+
}, d = () => {
|
|
430
430
|
s ? I({
|
|
431
431
|
requestId: s.requestId,
|
|
432
432
|
adId: e.original.id,
|
|
@@ -441,17 +441,17 @@ const C = class C {
|
|
|
441
441
|
userId: s.userId
|
|
442
442
|
}, { baseUrl: s.apiBaseUrl }).then(() => {
|
|
443
443
|
n && n(e);
|
|
444
|
-
}).catch((
|
|
445
|
-
console.error("[DOMRenderer] Analytics impression tracking failed:",
|
|
444
|
+
}).catch((l) => {
|
|
445
|
+
console.error("[DOMRenderer] Analytics impression tracking failed:", l);
|
|
446
446
|
}) : n && n(e);
|
|
447
447
|
};
|
|
448
448
|
if (typeof IntersectionObserver < "u") {
|
|
449
|
-
let
|
|
449
|
+
let l = !1, u = null;
|
|
450
450
|
const h = new IntersectionObserver(
|
|
451
451
|
(f) => {
|
|
452
452
|
f.forEach((v) => {
|
|
453
|
-
|
|
454
|
-
u = null,
|
|
453
|
+
l = v.isIntersecting && v.intersectionRatio >= 0.5, l ? u || (u = setTimeout(() => {
|
|
454
|
+
u = null, l && (c(), h.disconnect());
|
|
455
455
|
}, 1e3)) : u && (clearTimeout(u), u = null);
|
|
456
456
|
});
|
|
457
457
|
},
|
|
@@ -496,8 +496,8 @@ const C = class C {
|
|
|
496
496
|
* Generate HTML for Sponsored Source Ad variants
|
|
497
497
|
*/
|
|
498
498
|
static generateSponsoredSourceHTML(e, t) {
|
|
499
|
-
var
|
|
500
|
-
const i = e.adapted.title || "", s = e.adapted.body || "", n = ((
|
|
499
|
+
var r;
|
|
500
|
+
const i = e.adapted.title || "", s = e.adapted.body || "", n = ((r = e.adapted.image) == null ? void 0 : r.url) || "";
|
|
501
501
|
return t === "list-item" ? `
|
|
502
502
|
<div class="ax-ad-source ax-ad-variant-list-item" data-ad-id="${e.original.id}">
|
|
503
503
|
${n ? `<img src="${n}" alt="${i}" loading="lazy" />` : ""}
|
|
@@ -779,8 +779,8 @@ class S {
|
|
|
779
779
|
const n = e.match(/(MI|Redmi|POCO)\s([\w\s]+)/);
|
|
780
780
|
if (n) return n[1] + " " + n[2];
|
|
781
781
|
if (/OnePlus/.test(e)) {
|
|
782
|
-
const
|
|
783
|
-
return
|
|
782
|
+
const r = e.match(/OnePlus\s([A-Z\d]+)/);
|
|
783
|
+
return r ? "OnePlus " + r[1] : "OnePlus";
|
|
784
784
|
}
|
|
785
785
|
}
|
|
786
786
|
/**
|
|
@@ -927,85 +927,85 @@ class S {
|
|
|
927
927
|
}
|
|
928
928
|
}
|
|
929
929
|
const $ = () => S.getInstance();
|
|
930
|
-
function b(
|
|
931
|
-
return $().collect(
|
|
930
|
+
function b(o) {
|
|
931
|
+
return $().collect(o);
|
|
932
932
|
}
|
|
933
|
-
function
|
|
933
|
+
function X() {
|
|
934
934
|
return b().device;
|
|
935
935
|
}
|
|
936
936
|
function N() {
|
|
937
937
|
return b().user;
|
|
938
938
|
}
|
|
939
|
-
function
|
|
939
|
+
function Z() {
|
|
940
940
|
return b().app;
|
|
941
941
|
}
|
|
942
|
-
function
|
|
942
|
+
function Y() {
|
|
943
943
|
return b().geo;
|
|
944
944
|
}
|
|
945
|
-
function
|
|
945
|
+
function D() {
|
|
946
946
|
return N().id;
|
|
947
947
|
}
|
|
948
948
|
function ee() {
|
|
949
949
|
$().clearCache();
|
|
950
950
|
}
|
|
951
|
-
function te(
|
|
952
|
-
const e = b(
|
|
951
|
+
function te(o) {
|
|
952
|
+
const e = b(o);
|
|
953
953
|
return JSON.stringify(e, null, 2);
|
|
954
954
|
}
|
|
955
955
|
function ie() {
|
|
956
956
|
var t;
|
|
957
|
-
const
|
|
957
|
+
const o = b();
|
|
958
958
|
return [
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
((t =
|
|
959
|
+
o.device.os,
|
|
960
|
+
o.device.osv,
|
|
961
|
+
o.app.name,
|
|
962
|
+
o.user.id.slice(0, 8) + "...",
|
|
963
|
+
((t = o.geo) == null ? void 0 : t.country) || "Unknown"
|
|
964
964
|
].join(" / ");
|
|
965
965
|
}
|
|
966
966
|
function M() {
|
|
967
|
-
var
|
|
968
|
-
return typeof window < "u" ? !!((
|
|
967
|
+
var o;
|
|
968
|
+
return typeof window < "u" ? !!((o = window.__AD_CONFIG__) != null && o.debug) : !1;
|
|
969
969
|
}
|
|
970
|
-
function se(
|
|
970
|
+
function se(o) {
|
|
971
971
|
var e, t;
|
|
972
972
|
return {
|
|
973
|
-
id:
|
|
974
|
-
type:
|
|
975
|
-
score:
|
|
973
|
+
id: o.original.id,
|
|
974
|
+
type: o.original.type,
|
|
975
|
+
score: o.original.score,
|
|
976
976
|
source: "internal",
|
|
977
977
|
// Default source
|
|
978
978
|
content: {
|
|
979
|
-
title:
|
|
980
|
-
body:
|
|
981
|
-
image: (e =
|
|
982
|
-
cta_text:
|
|
983
|
-
price: (t =
|
|
984
|
-
rating:
|
|
985
|
-
brand:
|
|
979
|
+
title: o.adapted.title,
|
|
980
|
+
body: o.adapted.body,
|
|
981
|
+
image: (e = o.adapted.image) == null ? void 0 : e.url,
|
|
982
|
+
cta_text: o.adapted.ctaText,
|
|
983
|
+
price: (t = o.adapted.price) == null ? void 0 : t.display,
|
|
984
|
+
rating: o.adapted.rating,
|
|
985
|
+
brand: o.adapted.brand
|
|
986
986
|
},
|
|
987
987
|
tracking: {
|
|
988
|
-
click_url:
|
|
989
|
-
impression_url:
|
|
988
|
+
click_url: o.tracking.clickUrl || o.tracking.click_url || "",
|
|
989
|
+
impression_url: o.tracking.impressionUrl || o.tracking.impression_url || ""
|
|
990
990
|
},
|
|
991
991
|
metadata: {
|
|
992
|
-
viewToken:
|
|
993
|
-
styling:
|
|
992
|
+
viewToken: o.tracking.viewToken || o.tracking.view_token,
|
|
993
|
+
styling: o.adapted.styling
|
|
994
994
|
}
|
|
995
995
|
};
|
|
996
996
|
}
|
|
997
|
-
async function
|
|
997
|
+
async function H(o, e = {}) {
|
|
998
998
|
const t = e.apiBaseUrl || "/api/v1", s = $().collect(), n = {
|
|
999
|
-
...
|
|
999
|
+
...o,
|
|
1000
1000
|
clientInfo: s
|
|
1001
1001
|
// Auto-injected
|
|
1002
|
-
},
|
|
1002
|
+
}, r = {
|
|
1003
1003
|
"Content-Type": "application/json"
|
|
1004
1004
|
};
|
|
1005
|
-
e.apiKey && (
|
|
1005
|
+
e.apiKey && (r["X-API-Key"] = e.apiKey), M() && (console.log("[fetchAds] Request URL:", `${t}/ads/request`), console.log("[fetchAds] Request params:", o), console.log("[fetchAds] Request body:", n));
|
|
1006
1006
|
const a = await fetch(`${t}/ads/request`, {
|
|
1007
1007
|
method: "POST",
|
|
1008
|
-
headers:
|
|
1008
|
+
headers: r,
|
|
1009
1009
|
body: JSON.stringify(n)
|
|
1010
1010
|
});
|
|
1011
1011
|
if (!a.ok)
|
|
@@ -1015,16 +1015,16 @@ async function D(r, e = {}) {
|
|
|
1015
1015
|
throw new Error(c.error || "Ad request failed");
|
|
1016
1016
|
return c.data;
|
|
1017
1017
|
}
|
|
1018
|
-
const
|
|
1018
|
+
const F = { width: 400, height: 200 }, A = {
|
|
1019
1019
|
variant: "horizontal",
|
|
1020
1020
|
count: 1,
|
|
1021
1021
|
preferences: {}
|
|
1022
1022
|
};
|
|
1023
|
-
function _(
|
|
1023
|
+
function _(o = {}) {
|
|
1024
1024
|
const e = {
|
|
1025
|
-
variant:
|
|
1026
|
-
count:
|
|
1027
|
-
preferences: { ...A.preferences, ...
|
|
1025
|
+
variant: o.variant ?? A.variant,
|
|
1026
|
+
count: o.count ?? A.count,
|
|
1027
|
+
preferences: { ...A.preferences, ...o.preferences }
|
|
1028
1028
|
};
|
|
1029
1029
|
return [
|
|
1030
1030
|
{
|
|
@@ -1032,7 +1032,7 @@ function _(r = {}) {
|
|
|
1032
1032
|
slotName: "Action Card",
|
|
1033
1033
|
format: "action_card",
|
|
1034
1034
|
variant: e.variant,
|
|
1035
|
-
size:
|
|
1035
|
+
size: F,
|
|
1036
1036
|
count: e.count,
|
|
1037
1037
|
preferences: e.preferences,
|
|
1038
1038
|
placement: { position: "below_fold", context: "post_response" }
|
|
@@ -1053,9 +1053,8 @@ const g = class g {
|
|
|
1053
1053
|
});
|
|
1054
1054
|
}
|
|
1055
1055
|
async requestAds(e) {
|
|
1056
|
-
var o;
|
|
1057
1056
|
const t = "conversationContext" in e ? e : { conversationContext: e };
|
|
1058
|
-
this.currentUserId = (
|
|
1057
|
+
this.currentUserId = this.resolveUserId(t.userContext);
|
|
1059
1058
|
const i = this.buildRequestKey(t), s = g.inFlightRequests.get(i);
|
|
1060
1059
|
if (s)
|
|
1061
1060
|
return this.config.debug && console.log("[AdManager] Reusing in-flight request for identical context"), s;
|
|
@@ -1066,10 +1065,14 @@ const g = class g {
|
|
|
1066
1065
|
this.isLoading = !0, this.eventBus.emit("adsLoading"), this.config.debug && console.log("[AdManager] Starting ad request...");
|
|
1067
1066
|
const n = (async () => {
|
|
1068
1067
|
try {
|
|
1069
|
-
const
|
|
1068
|
+
const r = await H(
|
|
1070
1069
|
{
|
|
1071
1070
|
conversationContext: t.conversationContext,
|
|
1072
|
-
userContext: t.userContext ? {
|
|
1071
|
+
userContext: t.userContext ? {
|
|
1072
|
+
...t.userContext,
|
|
1073
|
+
userId: t.userContext.userId ?? this.currentUserId,
|
|
1074
|
+
sessionId: t.userContext.sessionId ?? m()
|
|
1075
|
+
} : { userId: this.currentUserId, sessionId: m() },
|
|
1073
1076
|
slots: this.slots_config
|
|
1074
1077
|
},
|
|
1075
1078
|
{
|
|
@@ -1077,29 +1080,29 @@ const g = class g {
|
|
|
1077
1080
|
apiKey: this.config.apiKey
|
|
1078
1081
|
}
|
|
1079
1082
|
);
|
|
1080
|
-
this.currentRequestId =
|
|
1081
|
-
|
|
1082
|
-
const
|
|
1083
|
-
this.adsAnalyticsMap.set(
|
|
1084
|
-
requestId:
|
|
1085
|
-
slotId:
|
|
1086
|
-
position:
|
|
1087
|
-
totalAds:
|
|
1083
|
+
this.currentRequestId = r.requestId, this.adsAnalyticsMap.clear(), r.slots.forEach((c) => {
|
|
1084
|
+
c.ads.forEach((d, l) => {
|
|
1085
|
+
const u = d.original.id;
|
|
1086
|
+
this.adsAnalyticsMap.set(u, {
|
|
1087
|
+
requestId: r.requestId,
|
|
1088
|
+
slotId: c.slotId,
|
|
1089
|
+
position: l,
|
|
1090
|
+
totalAds: c.ads.length,
|
|
1088
1091
|
apiBaseUrl: this.config.apiBaseUrl,
|
|
1089
1092
|
userId: this.currentUserId
|
|
1090
1093
|
});
|
|
1091
1094
|
});
|
|
1092
1095
|
});
|
|
1093
|
-
const
|
|
1094
|
-
return
|
|
1095
|
-
c
|
|
1096
|
-
}), this.slots =
|
|
1096
|
+
const a = {};
|
|
1097
|
+
return r.slots.forEach((c) => {
|
|
1098
|
+
a[c.slotId] = c;
|
|
1099
|
+
}), this.slots = a, this.eventBus.emit("adsUpdated", this.slots), this.config.debug && console.log("[AdManager] Ads received:", {
|
|
1097
1100
|
slotCount: Object.keys(this.slots).length,
|
|
1098
1101
|
slots: Object.keys(this.slots)
|
|
1099
|
-
}),
|
|
1100
|
-
} catch (
|
|
1101
|
-
const
|
|
1102
|
-
throw this.eventBus.emit("adsError",
|
|
1102
|
+
}), r;
|
|
1103
|
+
} catch (r) {
|
|
1104
|
+
const a = r;
|
|
1105
|
+
throw this.eventBus.emit("adsError", a), a;
|
|
1103
1106
|
} finally {
|
|
1104
1107
|
this.isLoading = !1, g.inFlightRequests.delete(i);
|
|
1105
1108
|
}
|
|
@@ -1115,25 +1118,34 @@ const g = class g {
|
|
|
1115
1118
|
slots: this.slots_config
|
|
1116
1119
|
});
|
|
1117
1120
|
}
|
|
1121
|
+
resolveUserId(e) {
|
|
1122
|
+
if (e != null && e.userId)
|
|
1123
|
+
return e.userId;
|
|
1124
|
+
try {
|
|
1125
|
+
return D();
|
|
1126
|
+
} catch {
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1118
1130
|
render(e, t, i = {}) {
|
|
1119
|
-
const s = typeof e != "string", n = s ? g.DEFAULT_SLOT_ID : e,
|
|
1120
|
-
if (!
|
|
1131
|
+
const s = typeof e != "string", n = s ? g.DEFAULT_SLOT_ID : e, r = s ? e : t, a = (s ? t : i) || {};
|
|
1132
|
+
if (!r)
|
|
1121
1133
|
return this.config.debug && console.warn("[AdManager] Render container is required"), null;
|
|
1122
1134
|
const c = this.slots[n];
|
|
1123
1135
|
if (!c || !c.ads || c.ads.length === 0)
|
|
1124
1136
|
return this.config.debug && console.warn("[AdManager] No ads in slot:", n), null;
|
|
1125
|
-
const
|
|
1126
|
-
if (!
|
|
1127
|
-
return this.config.debug && console.warn(`[AdManager] Ad at index ${
|
|
1128
|
-
const u = this.adsAnalyticsMap.get(
|
|
1129
|
-
|
|
1137
|
+
const d = a.adIndex ?? 0, l = c.ads[d];
|
|
1138
|
+
if (!l)
|
|
1139
|
+
return this.config.debug && console.warn(`[AdManager] Ad at index ${d} not found in slot:`, n), null;
|
|
1140
|
+
const u = this.adsAnalyticsMap.get(l.original.id), h = this.slots_config.find((v) => v.slotId === n), p = (h == null ? void 0 : h.format) || "action_card";
|
|
1141
|
+
r.innerHTML = "";
|
|
1130
1142
|
const f = {
|
|
1131
1143
|
analytics: u,
|
|
1132
1144
|
variant: a.variant,
|
|
1133
1145
|
onClick: a.onClick,
|
|
1134
1146
|
onImpression: a.onImpression
|
|
1135
1147
|
};
|
|
1136
|
-
return p === "suffix" ? y.renderSuffixAd(
|
|
1148
|
+
return p === "suffix" ? y.renderSuffixAd(l, r, f) : p === "source" ? y.renderSponsoredSource(l, r, f) : p === "lead_gen" ? y.renderLeadGenAd(l, r, f) : y.renderActionCard(l, r, f);
|
|
1137
1149
|
}
|
|
1138
1150
|
/**
|
|
1139
1151
|
* Get ad slots data
|
|
@@ -1227,7 +1239,7 @@ const g = class g {
|
|
|
1227
1239
|
const s = this.adsAnalyticsMap.get(e);
|
|
1228
1240
|
if (!s || !this.currentRequestId)
|
|
1229
1241
|
return this.config.debug && console.warn("[AdManager] No analytics info for ad:", e), null;
|
|
1230
|
-
const n = m(),
|
|
1242
|
+
const n = m(), r = await T({
|
|
1231
1243
|
requestId: this.currentRequestId,
|
|
1232
1244
|
adId: e,
|
|
1233
1245
|
destinationUrl: t,
|
|
@@ -1238,7 +1250,7 @@ const g = class g {
|
|
|
1238
1250
|
source: i == null ? void 0 : i.source,
|
|
1239
1251
|
userId: s.userId ?? this.currentUserId
|
|
1240
1252
|
}, { baseUrl: s.apiBaseUrl });
|
|
1241
|
-
return
|
|
1253
|
+
return r && (this.eventBus.emit("adClicked", e, s.slotId), this.config.debug && console.log("[AdManager] Click tracked via Analytics API:", r.eventId)), r;
|
|
1242
1254
|
}
|
|
1243
1255
|
/**
|
|
1244
1256
|
* Track ad impression using Analytics API
|
|
@@ -1273,12 +1285,12 @@ g.DEFAULT_SLOT_ID = "action_card", g.inFlightRequests = /* @__PURE__ */ new Map(
|
|
|
1273
1285
|
let V = g;
|
|
1274
1286
|
function E() {
|
|
1275
1287
|
if (typeof window < "u") {
|
|
1276
|
-
const
|
|
1277
|
-
if (
|
|
1278
|
-
return
|
|
1288
|
+
const o = window.__AD_CONFIG__;
|
|
1289
|
+
if (o != null && o.apiKey)
|
|
1290
|
+
return o.apiKey;
|
|
1279
1291
|
}
|
|
1280
1292
|
}
|
|
1281
|
-
class
|
|
1293
|
+
class G {
|
|
1282
1294
|
constructor(e = {}, t = "/api/v1") {
|
|
1283
1295
|
var i, s;
|
|
1284
1296
|
this.observers = /* @__PURE__ */ new Map(), this.metrics = /* @__PURE__ */ new Map(), this.timers = /* @__PURE__ */ new Map(), this.batchTimers = /* @__PURE__ */ new Map(), this.eventQueue = /* @__PURE__ */ new Map(), this.isTracking = /* @__PURE__ */ new Map(), this.config = {
|
|
@@ -1301,7 +1313,7 @@ class F {
|
|
|
1301
1313
|
return;
|
|
1302
1314
|
}
|
|
1303
1315
|
this.log(`[ViewabilityTracker] Starting tracking for ${e}`);
|
|
1304
|
-
const
|
|
1316
|
+
const r = {
|
|
1305
1317
|
visiblePercentage: 0,
|
|
1306
1318
|
maxVisiblePercentage: 0,
|
|
1307
1319
|
totalVisibleTimeMs: 0,
|
|
@@ -1311,9 +1323,9 @@ class F {
|
|
|
1311
1323
|
enteredViewportAt: null,
|
|
1312
1324
|
enterCount: 0
|
|
1313
1325
|
};
|
|
1314
|
-
this.metrics.set(e,
|
|
1326
|
+
this.metrics.set(e, r), this.isTracking.set(e, !0), this.eventQueue.set(e, []);
|
|
1315
1327
|
const a = new IntersectionObserver(
|
|
1316
|
-
(
|
|
1328
|
+
(d) => this.handleIntersection(e, d[0], i, s, n),
|
|
1317
1329
|
{
|
|
1318
1330
|
threshold: this.createThresholds()
|
|
1319
1331
|
}
|
|
@@ -1341,54 +1353,54 @@ class F {
|
|
|
1341
1353
|
* Event-driven: Only process when state actually changes
|
|
1342
1354
|
*/
|
|
1343
1355
|
handleIntersection(e, t, i, s, n) {
|
|
1344
|
-
const
|
|
1345
|
-
if (!
|
|
1346
|
-
const a =
|
|
1347
|
-
|
|
1348
|
-
const
|
|
1349
|
-
if (
|
|
1356
|
+
const r = this.metrics.get(e);
|
|
1357
|
+
if (!r || !this.isTracking.get(e)) return;
|
|
1358
|
+
const a = r.isViewable, c = Math.round(t.intersectionRatio * 100);
|
|
1359
|
+
r.visiblePercentage = c, r.maxVisiblePercentage = Math.max(r.maxVisiblePercentage, c);
|
|
1360
|
+
const d = Date.now(), l = c >= this.config.minVisiblePercentage;
|
|
1361
|
+
if (l && !r.enteredViewportAt && (r.enteredViewportAt = d, r.enterCount++, this.log(`[ViewabilityTracker] ${e} entered viewport at ${d}`), this.queueEvent(e, {
|
|
1350
1362
|
adId: e,
|
|
1351
1363
|
sessionId: i,
|
|
1352
1364
|
requestId: s,
|
|
1353
1365
|
viewToken: n,
|
|
1354
1366
|
eventType: "enter_viewport",
|
|
1355
1367
|
visiblePercentage: c,
|
|
1356
|
-
maxVisiblePercentage:
|
|
1357
|
-
totalVisibleTimeMs:
|
|
1368
|
+
maxVisiblePercentage: r.maxVisiblePercentage,
|
|
1369
|
+
totalVisibleTimeMs: r.totalVisibleTimeMs,
|
|
1358
1370
|
isViewable: !1,
|
|
1359
|
-
timestamp:
|
|
1360
|
-
})), !
|
|
1361
|
-
const u =
|
|
1362
|
-
|
|
1371
|
+
timestamp: d
|
|
1372
|
+
})), !l && r.enteredViewportAt) {
|
|
1373
|
+
const u = d - r.enteredViewportAt;
|
|
1374
|
+
r.totalVisibleTimeMs += u, r.enteredViewportAt = null, r.currentVisibleTimeMs = 0, this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${r.totalVisibleTimeMs}ms`), this.queueEvent(e, {
|
|
1363
1375
|
adId: e,
|
|
1364
1376
|
sessionId: i,
|
|
1365
1377
|
requestId: s,
|
|
1366
1378
|
viewToken: n,
|
|
1367
1379
|
eventType: "exit_viewport",
|
|
1368
1380
|
visiblePercentage: c,
|
|
1369
|
-
maxVisiblePercentage:
|
|
1370
|
-
totalVisibleTimeMs:
|
|
1371
|
-
isViewable:
|
|
1372
|
-
timestamp:
|
|
1381
|
+
maxVisiblePercentage: r.maxVisiblePercentage,
|
|
1382
|
+
totalVisibleTimeMs: r.totalVisibleTimeMs,
|
|
1383
|
+
isViewable: r.isViewable,
|
|
1384
|
+
timestamp: d
|
|
1373
1385
|
});
|
|
1374
1386
|
}
|
|
1375
|
-
|
|
1387
|
+
r.isViewable !== a && r.isViewable && this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`), this.metrics.set(e, r);
|
|
1376
1388
|
}
|
|
1377
1389
|
/**
|
|
1378
1390
|
* Start monitoring loop for tracking duration
|
|
1379
1391
|
* Runs every 100ms to track continuous visible time
|
|
1380
1392
|
*/
|
|
1381
1393
|
startMonitoring(e, t, i, s) {
|
|
1382
|
-
const
|
|
1394
|
+
const r = setInterval(() => {
|
|
1383
1395
|
const a = this.metrics.get(e);
|
|
1384
1396
|
if (!a || !this.isTracking.get(e)) {
|
|
1385
|
-
clearInterval(
|
|
1397
|
+
clearInterval(r);
|
|
1386
1398
|
return;
|
|
1387
1399
|
}
|
|
1388
|
-
const c = Date.now(),
|
|
1400
|
+
const c = Date.now(), d = a.isViewable;
|
|
1389
1401
|
if (a.enteredViewportAt) {
|
|
1390
|
-
const
|
|
1391
|
-
a.currentVisibleTimeMs =
|
|
1402
|
+
const l = c - a.enteredViewportAt;
|
|
1403
|
+
a.currentVisibleTimeMs = l, l >= this.config.minViewableDuration && a.visiblePercentage >= this.config.minVisiblePercentage && (a.isViewable = !0, d || (a.viewableAt = a.enteredViewportAt, this.log(`[ViewabilityTracker] ${e} BECAME VIEWABLE at ${a.viewableAt}`), this.queueEvent(e, {
|
|
1392
1404
|
adId: e,
|
|
1393
1405
|
sessionId: t,
|
|
1394
1406
|
requestId: i,
|
|
@@ -1403,13 +1415,13 @@ class F {
|
|
|
1403
1415
|
}
|
|
1404
1416
|
a.enteredViewportAt && (a.totalVisibleTimeMs += 100), this.metrics.set(e, a);
|
|
1405
1417
|
}, 100);
|
|
1406
|
-
this.timers.set(`${e}_monitoring`,
|
|
1418
|
+
this.timers.set(`${e}_monitoring`, r);
|
|
1407
1419
|
}
|
|
1408
1420
|
/**
|
|
1409
1421
|
* Queue an event to be sent (batched)
|
|
1410
1422
|
*/
|
|
1411
1423
|
queueEvent(e, t) {
|
|
1412
|
-
var n,
|
|
1424
|
+
var n, r;
|
|
1413
1425
|
const i = this.eventQueue.get(e);
|
|
1414
1426
|
if (!i) {
|
|
1415
1427
|
this.log(`[ViewabilityTracker] No queue found for ${e}, skipping event`);
|
|
@@ -1422,10 +1434,10 @@ class F {
|
|
|
1422
1434
|
else {
|
|
1423
1435
|
const a = this.batchTimers.get(e);
|
|
1424
1436
|
a && clearTimeout(a);
|
|
1425
|
-
const c = ((
|
|
1437
|
+
const c = ((r = this.config.batchConfig) == null ? void 0 : r.maxBatchWaitMs) ?? 1e4, d = setTimeout(() => {
|
|
1426
1438
|
this.flushQueue(e);
|
|
1427
1439
|
}, c);
|
|
1428
|
-
this.batchTimers.set(e,
|
|
1440
|
+
this.batchTimers.set(e, d);
|
|
1429
1441
|
}
|
|
1430
1442
|
}
|
|
1431
1443
|
/**
|
|
@@ -1456,7 +1468,7 @@ class F {
|
|
|
1456
1468
|
const n = this.metrics.get(e);
|
|
1457
1469
|
if (!n) return;
|
|
1458
1470
|
this.log(`[ViewabilityTracker] Ending tracking for ${e}`), this.flushQueue(e);
|
|
1459
|
-
const
|
|
1471
|
+
const r = Date.now();
|
|
1460
1472
|
this.queueEvent(e, {
|
|
1461
1473
|
adId: e,
|
|
1462
1474
|
sessionId: t,
|
|
@@ -1467,7 +1479,7 @@ class F {
|
|
|
1467
1479
|
maxVisiblePercentage: n.maxVisiblePercentage,
|
|
1468
1480
|
totalVisibleTimeMs: n.totalVisibleTimeMs,
|
|
1469
1481
|
isViewable: n.isViewable,
|
|
1470
|
-
timestamp:
|
|
1482
|
+
timestamp: r
|
|
1471
1483
|
}), this.flushQueue(e), this.cleanup(e);
|
|
1472
1484
|
}
|
|
1473
1485
|
/**
|
|
@@ -1526,8 +1538,8 @@ class F {
|
|
|
1526
1538
|
}
|
|
1527
1539
|
}
|
|
1528
1540
|
let k = null;
|
|
1529
|
-
function ne(
|
|
1530
|
-
return k || (k = new
|
|
1541
|
+
function ne(o, e) {
|
|
1542
|
+
return k || (k = new G(o, e), k.setupBeforeUnload()), k;
|
|
1531
1543
|
}
|
|
1532
1544
|
const re = "0.1.0";
|
|
1533
1545
|
export {
|
|
@@ -1538,26 +1550,26 @@ export {
|
|
|
1538
1550
|
w as HTMLRenderer,
|
|
1539
1551
|
re as SDK_VERSION,
|
|
1540
1552
|
P as SessionManager,
|
|
1541
|
-
|
|
1553
|
+
G as ViewabilityTracker,
|
|
1542
1554
|
se as adaptAdToKoahAd,
|
|
1543
1555
|
ee as clearClientInfoCache,
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1556
|
+
Q as createAnalytics,
|
|
1557
|
+
J as createSession,
|
|
1558
|
+
H as fetchAds,
|
|
1559
|
+
z as generateViewToken,
|
|
1560
|
+
Z as getAppInfo,
|
|
1549
1561
|
b as getClientInfo,
|
|
1550
1562
|
$ as getClientInfoCollector,
|
|
1551
1563
|
te as getClientInfoJSON,
|
|
1552
1564
|
ie as getClientInfoSummary,
|
|
1553
|
-
|
|
1554
|
-
|
|
1565
|
+
X as getDeviceInfo,
|
|
1566
|
+
Y as getGeoInfo,
|
|
1555
1567
|
m as getSessionId,
|
|
1556
|
-
|
|
1568
|
+
D as getUserId,
|
|
1557
1569
|
N as getUserInfo,
|
|
1558
1570
|
ne as getViewabilityTracker,
|
|
1559
1571
|
T as trackAdClick,
|
|
1560
1572
|
I as trackAdImpression,
|
|
1561
|
-
|
|
1562
|
-
|
|
1573
|
+
W as trackClicksBatch,
|
|
1574
|
+
j as trackImpressionsBatch
|
|
1563
1575
|
};
|
package/dist/index.umd.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
(function(l,k){typeof exports=="object"&&typeof module<"u"?k(exports):typeof define=="function"&&define.amd?define(["exports"],k):(l=typeof globalThis<"u"?globalThis:l||self,k(l.ActionXAdSDK={}))})(this,function(l){"use strict";class k{constructor(){this.events={}}on(e,t){this.events[e]||(this.events[e]=[]),this.events[e].push(t)}off(e,t){if(!this.events[e])return;const i=this.events[e].indexOf(t);i>-1&&this.events[e].splice(i,1)}emit(e,...t){this.events[e]&&this.events[e].forEach(i=>{try{i(...t)}catch(n){console.error(`[EventEmitter] Error in event handler for "${e}":`,n)}})}removeAllListeners(e){e?delete this.events[e]:this.events={}}listenerCount(e){var t;return((t=this.events[e])==null?void 0:t.length)||0}}class v{static getClickUrl(e){return e.tracking.clickUrl||e.tracking.click_url||"#"}static getImpressionUrl(e){return e.tracking.impressionUrl||e.tracking.impression_url||""}static getCtaText(e){return e.adapted.ctaText||e.adapted.cta_text||"Learn More"}static renderActionCard(e,t={}){var g;const{variant:i="horizontal",includeWrapper:n=!0}=t,s=e.adapted,
|
|
1
|
+
(function(l,k){typeof exports=="object"&&typeof module<"u"?k(exports):typeof define=="function"&&define.amd?define(["exports"],k):(l=typeof globalThis<"u"?globalThis:l||self,k(l.ActionXAdSDK={}))})(this,function(l){"use strict";class k{constructor(){this.events={}}on(e,t){this.events[e]||(this.events[e]=[]),this.events[e].push(t)}off(e,t){if(!this.events[e])return;const i=this.events[e].indexOf(t);i>-1&&this.events[e].splice(i,1)}emit(e,...t){this.events[e]&&this.events[e].forEach(i=>{try{i(...t)}catch(n){console.error(`[EventEmitter] Error in event handler for "${e}":`,n)}})}removeAllListeners(e){e?delete this.events[e]:this.events={}}listenerCount(e){var t;return((t=this.events[e])==null?void 0:t.length)||0}}class v{static getClickUrl(e){return e.tracking.clickUrl||e.tracking.click_url||"#"}static getImpressionUrl(e){return e.tracking.impressionUrl||e.tracking.impression_url||""}static getCtaText(e){return e.adapted.ctaText||e.adapted.cta_text||"Learn More"}static renderActionCard(e,t={}){var g;const{variant:i="horizontal",includeWrapper:n=!0}=t,s=e.adapted,r=this.getClickUrl(e),a=this.getImpressionUrl(e),c=n?`<div class="ax-ad-card ax-ad-card-${i}" data-ad-id="${e.original.id}">`:"",u=(g=s.image)!=null&&g.url?`<img
|
|
2
2
|
src="${s.image.url}"
|
|
3
3
|
alt="${s.title}"
|
|
4
4
|
class="ax-ad-image"
|
|
5
5
|
loading="lazy"
|
|
6
|
-
/>`:"",
|
|
7
|
-
${
|
|
6
|
+
/>`:"",d=(s.brand||"").trim(),h=d&&d.toLowerCase()!=="unknown"?`<span class="ax-ad-brand">${s.brand}</span>`:"",f=`
|
|
7
|
+
${u}
|
|
8
8
|
<div class="ax-ad-content">
|
|
9
9
|
${h}
|
|
10
10
|
<h3 class="ax-ad-title">${s.title}</h3>
|
|
11
11
|
${s.body?`<p class="ax-ad-body">${s.body}</p>`:""}
|
|
12
12
|
<div class="ax-ad-footer">
|
|
13
13
|
<a
|
|
14
|
-
href="${
|
|
14
|
+
href="${r}"
|
|
15
15
|
class="ax-ad-cta"
|
|
16
16
|
target="_blank"
|
|
17
17
|
rel="sponsored noopener noreferrer"
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
</div>
|
|
76
76
|
</div>
|
|
77
77
|
`}static renderAds(e,t=v.renderActionCard.bind(v)){return e.map(t).join(`
|
|
78
|
-
`)}}const
|
|
78
|
+
`)}}const D="ad_session_id";function H(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}function F(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function E(o){if(o)return o;if(typeof window<"u"){const e=window.__AD_CONFIG__;if(e!=null&&e.apiBaseUrl)return e.apiBaseUrl}return"/api/v1"}class U{constructor(e={}){this.memoryCache=new Map,this.sessionKey=e.sessionKey||D,this.storage=e.storage||"sessionStorage",(typeof window>"u"||typeof window.sessionStorage>"u")&&(this.storage="memory")}getSessionId(){if(this.storage==="sessionStorage"){let e=sessionStorage.getItem(this.sessionKey);return e||(e=this.generateSessionId(),sessionStorage.setItem(this.sessionKey,e)),e}else{let e=this.memoryCache.get(this.sessionKey);return e||(e=this.generateSessionId(),this.memoryCache.set(this.sessionKey,e)),e}}regenerateSessionId(){const e=this.generateSessionId();return this.storage==="sessionStorage"?sessionStorage.setItem(this.sessionKey,e):this.memoryCache.set(this.sessionKey,e),e}generateSessionId(){return`session_${Date.now()}_${Math.random().toString(36).substring(2,11)}`}clearSessionId(){this.storage==="sessionStorage"?sessionStorage.removeItem(this.sessionKey):this.memoryCache.delete(this.sessionKey)}}const G=new U,m=()=>G.getSessionId();class A{constructor(e={}){this.explicitBaseUrl=e.baseUrl,this.timeout=e.timeout||1e4,this.retryAttempts=e.retryAttempts??2,this.retryDelay=e.retryDelay||1e3,this.debug=e.debug||!1}async trackImpression(e){return this.sendRequest(`${E(this.explicitBaseUrl)}/ads/impression`,e,"impression")}async trackClick(e){return this.sendRequest(`${E(this.explicitBaseUrl)}/ads/click`,e,"click")}async sendRequest(e,t,i){let n=null;for(let s=0;s<=this.retryAttempts;s++){const r=this.debug||F();try{r&&(console.log(`[Analytics] Request URL (${i}):`,e),console.log(`[Analytics] Sending ${i} event (attempt ${s+1}):`,t));const a=new AbortController,c=setTimeout(()=>a.abort(),this.timeout),u={"Content-Type":"application/json"},d=H();d&&(u["X-API-Key"]=d,r&&console.log(`[Analytics] Adding X-API-Key header for ${i} event`));const h=await fetch(e,{method:"POST",headers:u,body:JSON.stringify(t),keepalive:!0,signal:a.signal});if(clearTimeout(c),!h.ok)throw new Error(`HTTP ${h.status}: ${h.statusText}`);const f=await h.json();return r&&console.log(`[Analytics] ${i} event tracked successfully:`,f),f}catch(a){n=a,r&&console.warn(`[Analytics] ${i} tracking failed (attempt ${s+1}):`,a),s<this.retryAttempts&&await this.delay(this.retryDelay*(s+1))}}return console.error(`[Analytics] ${i} tracking failed after ${this.retryAttempts+1} attempts:`,n),null}delay(e){return new Promise(t=>setTimeout(t,e))}}const P=new A,I=(o,e)=>e!=null&&e.baseUrl?new A({baseUrl:e.baseUrl}).trackImpression(o):P.trackImpression(o),x=(o,e)=>e!=null&&e.baseUrl?new A({baseUrl:e.baseUrl}).trackClick(o):P.trackClick(o);function j(o,e){return`vt_${o}_${e}`}async function z(o){return Promise.all(o.map(e=>I(e)))}async function W(o){return Promise.all(o.map(e=>x(e)))}function Q(o){const e=new A(o);return{trackImpression:t=>e.trackImpression(t),trackClick:t=>e.trackClick(t)}}function J(o={}){const e=new U(o);return{getSessionId:()=>e.getSessionId(),regenerateSessionId:()=>e.regenerateSessionId(),clearSessionId:()=>e.clearSessionId()}}const V=class V{static isDebugEnabled(){var e;return typeof window<"u"?!!((e=window.__AD_CONFIG__)!=null&&e.debug):!1}static getClickUrl(e){return e.tracking.clickUrl||e.tracking.click_url||""}static getViewToken(e){return e.tracking.viewToken||e.tracking.view_token}static renderActionCard(e,t,i={}){t.innerHTML=v.renderActionCard(e,i);const n=t.querySelector(".ax-ad-cta");return n&&n.addEventListener("click",s=>{s.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSuffixAd(e,t,i={}){const n=i.variant||"block";t.innerHTML=this.generateSuffixAdHTML(e,n);const s=t.querySelector(".ax-ad-suffix-link");return s&&s.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderSponsoredSource(e,t,i={}){const n=i.variant||"card";t.innerHTML=this.generateSponsoredSourceHTML(e,n);const s=t.querySelector(".ax-ad-source-link");return s&&s.addEventListener("click",r=>{r.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderLeadGenAd(e,t,i={}){t.innerHTML=v.renderLeadGenAd(e);const n=t.querySelector(".ax-ad-leadgen-cta");return n&&n.addEventListener("click",s=>{s.preventDefault(),this.handleClick(e,i)}),this.trackImpression(e,t,i),t}static renderAds(e,t,i=this.renderActionCard.bind(this),n){t.innerHTML="";const s=document.createElement("div");return s.className="ax-ads-container",e.forEach(r=>{const a=document.createElement("div");a.className="ax-ad-wrapper",i.call(this,r,a,n),s.appendChild(a)}),t.appendChild(s),t}static handleClick(e,t){const{analytics:i,onClick:n}=t,s=this.getClickUrl(e);if(!s){console.warn("[DOMRenderer] Missing click url for ad:",e.original.id);return}n&&n(e),i&&x({requestId:i.requestId,adId:e.original.id,destinationUrl:s,slotId:i.slotId,sessionId:m(),adTitle:e.adapted.title,format:e.original.type,userId:i.userId},{baseUrl:i.apiBaseUrl}).catch(r=>{console.error("[DOMRenderer] Analytics click tracking failed:",r)}),this.isDebugEnabled()&&console.log("[DOMRenderer] Redirect URL:",s),window.open(s,"_blank","noopener,noreferrer")}static trackImpression(e,t,i){const{analytics:n,onImpression:s}=i,r=n?`${n.requestId}:${n.slotId}:${e.original.id}:${this.getViewToken(e)||""}`:`no-analytics:${e.original.id}:${this.getViewToken(e)||""}`;if(this.trackedImpressionKeys.has(r))return;let a=!1;const c=()=>{a||this.trackedImpressionKeys.has(r)||(a=!0,this.trackedImpressionKeys.add(r),u())},u=()=>{n?I({requestId:n.requestId,adId:e.original.id,slotId:n.slotId,position:n.position,totalAds:n.totalAds,sessionId:m(),adTitle:e.adapted.title,format:e.original.type,source:"internal",viewToken:this.getViewToken(e),userId:n.userId},{baseUrl:n.apiBaseUrl}).then(()=>{s&&s(e)}).catch(d=>{console.error("[DOMRenderer] Analytics impression tracking failed:",d)}):s&&s(e)};if(typeof IntersectionObserver<"u"){let d=!1,h=null;const f=new IntersectionObserver(g=>{g.forEach($=>{d=$.isIntersecting&&$.intersectionRatio>=.5,d?h||(h=setTimeout(()=>{h=null,d&&(c(),f.disconnect())},1e3)):h&&(clearTimeout(h),h=null)})},{threshold:.5}),w=t.querySelector(`[data-ad-id="${e.original.id}"]`)||t;f.observe(w)}else c()}static generateSuffixAdHTML(e,t){const i=e.adapted.title||"",n=e.adapted.body||"";return t==="inline"?`
|
|
79
79
|
<span class="ax-ad-suffix-link ax-ad-variant-inline" data-ad-id="${e.original.id}">
|
|
80
80
|
${i}
|
|
81
81
|
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-left:4px">
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
</div>
|
|
100
100
|
${n?`<p class="ax-ad-suffix-body">${n}</p>`:""}
|
|
101
101
|
</div>
|
|
102
|
-
`}static generateSponsoredSourceHTML(e,t){var
|
|
102
|
+
`}static generateSponsoredSourceHTML(e,t){var r;const i=e.adapted.title||"",n=e.adapted.body||"",s=((r=e.adapted.image)==null?void 0:r.url)||"";return t==="list-item"?`
|
|
103
103
|
<div class="ax-ad-source ax-ad-variant-list-item" data-ad-id="${e.original.id}">
|
|
104
104
|
${s?`<img src="${s}" alt="${i}" loading="lazy" />`:""}
|
|
105
105
|
<div class="ax-ad-source-info">
|
|
@@ -132,4 +132,4 @@
|
|
|
132
132
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
|
|
133
133
|
</svg>
|
|
134
134
|
</div>
|
|
135
|
-
`}};V.trackedImpressionKeys=new Set;let y=V;class S{constructor(){this.cachedStaticInfo=null,this.cacheTimestamp=0,this.CACHE_TTL=36e5}inferAppInfo(){if(typeof window>"u")return{bundle:"unknown",ver:"1.0.0",name:"Unknown App",publisher:{id:"unknown"}};const e=this.inferBundle(),t=this.inferAppName(),i=this.inferAppVersion(),n=this.inferPublisherId();return{bundle:e,ver:i,name:t,publisher:{id:n,domain:window.location.hostname}}}inferBundle(){var t;if(typeof window>"u")return"unknown";const e=(t=window.location)==null?void 0:t.hostname;return e?e.replace(/^www\./,"").replace(/:\d+$/,""):"unknown"}inferAppName(){var n,s;if(typeof document>"u")return"Unknown App";const e=document.title;if(e&&e!=="Loading..."&&e.length<100)return e;const t=(n=document.querySelector('meta[property="og:title"]'))==null?void 0:n.getAttribute("content");if(t)return t;const i=(s=document.querySelector('meta[name="application-name"]'))==null?void 0:s.getAttribute("content");return i||"Unknown App"}inferAppVersion(){if(typeof document>"u")return"1.0.0";const e=['meta[name="version"]','meta[name="app-version"]','meta[name="application-version"]','link[rel="manifest"]'];for(const t of e){const i=document.querySelector(t);if(i){if(t.includes("manifest"))return"1.0.0";const n=i.getAttribute("content");if(n)return n}}return"1.0.0"}inferPublisherId(){var i,n;if(typeof document>"u")return"unknown";const e=(i=document.querySelector('meta[name="publisher-id"]'))==null?void 0:i.getAttribute("content");if(e)return e;const t=(n=window.location)==null?void 0:n.hostname;return t?t.replace(/\./g,"_"):"unknown"}static getInstance(){return this.instance||(this.instance=new S),this.instance}collect(e){const t=Date.now();(!this.cachedStaticInfo||t-this.cacheTimestamp>this.CACHE_TTL)&&(this.cachedStaticInfo=this.collectStaticInfo(),this.cacheTimestamp=t);const i=this.collectDynamicInfo();return{device:{ua:this.cachedStaticInfo.ua,os:this.cachedStaticInfo.os,osv:this.cachedStaticInfo.osv,devicetype:this.cachedStaticInfo.devicetype,model:this.cachedStaticInfo.model,make:this.cachedStaticInfo.make,language:this.cachedStaticInfo.language,connectiontype:i.connectiontype??0,bandwidth:i.bandwidth,...(e==null?void 0:e.device)||{}},app:{...this.inferAppInfo(),...(e==null?void 0:e.app)||{}},user:{...this.collectUserInfo(),...(e==null?void 0:e.user)||{}},geo:{...this.collectGeoInfo(),...(e==null?void 0:e.geo)||{}},timestamp:t}}collectStaticInfo(){if(typeof navigator>"u")return this.getFallbackInfo();const e=navigator.userAgent,t=this.detectOS(e),i=this.detectOSVersion(e);return{ua:e,os:t,osv:i,devicetype:this.detectDeviceType(e),model:this.detectModel(e),make:this.detectMake(e),language:navigator.language||"en-US"}}collectDynamicInfo(){if(typeof navigator>"u")return{connectiontype:0};const e=this.getConnectionInfo();return{connectiontype:(e==null?void 0:e.type)||0,bandwidth:e==null?void 0:e.downlink}}collectUserInfo(){let e;try{e=localStorage.getItem("actionx_user_id")||"",e||(e=this.generateUUID(),localStorage.setItem("actionx_user_id",e))}catch{e=this.generateUUID()}return{id:e,language:(navigator==null?void 0:navigator.language)||"en-US"}}collectGeoInfo(){if(typeof navigator>"u"||typeof Intl>"u")return{};const e=Intl.DateTimeFormat().resolvedOptions().timeZone,t=navigator.language;return{country:this.inferCountry(t,e),timezone:e}}detectOS(e){return e?/iPad|iPhone|iPod/.test(e)?"iOS":/Android/.test(e)?"Android":/Windows/.test(e)?"Windows":/Macintosh|Mac OS/.test(e)?"macOS":/Linux/.test(e)?"Linux":/CrOS/.test(e)?"Chrome OS":"Unknown":"Unknown"}detectOSVersion(e){if(!e)return"Unknown";const t=e.match(/OS (\d+)_(\d+)_?(\d+)?/);if(t)return`${t[1]}.${t[2]}${t[3]?"."+t[3]:""}`;const i=e.match(/Android (\d+)\.(\d+)/);if(i)return`${i[1]}.${i[2]}`;const n=e.match(/Windows NT (\d+\.\d+)/);if(n)return n[1];const s=e.match(/Mac OS X (\d+[._]\d+)/);return s?s[1].replace("_","."):"Unknown"}detectDeviceType(e){return e?/iPad|Tablet/i.test(e)||/Android/.test(e)&&!/Mobile/.test(e)?2:/iPhone|iPod|Mobile|Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(e)?1:/Windows|Macintosh|Linux|x86_64/i.test(e)?3:/SmartTV|TV|WebTV|GoogleTV|AppleTV/i.test(e)?4:0:0}detectModel(e){if(!e)return;const t=e.match(/iPhone(?:;\s*[^\)]*)?\s*(\d+,\d)?/);if(t)return`iPhone${t[1]||""}`;if(/iPad/.test(e))return"iPad";const i=e.match(/Samsung-.*(SM-\w+)/);if(i)return i[1];if(/Pixel/.test(e))return"Google Pixel";const n=e.match(/(BARC-|HUAWEI-)?([A-Z]{2}\-\w{4})/);if(n)return n[2];const s=e.match(/(MI|Redmi|POCO)\s([\w\s]+)/);if(s)return s[1]+" "+s[2];if(/OnePlus/.test(e)){const o=e.match(/OnePlus\s([A-Z\d]+)/);return o?"OnePlus "+o[1]:"OnePlus"}}detectMake(e){if(e){if(/iPad|iPhone|iPod/.test(e))return"Apple";if(/Samsung/.test(e))return"Samsung";if(/Pixel/.test(e))return"Google";if(/Huawei|HONOR/.test(e))return"Huawei";if(/Xiaomi|Redmi|POCO/.test(e))return"Xiaomi";if(/OnePlus/.test(e))return"OnePlus";if(/Sony/.test(e))return"Sony";if(/LG/.test(e))return"LG";if(/Motorola|Moto/.test(e))return"Motorola";if(/Nokia/.test(e))return"Nokia";if(/HTC/.test(e))return"HTC";if(/OPPO/.test(e))return"OPPO";if(/vivo/.test(e))return"vivo";if(/Realme/.test(e))return"Realme"}}getConnectionInfo(){if(typeof navigator>"u")return null;const e=navigator,t=e.connection||e.mozConnection||e.webkitConnection;return t?{type:this.mapConnectionType(t.effectiveType),downlink:t.downlink,rtt:t.rtt}:null}mapConnectionType(e){if(!e)return 0;switch(e.toLowerCase()){case"slow-2g":case"2g":return 3;case"3g":return 4;case"4g":return 5;default:return 0}}inferCountry(e,t){if(!e&&!t)return;const i={"Asia/Shanghai":"CN","Asia/Hong_Kong":"HK","Asia/Taipei":"TW","Asia/Tokyo":"JP","Asia/Seoul":"KR","Asia/Singapore":"SG","Asia/Dubai":"AE","Asia/Kolkata":"IN","Asia/Jakarta":"ID","Asia/Manila":"PH","Asia/Bangkok":"TH","Asia/Kuala_Lumpur":"MY","America/New_York":"US","America/Chicago":"US","America/Denver":"US","America/Los_Angeles":"US","America/Toronto":"CA","America/Vancouver":"CA","America/Mexico_City":"MX","America/Sao_Paulo":"BR","America/Buenos_Aires":"AR","Europe/London":"GB","Europe/Paris":"FR","Europe/Berlin":"DE","Europe/Madrid":"ES","Europe/Rome":"IT","Europe/Amsterdam":"NL","Europe/Brussels":"BE","Europe/Zurich":"CH","Europe/Vienna":"AT","Europe/Stockholm":"SE","Europe/Oslo":"NO","Europe/Copenhagen":"DK","Europe/Helsinki":"FI","Europe/Dublin":"IE","Europe/Lisbon":"PT","Europe/Athens":"GR","Europe/Prague":"CZ","Europe/Warsaw":"PL","Europe/Budapest":"HU","Europe/Bucharest":"RO","Australia/Sydney":"AU","Pacific/Auckland":"NZ"};if(t&&i[t])return i[t];if(e){const n=e.split("-")[1];if(n)return n.toUpperCase()}}generateUUID(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}getFallbackInfo(){return{ua:"Unknown",os:"Unknown",osv:"Unknown",devicetype:0,language:"en-US"}}clearCache(){this.cachedStaticInfo=null,this.cacheTimestamp=0}}const T=()=>S.getInstance();function b(r){return T().collect(r)}function J(){return b().device}function O(){return b().user}function X(){return b().app}function Z(){return b().geo}function Y(){return O().id}function ee(){T().clearCache()}function te(r){const e=b(r);return JSON.stringify(e,null,2)}function ie(){var t;const r=b();return[r.device.os,r.device.osv,r.app.name,r.user.id.slice(0,8)+"...",((t=r.geo)==null?void 0:t.country)||"Unknown"].join(" / ")}function B(){var r;return typeof window<"u"?!!((r=window.__AD_CONFIG__)!=null&&r.debug):!1}function ne(r){var e,t;return{id:r.original.id,type:r.original.type,score:r.original.score,source:"internal",content:{title:r.adapted.title,body:r.adapted.body,image:(e=r.adapted.image)==null?void 0:e.url,cta_text:r.adapted.ctaText,price:(t=r.adapted.price)==null?void 0:t.display,rating:r.adapted.rating,brand:r.adapted.brand},tracking:{click_url:r.tracking.clickUrl||r.tracking.click_url||"",impression_url:r.tracking.impressionUrl||r.tracking.impression_url||""},metadata:{viewToken:r.tracking.viewToken||r.tracking.view_token,styling:r.adapted.styling}}}async function q(r,e={}){const t=e.apiBaseUrl||"/api/v1",n=T().collect(),s={...r,clientInfo:n},o={"Content-Type":"application/json"};e.apiKey&&(o["X-API-Key"]=e.apiKey),B()&&(console.log("[fetchAds] Request URL:",`${t}/ads/request`),console.log("[fetchAds] Request params:",r),console.log("[fetchAds] Request body:",s));const a=await fetch(`${t}/ads/request`,{method:"POST",headers:o,body:JSON.stringify(s)});if(!a.ok)throw new Error(`Ad request failed: ${a.status} ${a.statusText}`);const c=await a.json();if(B()&&console.log("[fetchAds] Response:",c),!c.success)throw new Error(c.error||"Ad request failed");return c.data}const se={width:400,height:200},M={variant:"horizontal",count:1,preferences:{}};function L(r={}){const e={variant:r.variant??M.variant,count:r.count??M.count,preferences:{...M.preferences,...r.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:se,count:e.count,preferences:e.preferences,placement:{position:"below_fold",context:"post_response"}}]}const p=class p{constructor(e){this.slots={},this.isLoading=!1,this.currentRequestId=null,this.adsAnalyticsMap=new Map,this.config=e,this.enabled=e.enabled!==!1,this.eventBus=new k,this.slots_config=L(e.cardOption),typeof window<"u"&&(window.__AD_CONFIG__={...window.__AD_CONFIG__||{},apiKey:e.apiKey,apiBaseUrl:e.apiBaseUrl,debug:!!e.debug}),this.config.debug&&console.log("[AdManager] Initialized with config:",{apiBaseUrl:e.apiBaseUrl,hasApiKey:!!e.apiKey,enabled:this.enabled})}async requestAds(e){var o;const t="conversationContext"in e?e:{conversationContext:e};this.currentUserId=(o=t.userContext)==null?void 0:o.userId;const i=this.buildRequestKey(t),n=p.inFlightRequests.get(i);if(n)return this.config.debug&&console.log("[AdManager] Reusing in-flight request for identical context"),n;if(!this.enabled)throw this.config.debug&&console.warn("[AdManager] Ads are disabled, skipping request"),new Error("Ads are disabled");if(this.isLoading)throw this.config.debug&&console.warn("[AdManager] Request already in progress"),new Error("Request already in progress");this.isLoading=!0,this.eventBus.emit("adsLoading"),this.config.debug&&console.log("[AdManager] Starting ad request...");const s=(async()=>{try{const a=await q({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,sessionId:t.userContext.sessionId??m()}:{sessionId:m()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=a.requestId,this.adsAnalyticsMap.clear(),a.slots.forEach(d=>{d.ads.forEach((u,h)=>{const f=u.original.id;this.adsAnalyticsMap.set(f,{requestId:a.requestId,slotId:d.slotId,position:h,totalAds:d.ads.length,apiBaseUrl:this.config.apiBaseUrl,userId:this.currentUserId})})});const c={};return a.slots.forEach(d=>{c[d.slotId]=d}),this.slots=c,this.eventBus.emit("adsUpdated",this.slots),this.config.debug&&console.log("[AdManager] Ads received:",{slotCount:Object.keys(this.slots).length,slots:Object.keys(this.slots)}),a}catch(a){const c=a;throw this.eventBus.emit("adsError",c),c}finally{this.isLoading=!1,p.inFlightRequests.delete(i)}})();return p.inFlightRequests.set(i,s),s}buildRequestKey(e){return JSON.stringify({apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey,conversationContext:e.conversationContext,userContext:e.userContext||null,slots:this.slots_config})}render(e,t,i={}){const n=typeof e!="string",s=n?p.DEFAULT_SLOT_ID:e,o=n?e:t,a=(n?t:i)||{};if(!o)return this.config.debug&&console.warn("[AdManager] Render container is required"),null;const c=this.slots[s];if(!c||!c.ads||c.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",s),null;const d=a.adIndex??0,u=c.ads[d];if(!u)return this.config.debug&&console.warn(`[AdManager] Ad at index ${d} not found in slot:`,s),null;const h=this.adsAnalyticsMap.get(u.original.id),f=this.slots_config.find($=>$.slotId===s),w=(f==null?void 0:f.format)||"action_card";o.innerHTML="";const g={analytics:h,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return w==="suffix"?y.renderSuffixAd(u,o,g):w==="source"?y.renderSponsoredSource(u,o,g):w==="lead_gen"?y.renderLeadGenAd(u,o,g):y.renderActionCard(u,o,g)}getSlots(e){return e?this.slots[e]:this.slots}hasAds(e){var i;const t=this.slots[e];return(t==null?void 0:t.status)==="filled"&&((i=t.ads)==null?void 0:i.length)>0}getAds(e){var t;return((t=this.slots[e])==null?void 0:t.ads)||[]}getLoadingStatus(){return this.isLoading}setEnabled(e){this.enabled=e,this.config.debug&&console.log("[AdManager] Ads",e?"enabled":"disabled")}updateConfig(e){this.config={...this.config,...e},e.cardOption!==void 0&&(this.slots_config=L(this.config.cardOption)),this.config.debug&&console.log("[AdManager] Config updated:",e)}clearSlots(){this.slots={},this.currentRequestId=null,this.adsAnalyticsMap.clear(),this.config.debug&&console.log("[AdManager] Slots and analytics cleared")}on(e,t){this.eventBus.on(e,t)}off(e,t){this.eventBus.off(e,t)}removeAllListeners(e){this.eventBus.removeAllListeners(e)}getCurrentRequestId(){return this.currentRequestId}getAdAnalytics(e){return this.adsAnalyticsMap.get(e)||null}getAdsAnalytics(e){const t=new Map;return e.forEach(i=>{const n=this.adsAnalyticsMap.get(i);n&&t.set(i,n)}),t}async trackAdClick(e,t,i){const n=this.adsAnalyticsMap.get(e);if(!n||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const s=m(),o=await I({requestId:this.currentRequestId,adId:e,destinationUrl:t,sessionId:s,slotId:n.slotId,adTitle:i==null?void 0:i.title,format:i==null?void 0:i.format,source:i==null?void 0:i.source,userId:n.userId??this.currentUserId},{baseUrl:n.apiBaseUrl});return o&&(this.eventBus.emit("adClicked",e,n.slotId),this.config.debug&&console.log("[AdManager] Click tracked via Analytics API:",o.eventId)),o}async trackAdImpressionAPI(e,t){const i=this.adsAnalyticsMap.get(e);if(!i||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const n=m(),s=await x({requestId:this.currentRequestId,adId:e,slotId:i.slotId,position:i.position,totalAds:i.totalAds,sessionId:n,adTitle:t==null?void 0:t.title,format:t==null?void 0:t.format,source:t==null?void 0:t.source,viewToken:t==null?void 0:t.viewToken,userId:i.userId??this.currentUserId},{baseUrl:i.apiBaseUrl});return s&&(this.eventBus.emit("adImpression",e,i.slotId),this.config.debug&&console.log("[AdManager] Impression tracked via Analytics API:",s.eventId)),s}destroy(){this.removeAllListeners(),this.clearSlots(),this.config.debug&&console.log("[AdManager] Destroyed")}};p.DEFAULT_SLOT_ID="action_card",p.inFlightRequests=new Map;let _=p;function R(){if(typeof window<"u"){const r=window.__AD_CONFIG__;if(r!=null&&r.apiKey)return r.apiKey}}class K{constructor(e={},t="/api/v1"){var i,n;this.observers=new Map,this.metrics=new Map,this.timers=new Map,this.batchTimers=new Map,this.eventQueue=new Map,this.isTracking=new Map,this.config={minVisiblePercentage:e.minVisiblePercentage??50,minViewableDuration:e.minViewableDuration??1e3,maxTrackingDuration:e.maxTrackingDuration??6e4,batchConfig:{maxBatchSize:((i=e.batchConfig)==null?void 0:i.maxBatchSize)??5,maxBatchWaitMs:((n=e.batchConfig)==null?void 0:n.maxBatchWaitMs)??1e4},debug:e.debug??!1},this.baseUrl=t}startTracking(e,t,i,n,s){if(this.isTracking.get(e)){this.log(`[ViewabilityTracker] Already tracking ${e}, skipping`);return}this.log(`[ViewabilityTracker] Starting tracking for ${e}`);const o={visiblePercentage:0,maxVisiblePercentage:0,totalVisibleTimeMs:0,currentVisibleTimeMs:0,isViewable:!1,viewableAt:null,enteredViewportAt:null,enterCount:0};this.metrics.set(e,o),this.isTracking.set(e,!0),this.eventQueue.set(e,[]);const a=new IntersectionObserver(d=>this.handleIntersection(e,d[0],i,n,s),{threshold:this.createThresholds()});a.observe(t),this.observers.set(e,a),this.startMonitoring(e,i,n,s);const c=setTimeout(()=>{this.endTracking(e,i,n,s)},this.config.maxTrackingDuration);this.timers.set(e,c)}stopTracking(e){this.log(`[ViewabilityTracker] Manual stop tracking for ${e}`),this.cleanup(e)}getMetrics(e){return this.metrics.get(e)}handleIntersection(e,t,i,n,s){const o=this.metrics.get(e);if(!o||!this.isTracking.get(e))return;const a=o.isViewable,c=Math.round(t.intersectionRatio*100);o.visiblePercentage=c,o.maxVisiblePercentage=Math.max(o.maxVisiblePercentage,c);const d=Date.now(),u=c>=this.config.minVisiblePercentage;if(u&&!o.enteredViewportAt&&(o.enteredViewportAt=d,o.enterCount++,this.log(`[ViewabilityTracker] ${e} entered viewport at ${d}`),this.queueEvent(e,{adId:e,sessionId:i,requestId:n,viewToken:s,eventType:"enter_viewport",visiblePercentage:c,maxVisiblePercentage:o.maxVisiblePercentage,totalVisibleTimeMs:o.totalVisibleTimeMs,isViewable:!1,timestamp:d})),!u&&o.enteredViewportAt){const h=d-o.enteredViewportAt;o.totalVisibleTimeMs+=h,o.enteredViewportAt=null,o.currentVisibleTimeMs=0,this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${o.totalVisibleTimeMs}ms`),this.queueEvent(e,{adId:e,sessionId:i,requestId:n,viewToken:s,eventType:"exit_viewport",visiblePercentage:c,maxVisiblePercentage:o.maxVisiblePercentage,totalVisibleTimeMs:o.totalVisibleTimeMs,isViewable:o.isViewable,timestamp:d})}o.isViewable!==a&&o.isViewable&&this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`),this.metrics.set(e,o)}startMonitoring(e,t,i,n){const o=setInterval(()=>{const a=this.metrics.get(e);if(!a||!this.isTracking.get(e)){clearInterval(o);return}const c=Date.now(),d=a.isViewable;if(a.enteredViewportAt){const u=c-a.enteredViewportAt;a.currentVisibleTimeMs=u,u>=this.config.minViewableDuration&&a.visiblePercentage>=this.config.minVisiblePercentage&&(a.isViewable=!0,d||(a.viewableAt=a.enteredViewportAt,this.log(`[ViewabilityTracker] ${e} BECAME VIEWABLE at ${a.viewableAt}`),this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:n,eventType:"become_viewable",visiblePercentage:a.visiblePercentage,maxVisiblePercentage:a.maxVisiblePercentage,totalVisibleTimeMs:a.totalVisibleTimeMs,isViewable:!0,timestamp:c})))}a.enteredViewportAt&&(a.totalVisibleTimeMs+=100),this.metrics.set(e,a)},100);this.timers.set(`${e}_monitoring`,o)}queueEvent(e,t){var s,o;const i=this.eventQueue.get(e);if(!i){this.log(`[ViewabilityTracker] No queue found for ${e}, skipping event`);return}i.push(t),this.log(`[ViewabilityTracker] Queued ${t.eventType} for ${e} (queue size: ${i.length})`);const n=((s=this.config.batchConfig)==null?void 0:s.maxBatchSize)??5;if(i.length>=n)this.flushQueue(e);else{const a=this.batchTimers.get(e);a&&clearTimeout(a);const c=((o=this.config.batchConfig)==null?void 0:o.maxBatchWaitMs)??1e4,d=setTimeout(()=>{this.flushQueue(e)},c);this.batchTimers.set(e,d)}}async flushQueue(e){const t=this.eventQueue.get(e);if(!t||t.length===0)return;const i=[...t];t.length=0,this.log(`[ViewabilityTracker] Flushing ${i.length} events for ${e}`);try{const n={"Content-Type":"application/json"},s=R();s&&(n["x-api-key"]=s),await fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:n,body:JSON.stringify({events:i})}),this.log(`[ViewabilityTracker] Successfully sent ${i.length} events for ${e}`)}catch(n){this.log(`[ViewabilityTracker] Failed to send events for ${e}:`,n),t.unshift(...i)}}endTracking(e,t,i,n){const s=this.metrics.get(e);if(!s)return;this.log(`[ViewabilityTracker] Ending tracking for ${e}`),this.flushQueue(e);const o=Date.now();this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:n,eventType:"end_tracking",visiblePercentage:s.visiblePercentage,maxVisiblePercentage:s.maxVisiblePercentage,totalVisibleTimeMs:s.totalVisibleTimeMs,isViewable:s.isViewable,timestamp:o}),this.flushQueue(e),this.cleanup(e)}cleanup(e){const t=this.observers.get(e);t&&(t.disconnect(),this.observers.delete(e));const i=this.timers.get(e);i&&(clearTimeout(i),this.timers.delete(e));const n=this.timers.get(`${e}_monitoring`);n&&(clearInterval(n),this.timers.delete(`${e}_monitoring`));const s=this.batchTimers.get(e);s&&(clearTimeout(s),this.batchTimers.delete(e)),this.flushQueue(e),this.isTracking.delete(e),this.metrics.delete(e),this.eventQueue.delete(e)}createThresholds(){return[0,.25,.5,.75,.9,1]}stopAll(){const e=Array.from(this.isTracking.keys());for(const t of e)this.cleanup(t)}log(...e){this.config.debug&&console.log(...e)}setupBeforeUnload(){typeof window<"u"&&window.addEventListener("beforeunload",()=>{for(const e of this.eventQueue.keys()){const t=this.eventQueue.get(e);if(t&&t.length>0){const i={"Content-Type":"application/json"},n=R();n&&(i["x-api-key"]=n),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let C=null;function re(r,e){return C||(C=new K(r,e),C.setupBeforeUnload()),C}const oe="0.1.0";l.AdManager=_,l.AnalyticsSender=A,l.ClientInfoCollector=S,l.DOMRenderer=y,l.HTMLRenderer=v,l.SDK_VERSION=oe,l.SessionManager=U,l.ViewabilityTracker=K,l.adaptAdToKoahAd=ne,l.clearClientInfoCache=ee,l.createAnalytics=W,l.createSession=Q,l.fetchAds=q,l.generateViewToken=G,l.getAppInfo=X,l.getClientInfo=b,l.getClientInfoCollector=T,l.getClientInfoJSON=te,l.getClientInfoSummary=ie,l.getDeviceInfo=J,l.getGeoInfo=Z,l.getSessionId=m,l.getUserId=Y,l.getUserInfo=O,l.getViewabilityTracker=re,l.trackAdClick=I,l.trackAdImpression=x,l.trackClicksBatch=z,l.trackImpressionsBatch=j,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
|
|
135
|
+
`}};V.trackedImpressionKeys=new Set;let y=V;class S{constructor(){this.cachedStaticInfo=null,this.cacheTimestamp=0,this.CACHE_TTL=36e5}inferAppInfo(){if(typeof window>"u")return{bundle:"unknown",ver:"1.0.0",name:"Unknown App",publisher:{id:"unknown"}};const e=this.inferBundle(),t=this.inferAppName(),i=this.inferAppVersion(),n=this.inferPublisherId();return{bundle:e,ver:i,name:t,publisher:{id:n,domain:window.location.hostname}}}inferBundle(){var t;if(typeof window>"u")return"unknown";const e=(t=window.location)==null?void 0:t.hostname;return e?e.replace(/^www\./,"").replace(/:\d+$/,""):"unknown"}inferAppName(){var n,s;if(typeof document>"u")return"Unknown App";const e=document.title;if(e&&e!=="Loading..."&&e.length<100)return e;const t=(n=document.querySelector('meta[property="og:title"]'))==null?void 0:n.getAttribute("content");if(t)return t;const i=(s=document.querySelector('meta[name="application-name"]'))==null?void 0:s.getAttribute("content");return i||"Unknown App"}inferAppVersion(){if(typeof document>"u")return"1.0.0";const e=['meta[name="version"]','meta[name="app-version"]','meta[name="application-version"]','link[rel="manifest"]'];for(const t of e){const i=document.querySelector(t);if(i){if(t.includes("manifest"))return"1.0.0";const n=i.getAttribute("content");if(n)return n}}return"1.0.0"}inferPublisherId(){var i,n;if(typeof document>"u")return"unknown";const e=(i=document.querySelector('meta[name="publisher-id"]'))==null?void 0:i.getAttribute("content");if(e)return e;const t=(n=window.location)==null?void 0:n.hostname;return t?t.replace(/\./g,"_"):"unknown"}static getInstance(){return this.instance||(this.instance=new S),this.instance}collect(e){const t=Date.now();(!this.cachedStaticInfo||t-this.cacheTimestamp>this.CACHE_TTL)&&(this.cachedStaticInfo=this.collectStaticInfo(),this.cacheTimestamp=t);const i=this.collectDynamicInfo();return{device:{ua:this.cachedStaticInfo.ua,os:this.cachedStaticInfo.os,osv:this.cachedStaticInfo.osv,devicetype:this.cachedStaticInfo.devicetype,model:this.cachedStaticInfo.model,make:this.cachedStaticInfo.make,language:this.cachedStaticInfo.language,connectiontype:i.connectiontype??0,bandwidth:i.bandwidth,...(e==null?void 0:e.device)||{}},app:{...this.inferAppInfo(),...(e==null?void 0:e.app)||{}},user:{...this.collectUserInfo(),...(e==null?void 0:e.user)||{}},geo:{...this.collectGeoInfo(),...(e==null?void 0:e.geo)||{}},timestamp:t}}collectStaticInfo(){if(typeof navigator>"u")return this.getFallbackInfo();const e=navigator.userAgent,t=this.detectOS(e),i=this.detectOSVersion(e);return{ua:e,os:t,osv:i,devicetype:this.detectDeviceType(e),model:this.detectModel(e),make:this.detectMake(e),language:navigator.language||"en-US"}}collectDynamicInfo(){if(typeof navigator>"u")return{connectiontype:0};const e=this.getConnectionInfo();return{connectiontype:(e==null?void 0:e.type)||0,bandwidth:e==null?void 0:e.downlink}}collectUserInfo(){let e;try{e=localStorage.getItem("actionx_user_id")||"",e||(e=this.generateUUID(),localStorage.setItem("actionx_user_id",e))}catch{e=this.generateUUID()}return{id:e,language:(navigator==null?void 0:navigator.language)||"en-US"}}collectGeoInfo(){if(typeof navigator>"u"||typeof Intl>"u")return{};const e=Intl.DateTimeFormat().resolvedOptions().timeZone,t=navigator.language;return{country:this.inferCountry(t,e),timezone:e}}detectOS(e){return e?/iPad|iPhone|iPod/.test(e)?"iOS":/Android/.test(e)?"Android":/Windows/.test(e)?"Windows":/Macintosh|Mac OS/.test(e)?"macOS":/Linux/.test(e)?"Linux":/CrOS/.test(e)?"Chrome OS":"Unknown":"Unknown"}detectOSVersion(e){if(!e)return"Unknown";const t=e.match(/OS (\d+)_(\d+)_?(\d+)?/);if(t)return`${t[1]}.${t[2]}${t[3]?"."+t[3]:""}`;const i=e.match(/Android (\d+)\.(\d+)/);if(i)return`${i[1]}.${i[2]}`;const n=e.match(/Windows NT (\d+\.\d+)/);if(n)return n[1];const s=e.match(/Mac OS X (\d+[._]\d+)/);return s?s[1].replace("_","."):"Unknown"}detectDeviceType(e){return e?/iPad|Tablet/i.test(e)||/Android/.test(e)&&!/Mobile/.test(e)?2:/iPhone|iPod|Mobile|Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(e)?1:/Windows|Macintosh|Linux|x86_64/i.test(e)?3:/SmartTV|TV|WebTV|GoogleTV|AppleTV/i.test(e)?4:0:0}detectModel(e){if(!e)return;const t=e.match(/iPhone(?:;\s*[^\)]*)?\s*(\d+,\d)?/);if(t)return`iPhone${t[1]||""}`;if(/iPad/.test(e))return"iPad";const i=e.match(/Samsung-.*(SM-\w+)/);if(i)return i[1];if(/Pixel/.test(e))return"Google Pixel";const n=e.match(/(BARC-|HUAWEI-)?([A-Z]{2}\-\w{4})/);if(n)return n[2];const s=e.match(/(MI|Redmi|POCO)\s([\w\s]+)/);if(s)return s[1]+" "+s[2];if(/OnePlus/.test(e)){const r=e.match(/OnePlus\s([A-Z\d]+)/);return r?"OnePlus "+r[1]:"OnePlus"}}detectMake(e){if(e){if(/iPad|iPhone|iPod/.test(e))return"Apple";if(/Samsung/.test(e))return"Samsung";if(/Pixel/.test(e))return"Google";if(/Huawei|HONOR/.test(e))return"Huawei";if(/Xiaomi|Redmi|POCO/.test(e))return"Xiaomi";if(/OnePlus/.test(e))return"OnePlus";if(/Sony/.test(e))return"Sony";if(/LG/.test(e))return"LG";if(/Motorola|Moto/.test(e))return"Motorola";if(/Nokia/.test(e))return"Nokia";if(/HTC/.test(e))return"HTC";if(/OPPO/.test(e))return"OPPO";if(/vivo/.test(e))return"vivo";if(/Realme/.test(e))return"Realme"}}getConnectionInfo(){if(typeof navigator>"u")return null;const e=navigator,t=e.connection||e.mozConnection||e.webkitConnection;return t?{type:this.mapConnectionType(t.effectiveType),downlink:t.downlink,rtt:t.rtt}:null}mapConnectionType(e){if(!e)return 0;switch(e.toLowerCase()){case"slow-2g":case"2g":return 3;case"3g":return 4;case"4g":return 5;default:return 0}}inferCountry(e,t){if(!e&&!t)return;const i={"Asia/Shanghai":"CN","Asia/Hong_Kong":"HK","Asia/Taipei":"TW","Asia/Tokyo":"JP","Asia/Seoul":"KR","Asia/Singapore":"SG","Asia/Dubai":"AE","Asia/Kolkata":"IN","Asia/Jakarta":"ID","Asia/Manila":"PH","Asia/Bangkok":"TH","Asia/Kuala_Lumpur":"MY","America/New_York":"US","America/Chicago":"US","America/Denver":"US","America/Los_Angeles":"US","America/Toronto":"CA","America/Vancouver":"CA","America/Mexico_City":"MX","America/Sao_Paulo":"BR","America/Buenos_Aires":"AR","Europe/London":"GB","Europe/Paris":"FR","Europe/Berlin":"DE","Europe/Madrid":"ES","Europe/Rome":"IT","Europe/Amsterdam":"NL","Europe/Brussels":"BE","Europe/Zurich":"CH","Europe/Vienna":"AT","Europe/Stockholm":"SE","Europe/Oslo":"NO","Europe/Copenhagen":"DK","Europe/Helsinki":"FI","Europe/Dublin":"IE","Europe/Lisbon":"PT","Europe/Athens":"GR","Europe/Prague":"CZ","Europe/Warsaw":"PL","Europe/Budapest":"HU","Europe/Bucharest":"RO","Australia/Sydney":"AU","Pacific/Auckland":"NZ"};if(t&&i[t])return i[t];if(e){const n=e.split("-")[1];if(n)return n.toUpperCase()}}generateUUID(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}getFallbackInfo(){return{ua:"Unknown",os:"Unknown",osv:"Unknown",devicetype:0,language:"en-US"}}clearCache(){this.cachedStaticInfo=null,this.cacheTimestamp=0}}const T=()=>S.getInstance();function b(o){return T().collect(o)}function X(){return b().device}function O(){return b().user}function Z(){return b().app}function Y(){return b().geo}function B(){return O().id}function ee(){T().clearCache()}function te(o){const e=b(o);return JSON.stringify(e,null,2)}function ie(){var t;const o=b();return[o.device.os,o.device.osv,o.app.name,o.user.id.slice(0,8)+"...",((t=o.geo)==null?void 0:t.country)||"Unknown"].join(" / ")}function q(){var o;return typeof window<"u"?!!((o=window.__AD_CONFIG__)!=null&&o.debug):!1}function ne(o){var e,t;return{id:o.original.id,type:o.original.type,score:o.original.score,source:"internal",content:{title:o.adapted.title,body:o.adapted.body,image:(e=o.adapted.image)==null?void 0:e.url,cta_text:o.adapted.ctaText,price:(t=o.adapted.price)==null?void 0:t.display,rating:o.adapted.rating,brand:o.adapted.brand},tracking:{click_url:o.tracking.clickUrl||o.tracking.click_url||"",impression_url:o.tracking.impressionUrl||o.tracking.impression_url||""},metadata:{viewToken:o.tracking.viewToken||o.tracking.view_token,styling:o.adapted.styling}}}async function L(o,e={}){const t=e.apiBaseUrl||"/api/v1",n=T().collect(),s={...o,clientInfo:n},r={"Content-Type":"application/json"};e.apiKey&&(r["X-API-Key"]=e.apiKey),q()&&(console.log("[fetchAds] Request URL:",`${t}/ads/request`),console.log("[fetchAds] Request params:",o),console.log("[fetchAds] Request body:",s));const a=await fetch(`${t}/ads/request`,{method:"POST",headers:r,body:JSON.stringify(s)});if(!a.ok)throw new Error(`Ad request failed: ${a.status} ${a.statusText}`);const c=await a.json();if(q()&&console.log("[fetchAds] Response:",c),!c.success)throw new Error(c.error||"Ad request failed");return c.data}const se={width:400,height:200},M={variant:"horizontal",count:1,preferences:{}};function R(o={}){const e={variant:o.variant??M.variant,count:o.count??M.count,preferences:{...M.preferences,...o.preferences}};return[{slotId:"action_card",slotName:"Action Card",format:"action_card",variant:e.variant,size:se,count:e.count,preferences:e.preferences,placement:{position:"below_fold",context:"post_response"}}]}const p=class p{constructor(e){this.slots={},this.isLoading=!1,this.currentRequestId=null,this.adsAnalyticsMap=new Map,this.config=e,this.enabled=e.enabled!==!1,this.eventBus=new k,this.slots_config=R(e.cardOption),typeof window<"u"&&(window.__AD_CONFIG__={...window.__AD_CONFIG__||{},apiKey:e.apiKey,apiBaseUrl:e.apiBaseUrl,debug:!!e.debug}),this.config.debug&&console.log("[AdManager] Initialized with config:",{apiBaseUrl:e.apiBaseUrl,hasApiKey:!!e.apiKey,enabled:this.enabled})}async requestAds(e){const t="conversationContext"in e?e:{conversationContext:e};this.currentUserId=this.resolveUserId(t.userContext);const i=this.buildRequestKey(t),n=p.inFlightRequests.get(i);if(n)return this.config.debug&&console.log("[AdManager] Reusing in-flight request for identical context"),n;if(!this.enabled)throw this.config.debug&&console.warn("[AdManager] Ads are disabled, skipping request"),new Error("Ads are disabled");if(this.isLoading)throw this.config.debug&&console.warn("[AdManager] Request already in progress"),new Error("Request already in progress");this.isLoading=!0,this.eventBus.emit("adsLoading"),this.config.debug&&console.log("[AdManager] Starting ad request...");const s=(async()=>{try{const r=await L({conversationContext:t.conversationContext,userContext:t.userContext?{...t.userContext,userId:t.userContext.userId??this.currentUserId,sessionId:t.userContext.sessionId??m()}:{userId:this.currentUserId,sessionId:m()},slots:this.slots_config},{apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey});this.currentRequestId=r.requestId,this.adsAnalyticsMap.clear(),r.slots.forEach(c=>{c.ads.forEach((u,d)=>{const h=u.original.id;this.adsAnalyticsMap.set(h,{requestId:r.requestId,slotId:c.slotId,position:d,totalAds:c.ads.length,apiBaseUrl:this.config.apiBaseUrl,userId:this.currentUserId})})});const a={};return r.slots.forEach(c=>{a[c.slotId]=c}),this.slots=a,this.eventBus.emit("adsUpdated",this.slots),this.config.debug&&console.log("[AdManager] Ads received:",{slotCount:Object.keys(this.slots).length,slots:Object.keys(this.slots)}),r}catch(r){const a=r;throw this.eventBus.emit("adsError",a),a}finally{this.isLoading=!1,p.inFlightRequests.delete(i)}})();return p.inFlightRequests.set(i,s),s}buildRequestKey(e){return JSON.stringify({apiBaseUrl:this.config.apiBaseUrl,apiKey:this.config.apiKey,conversationContext:e.conversationContext,userContext:e.userContext||null,slots:this.slots_config})}resolveUserId(e){if(e!=null&&e.userId)return e.userId;try{return B()}catch{return}}render(e,t,i={}){const n=typeof e!="string",s=n?p.DEFAULT_SLOT_ID:e,r=n?e:t,a=(n?t:i)||{};if(!r)return this.config.debug&&console.warn("[AdManager] Render container is required"),null;const c=this.slots[s];if(!c||!c.ads||c.ads.length===0)return this.config.debug&&console.warn("[AdManager] No ads in slot:",s),null;const u=a.adIndex??0,d=c.ads[u];if(!d)return this.config.debug&&console.warn(`[AdManager] Ad at index ${u} not found in slot:`,s),null;const h=this.adsAnalyticsMap.get(d.original.id),f=this.slots_config.find($=>$.slotId===s),w=(f==null?void 0:f.format)||"action_card";r.innerHTML="";const g={analytics:h,variant:a.variant,onClick:a.onClick,onImpression:a.onImpression};return w==="suffix"?y.renderSuffixAd(d,r,g):w==="source"?y.renderSponsoredSource(d,r,g):w==="lead_gen"?y.renderLeadGenAd(d,r,g):y.renderActionCard(d,r,g)}getSlots(e){return e?this.slots[e]:this.slots}hasAds(e){var i;const t=this.slots[e];return(t==null?void 0:t.status)==="filled"&&((i=t.ads)==null?void 0:i.length)>0}getAds(e){var t;return((t=this.slots[e])==null?void 0:t.ads)||[]}getLoadingStatus(){return this.isLoading}setEnabled(e){this.enabled=e,this.config.debug&&console.log("[AdManager] Ads",e?"enabled":"disabled")}updateConfig(e){this.config={...this.config,...e},e.cardOption!==void 0&&(this.slots_config=R(this.config.cardOption)),this.config.debug&&console.log("[AdManager] Config updated:",e)}clearSlots(){this.slots={},this.currentRequestId=null,this.adsAnalyticsMap.clear(),this.config.debug&&console.log("[AdManager] Slots and analytics cleared")}on(e,t){this.eventBus.on(e,t)}off(e,t){this.eventBus.off(e,t)}removeAllListeners(e){this.eventBus.removeAllListeners(e)}getCurrentRequestId(){return this.currentRequestId}getAdAnalytics(e){return this.adsAnalyticsMap.get(e)||null}getAdsAnalytics(e){const t=new Map;return e.forEach(i=>{const n=this.adsAnalyticsMap.get(i);n&&t.set(i,n)}),t}async trackAdClick(e,t,i){const n=this.adsAnalyticsMap.get(e);if(!n||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const s=m(),r=await x({requestId:this.currentRequestId,adId:e,destinationUrl:t,sessionId:s,slotId:n.slotId,adTitle:i==null?void 0:i.title,format:i==null?void 0:i.format,source:i==null?void 0:i.source,userId:n.userId??this.currentUserId},{baseUrl:n.apiBaseUrl});return r&&(this.eventBus.emit("adClicked",e,n.slotId),this.config.debug&&console.log("[AdManager] Click tracked via Analytics API:",r.eventId)),r}async trackAdImpressionAPI(e,t){const i=this.adsAnalyticsMap.get(e);if(!i||!this.currentRequestId)return this.config.debug&&console.warn("[AdManager] No analytics info for ad:",e),null;const n=m(),s=await I({requestId:this.currentRequestId,adId:e,slotId:i.slotId,position:i.position,totalAds:i.totalAds,sessionId:n,adTitle:t==null?void 0:t.title,format:t==null?void 0:t.format,source:t==null?void 0:t.source,viewToken:t==null?void 0:t.viewToken,userId:i.userId??this.currentUserId},{baseUrl:i.apiBaseUrl});return s&&(this.eventBus.emit("adImpression",e,i.slotId),this.config.debug&&console.log("[AdManager] Impression tracked via Analytics API:",s.eventId)),s}destroy(){this.removeAllListeners(),this.clearSlots(),this.config.debug&&console.log("[AdManager] Destroyed")}};p.DEFAULT_SLOT_ID="action_card",p.inFlightRequests=new Map;let _=p;function K(){if(typeof window<"u"){const o=window.__AD_CONFIG__;if(o!=null&&o.apiKey)return o.apiKey}}class N{constructor(e={},t="/api/v1"){var i,n;this.observers=new Map,this.metrics=new Map,this.timers=new Map,this.batchTimers=new Map,this.eventQueue=new Map,this.isTracking=new Map,this.config={minVisiblePercentage:e.minVisiblePercentage??50,minViewableDuration:e.minViewableDuration??1e3,maxTrackingDuration:e.maxTrackingDuration??6e4,batchConfig:{maxBatchSize:((i=e.batchConfig)==null?void 0:i.maxBatchSize)??5,maxBatchWaitMs:((n=e.batchConfig)==null?void 0:n.maxBatchWaitMs)??1e4},debug:e.debug??!1},this.baseUrl=t}startTracking(e,t,i,n,s){if(this.isTracking.get(e)){this.log(`[ViewabilityTracker] Already tracking ${e}, skipping`);return}this.log(`[ViewabilityTracker] Starting tracking for ${e}`);const r={visiblePercentage:0,maxVisiblePercentage:0,totalVisibleTimeMs:0,currentVisibleTimeMs:0,isViewable:!1,viewableAt:null,enteredViewportAt:null,enterCount:0};this.metrics.set(e,r),this.isTracking.set(e,!0),this.eventQueue.set(e,[]);const a=new IntersectionObserver(u=>this.handleIntersection(e,u[0],i,n,s),{threshold:this.createThresholds()});a.observe(t),this.observers.set(e,a),this.startMonitoring(e,i,n,s);const c=setTimeout(()=>{this.endTracking(e,i,n,s)},this.config.maxTrackingDuration);this.timers.set(e,c)}stopTracking(e){this.log(`[ViewabilityTracker] Manual stop tracking for ${e}`),this.cleanup(e)}getMetrics(e){return this.metrics.get(e)}handleIntersection(e,t,i,n,s){const r=this.metrics.get(e);if(!r||!this.isTracking.get(e))return;const a=r.isViewable,c=Math.round(t.intersectionRatio*100);r.visiblePercentage=c,r.maxVisiblePercentage=Math.max(r.maxVisiblePercentage,c);const u=Date.now(),d=c>=this.config.minVisiblePercentage;if(d&&!r.enteredViewportAt&&(r.enteredViewportAt=u,r.enterCount++,this.log(`[ViewabilityTracker] ${e} entered viewport at ${u}`),this.queueEvent(e,{adId:e,sessionId:i,requestId:n,viewToken:s,eventType:"enter_viewport",visiblePercentage:c,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:!1,timestamp:u})),!d&&r.enteredViewportAt){const h=u-r.enteredViewportAt;r.totalVisibleTimeMs+=h,r.enteredViewportAt=null,r.currentVisibleTimeMs=0,this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${r.totalVisibleTimeMs}ms`),this.queueEvent(e,{adId:e,sessionId:i,requestId:n,viewToken:s,eventType:"exit_viewport",visiblePercentage:c,maxVisiblePercentage:r.maxVisiblePercentage,totalVisibleTimeMs:r.totalVisibleTimeMs,isViewable:r.isViewable,timestamp:u})}r.isViewable!==a&&r.isViewable&&this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`),this.metrics.set(e,r)}startMonitoring(e,t,i,n){const r=setInterval(()=>{const a=this.metrics.get(e);if(!a||!this.isTracking.get(e)){clearInterval(r);return}const c=Date.now(),u=a.isViewable;if(a.enteredViewportAt){const d=c-a.enteredViewportAt;a.currentVisibleTimeMs=d,d>=this.config.minViewableDuration&&a.visiblePercentage>=this.config.minVisiblePercentage&&(a.isViewable=!0,u||(a.viewableAt=a.enteredViewportAt,this.log(`[ViewabilityTracker] ${e} BECAME VIEWABLE at ${a.viewableAt}`),this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:n,eventType:"become_viewable",visiblePercentage:a.visiblePercentage,maxVisiblePercentage:a.maxVisiblePercentage,totalVisibleTimeMs:a.totalVisibleTimeMs,isViewable:!0,timestamp:c})))}a.enteredViewportAt&&(a.totalVisibleTimeMs+=100),this.metrics.set(e,a)},100);this.timers.set(`${e}_monitoring`,r)}queueEvent(e,t){var s,r;const i=this.eventQueue.get(e);if(!i){this.log(`[ViewabilityTracker] No queue found for ${e}, skipping event`);return}i.push(t),this.log(`[ViewabilityTracker] Queued ${t.eventType} for ${e} (queue size: ${i.length})`);const n=((s=this.config.batchConfig)==null?void 0:s.maxBatchSize)??5;if(i.length>=n)this.flushQueue(e);else{const a=this.batchTimers.get(e);a&&clearTimeout(a);const c=((r=this.config.batchConfig)==null?void 0:r.maxBatchWaitMs)??1e4,u=setTimeout(()=>{this.flushQueue(e)},c);this.batchTimers.set(e,u)}}async flushQueue(e){const t=this.eventQueue.get(e);if(!t||t.length===0)return;const i=[...t];t.length=0,this.log(`[ViewabilityTracker] Flushing ${i.length} events for ${e}`);try{const n={"Content-Type":"application/json"},s=K();s&&(n["x-api-key"]=s),await fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:n,body:JSON.stringify({events:i})}),this.log(`[ViewabilityTracker] Successfully sent ${i.length} events for ${e}`)}catch(n){this.log(`[ViewabilityTracker] Failed to send events for ${e}:`,n),t.unshift(...i)}}endTracking(e,t,i,n){const s=this.metrics.get(e);if(!s)return;this.log(`[ViewabilityTracker] Ending tracking for ${e}`),this.flushQueue(e);const r=Date.now();this.queueEvent(e,{adId:e,sessionId:t,requestId:i,viewToken:n,eventType:"end_tracking",visiblePercentage:s.visiblePercentage,maxVisiblePercentage:s.maxVisiblePercentage,totalVisibleTimeMs:s.totalVisibleTimeMs,isViewable:s.isViewable,timestamp:r}),this.flushQueue(e),this.cleanup(e)}cleanup(e){const t=this.observers.get(e);t&&(t.disconnect(),this.observers.delete(e));const i=this.timers.get(e);i&&(clearTimeout(i),this.timers.delete(e));const n=this.timers.get(`${e}_monitoring`);n&&(clearInterval(n),this.timers.delete(`${e}_monitoring`));const s=this.batchTimers.get(e);s&&(clearTimeout(s),this.batchTimers.delete(e)),this.flushQueue(e),this.isTracking.delete(e),this.metrics.delete(e),this.eventQueue.delete(e)}createThresholds(){return[0,.25,.5,.75,.9,1]}stopAll(){const e=Array.from(this.isTracking.keys());for(const t of e)this.cleanup(t)}log(...e){this.config.debug&&console.log(...e)}setupBeforeUnload(){typeof window<"u"&&window.addEventListener("beforeunload",()=>{for(const e of this.eventQueue.keys()){const t=this.eventQueue.get(e);if(t&&t.length>0){const i={"Content-Type":"application/json"},n=K();n&&(i["x-api-key"]=n),fetch(`${this.baseUrl}/ads/viewability/batch`,{method:"POST",headers:i,keepalive:!0,body:JSON.stringify({events:t})})}}})}}let C=null;function re(o,e){return C||(C=new N(o,e),C.setupBeforeUnload()),C}const oe="0.1.0";l.AdManager=_,l.AnalyticsSender=A,l.ClientInfoCollector=S,l.DOMRenderer=y,l.HTMLRenderer=v,l.SDK_VERSION=oe,l.SessionManager=U,l.ViewabilityTracker=N,l.adaptAdToKoahAd=ne,l.clearClientInfoCache=ee,l.createAnalytics=Q,l.createSession=J,l.fetchAds=L,l.generateViewToken=j,l.getAppInfo=Z,l.getClientInfo=b,l.getClientInfoCollector=T,l.getClientInfoJSON=te,l.getClientInfoSummary=ie,l.getDeviceInfo=X,l.getGeoInfo=Y,l.getSessionId=m,l.getUserId=B,l.getUserInfo=O,l.getViewabilityTracker=re,l.trackAdClick=x,l.trackAdImpression=I,l.trackClicksBatch=W,l.trackImpressionsBatch=z,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
|