@action-x/ad-sdk 0.1.10 → 0.1.12
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 +30 -28
- package/dist/index.d.ts +7 -0
- package/dist/index.js +324 -281
- package/dist/index.umd.js +16 -14
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class
|
|
1
|
+
class q {
|
|
2
2
|
constructor() {
|
|
3
3
|
this.events = {};
|
|
4
4
|
}
|
|
@@ -23,8 +23,8 @@ class B {
|
|
|
23
23
|
this.events[e] && this.events[e].forEach((i) => {
|
|
24
24
|
try {
|
|
25
25
|
i(...t);
|
|
26
|
-
} catch (
|
|
27
|
-
console.error(`[EventEmitter] Error in event handler for "${e}":`,
|
|
26
|
+
} catch (n) {
|
|
27
|
+
console.error(`[EventEmitter] Error in event handler for "${e}":`, n);
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
30
|
}
|
|
@@ -42,7 +42,10 @@ class B {
|
|
|
42
42
|
return ((t = this.events[e]) == null ? void 0 : t.length) || 0;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
function m(o) {
|
|
46
|
+
return o.replace(/<[^>]+>/g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, " ").replace(/\s{2,}/g, " ").trim();
|
|
47
|
+
}
|
|
48
|
+
class v {
|
|
46
49
|
static getClickUrl(e) {
|
|
47
50
|
return e.tracking.clickUrl || e.tracking.click_url || "#";
|
|
48
51
|
}
|
|
@@ -61,27 +64,29 @@ class w {
|
|
|
61
64
|
*/
|
|
62
65
|
static renderActionCard(e, t = {}) {
|
|
63
66
|
var f;
|
|
64
|
-
const { variant: i = "horizontal", includeWrapper:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
const { variant: i = "horizontal", includeWrapper: n = !0 } = t, s = e.adapted, r = this.getClickUrl(e), a = this.getImpressionUrl(e), c = (f = s.image) != null && f.url ? `<div class="ax-ad-image-wrap">
|
|
68
|
+
<img
|
|
69
|
+
src="${s.image.url}"
|
|
70
|
+
alt="${s.title}"
|
|
71
|
+
class="ax-ad-image"
|
|
72
|
+
loading="lazy"
|
|
73
|
+
/>
|
|
74
|
+
</div>` : "", d = (s.brand || "").trim(), l = d && d.toLowerCase() !== "unknown" ? `<span class="ax-ad-brand">${s.brand}</span>` : "", u = `
|
|
70
75
|
${c}
|
|
71
76
|
<div class="ax-ad-content">
|
|
72
77
|
${l}
|
|
73
|
-
<h3 class="ax-ad-title">${
|
|
74
|
-
${
|
|
78
|
+
<h3 class="ax-ad-title">${s.title}</h3>
|
|
79
|
+
${s.body ? `<p class="ax-ad-body">${m(s.body)}</p>` : ""}
|
|
75
80
|
</div>
|
|
76
|
-
`, h =
|
|
81
|
+
`, h = n ? `<a
|
|
77
82
|
href="${r}"
|
|
78
83
|
class="ax-ad-card ax-ad-card-${i} ax-ad-card-link"
|
|
79
84
|
target="_blank"
|
|
80
85
|
rel="sponsored noopener noreferrer"
|
|
81
86
|
data-ad-id="${e.original.id}"
|
|
82
87
|
data-impression-url="${a}"
|
|
83
|
-
>` : "", p =
|
|
84
|
-
return h +
|
|
88
|
+
>` : "", p = n ? "</a>" : "";
|
|
89
|
+
return h + u + p;
|
|
85
90
|
}
|
|
86
91
|
/**
|
|
87
92
|
* Render Suffix ad as HTML string
|
|
@@ -90,19 +95,19 @@ class w {
|
|
|
90
95
|
* @returns HTML string
|
|
91
96
|
*/
|
|
92
97
|
static renderSuffixAd(e) {
|
|
93
|
-
const t = e.adapted, i = this.getClickUrl(e),
|
|
98
|
+
const t = e.adapted, i = this.getClickUrl(e), n = this.getImpressionUrl(e);
|
|
94
99
|
return `
|
|
95
100
|
<div class="ax-ad-suffix" data-ad-id="${e.original.id}">
|
|
96
101
|
<div class="ax-ad-suffix-content">
|
|
97
102
|
${t.title ? `<h4 class="ax-ad-suffix-title">${t.title}</h4>` : ""}
|
|
98
|
-
${t.body ? `<p class="ax-ad-suffix-body">${t.body}</p>` : ""}
|
|
103
|
+
${t.body ? `<p class="ax-ad-suffix-body">${m(t.body)}</p>` : ""}
|
|
99
104
|
<a
|
|
100
105
|
href="${i}"
|
|
101
106
|
class="ax-ad-suffix-link"
|
|
102
107
|
target="_blank"
|
|
103
108
|
rel="sponsored noopener noreferrer"
|
|
104
109
|
data-ad-id="${e.original.id}"
|
|
105
|
-
data-impression-url="${
|
|
110
|
+
data-impression-url="${n}"
|
|
106
111
|
>
|
|
107
112
|
${this.getCtaText(e)}
|
|
108
113
|
</a>
|
|
@@ -117,8 +122,8 @@ class w {
|
|
|
117
122
|
* @returns HTML string
|
|
118
123
|
*/
|
|
119
124
|
static renderSponsoredSource(e) {
|
|
120
|
-
var
|
|
121
|
-
const t = e.adapted, i = this.getClickUrl(e),
|
|
125
|
+
var s;
|
|
126
|
+
const t = e.adapted, i = this.getClickUrl(e), n = this.getImpressionUrl(e);
|
|
122
127
|
return `
|
|
123
128
|
<div class="ax-ad-source" data-ad-id="${e.original.id}">
|
|
124
129
|
<a
|
|
@@ -127,12 +132,12 @@ class w {
|
|
|
127
132
|
target="_blank"
|
|
128
133
|
rel="sponsored noopener noreferrer"
|
|
129
134
|
data-ad-id="${e.original.id}"
|
|
130
|
-
data-impression-url="${
|
|
135
|
+
data-impression-url="${n}"
|
|
131
136
|
>
|
|
132
|
-
${(
|
|
137
|
+
${(s = t.image) != null && s.url ? `<img src="${t.image.url}" alt="" class="ax-ad-source-icon" />` : ""}
|
|
133
138
|
<div class="ax-ad-source-info">
|
|
134
139
|
<span class="ax-ad-source-name">${t.title}</span>
|
|
135
|
-
${t.body ? `<span class="ax-ad-source-desc">${t.body}</span>` : ""}
|
|
140
|
+
${t.body ? `<span class="ax-ad-source-desc">${m(t.body)}</span>` : ""}
|
|
136
141
|
</div>
|
|
137
142
|
<span class="ax-ad-source-badge">Sponsored</span>
|
|
138
143
|
</a>
|
|
@@ -146,19 +151,19 @@ class w {
|
|
|
146
151
|
* @returns HTML string
|
|
147
152
|
*/
|
|
148
153
|
static renderLeadGenAd(e) {
|
|
149
|
-
const t = e.adapted, i = this.getClickUrl(e),
|
|
154
|
+
const t = e.adapted, i = this.getClickUrl(e), n = this.getImpressionUrl(e);
|
|
150
155
|
return `
|
|
151
156
|
<div class="ax-ad-leadgen" data-ad-id="${e.original.id}">
|
|
152
157
|
<div class="ax-ad-leadgen-content">
|
|
153
158
|
${t.title ? `<h3 class="ax-ad-leadgen-title">${t.title}</h3>` : ""}
|
|
154
|
-
${t.body ? `<p class="ax-ad-leadgen-body">${t.body}</p>` : ""}
|
|
159
|
+
${t.body ? `<p class="ax-ad-leadgen-body">${m(t.body)}</p>` : ""}
|
|
155
160
|
<a
|
|
156
161
|
href="${i}"
|
|
157
162
|
class="ax-ad-leadgen-cta"
|
|
158
163
|
target="_blank"
|
|
159
164
|
rel="sponsored noopener noreferrer"
|
|
160
165
|
data-ad-id="${e.original.id}"
|
|
161
|
-
data-impression-url="${
|
|
166
|
+
data-impression-url="${n}"
|
|
162
167
|
>
|
|
163
168
|
${this.getCtaText(e)}
|
|
164
169
|
</a>
|
|
@@ -173,13 +178,13 @@ class w {
|
|
|
173
178
|
* @param renderFn - Render function to use
|
|
174
179
|
* @returns HTML string with all ads
|
|
175
180
|
*/
|
|
176
|
-
static renderAds(e, t =
|
|
181
|
+
static renderAds(e, t = v.renderActionCard.bind(v)) {
|
|
177
182
|
return e.map(t).join(`
|
|
178
183
|
`);
|
|
179
184
|
}
|
|
180
185
|
}
|
|
181
|
-
const
|
|
182
|
-
function
|
|
186
|
+
const L = "ad_session_id";
|
|
187
|
+
function K() {
|
|
183
188
|
if (typeof window < "u") {
|
|
184
189
|
const o = window.__AD_CONFIG__;
|
|
185
190
|
if (o != null && o.apiKey)
|
|
@@ -190,7 +195,7 @@ function R() {
|
|
|
190
195
|
var o;
|
|
191
196
|
return typeof window < "u" ? !!((o = window.__AD_CONFIG__) != null && o.debug) : !1;
|
|
192
197
|
}
|
|
193
|
-
function
|
|
198
|
+
function U(o) {
|
|
194
199
|
if (o) return o;
|
|
195
200
|
if (typeof window < "u") {
|
|
196
201
|
const e = window.__AD_CONFIG__;
|
|
@@ -199,9 +204,9 @@ function C(o) {
|
|
|
199
204
|
}
|
|
200
205
|
return "/api/v1";
|
|
201
206
|
}
|
|
202
|
-
class
|
|
207
|
+
class V {
|
|
203
208
|
constructor(e = {}) {
|
|
204
|
-
this.memoryCache = /* @__PURE__ */ new Map(), this.sessionKey = e.sessionKey ||
|
|
209
|
+
this.memoryCache = /* @__PURE__ */ new Map(), this.sessionKey = e.sessionKey || L, this.storage = e.storage || "sessionStorage", (typeof window > "u" || typeof window.sessionStorage > "u") && (this.storage = "memory");
|
|
205
210
|
}
|
|
206
211
|
/**
|
|
207
212
|
* 获取 Session ID
|
|
@@ -235,7 +240,7 @@ class E {
|
|
|
235
240
|
this.storage === "sessionStorage" ? sessionStorage.removeItem(this.sessionKey) : this.memoryCache.delete(this.sessionKey);
|
|
236
241
|
}
|
|
237
242
|
}
|
|
238
|
-
const
|
|
243
|
+
const N = new V(), w = () => N.getSessionId();
|
|
239
244
|
class x {
|
|
240
245
|
constructor(e = {}) {
|
|
241
246
|
this.explicitBaseUrl = e.baseUrl, this.timeout = e.timeout || 1e4, this.retryAttempts = e.retryAttempts ?? 2, this.retryDelay = e.retryDelay || 1e3, this.debug = e.debug || !1;
|
|
@@ -245,7 +250,7 @@ class x {
|
|
|
245
250
|
*/
|
|
246
251
|
async trackImpression(e) {
|
|
247
252
|
return this.sendRequest(
|
|
248
|
-
`${
|
|
253
|
+
`${U(this.explicitBaseUrl)}/ads/impression`,
|
|
249
254
|
e,
|
|
250
255
|
"impression"
|
|
251
256
|
);
|
|
@@ -255,7 +260,7 @@ class x {
|
|
|
255
260
|
*/
|
|
256
261
|
async trackClick(e) {
|
|
257
262
|
return this.sendRequest(
|
|
258
|
-
`${
|
|
263
|
+
`${U(this.explicitBaseUrl)}/ads/click`,
|
|
259
264
|
e,
|
|
260
265
|
"click"
|
|
261
266
|
);
|
|
@@ -265,31 +270,31 @@ class x {
|
|
|
265
270
|
* 自动添加 X-API-Key header
|
|
266
271
|
*/
|
|
267
272
|
async sendRequest(e, t, i) {
|
|
268
|
-
let
|
|
269
|
-
for (let
|
|
273
|
+
let n = null;
|
|
274
|
+
for (let s = 0; s <= this.retryAttempts; s++) {
|
|
270
275
|
const r = this.debug || R();
|
|
271
276
|
try {
|
|
272
|
-
r && (console.log(`[Analytics] Request URL (${i}):`, e), console.log(`[Analytics] Sending ${i} event (attempt ${
|
|
273
|
-
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 ${s + 1}):`, t));
|
|
278
|
+
const a = new AbortController(), c = setTimeout(() => a.abort(), this.timeout), d = {
|
|
274
279
|
"Content-Type": "application/json"
|
|
275
|
-
}, l =
|
|
276
|
-
l && (
|
|
277
|
-
const
|
|
280
|
+
}, l = K();
|
|
281
|
+
l && (d["X-API-Key"] = l, r && console.log(`[Analytics] Adding X-API-Key header for ${i} event`));
|
|
282
|
+
const u = await fetch(e, {
|
|
278
283
|
method: "POST",
|
|
279
|
-
headers:
|
|
284
|
+
headers: d,
|
|
280
285
|
body: JSON.stringify(t),
|
|
281
286
|
keepalive: !0,
|
|
282
287
|
signal: a.signal
|
|
283
288
|
});
|
|
284
|
-
if (clearTimeout(c), !
|
|
285
|
-
throw new Error(`HTTP ${
|
|
286
|
-
const h = await
|
|
289
|
+
if (clearTimeout(c), !u.ok)
|
|
290
|
+
throw new Error(`HTTP ${u.status}: ${u.statusText}`);
|
|
291
|
+
const h = await u.json();
|
|
287
292
|
return r && console.log(`[Analytics] ${i} event tracked successfully:`, h), h;
|
|
288
293
|
} catch (a) {
|
|
289
|
-
|
|
294
|
+
n = a, r && console.warn(`[Analytics] ${i} tracking failed (attempt ${s + 1}):`, a), s < this.retryAttempts && await this.delay(this.retryDelay * (s + 1));
|
|
290
295
|
}
|
|
291
296
|
}
|
|
292
|
-
return console.error(`[Analytics] ${i} tracking failed after ${this.retryAttempts + 1} attempts:`,
|
|
297
|
+
return console.error(`[Analytics] ${i} tracking failed after ${this.retryAttempts + 1} attempts:`, n), null;
|
|
293
298
|
}
|
|
294
299
|
/**
|
|
295
300
|
* 延迟辅助方法
|
|
@@ -298,25 +303,25 @@ class x {
|
|
|
298
303
|
return new Promise((t) => setTimeout(t, e));
|
|
299
304
|
}
|
|
300
305
|
}
|
|
301
|
-
const P = new x(),
|
|
302
|
-
function
|
|
306
|
+
const P = new x(), T = (o, e) => e != null && e.baseUrl ? new x({ baseUrl: e.baseUrl }).trackImpression(o) : P.trackImpression(o), O = (o, e) => e != null && e.baseUrl ? new x({ baseUrl: e.baseUrl }).trackClick(o) : P.trackClick(o);
|
|
307
|
+
function J(o, e) {
|
|
303
308
|
return `vt_${o}_${e}`;
|
|
304
309
|
}
|
|
305
|
-
async function
|
|
306
|
-
return Promise.all(o.map((e) =>
|
|
310
|
+
async function X(o) {
|
|
311
|
+
return Promise.all(o.map((e) => T(e)));
|
|
307
312
|
}
|
|
308
|
-
async function
|
|
313
|
+
async function Z(o) {
|
|
309
314
|
return Promise.all(o.map((e) => O(e)));
|
|
310
315
|
}
|
|
311
|
-
function
|
|
316
|
+
function Y(o) {
|
|
312
317
|
const e = new x(o);
|
|
313
318
|
return {
|
|
314
319
|
trackImpression: (t) => e.trackImpression(t),
|
|
315
320
|
trackClick: (t) => e.trackClick(t)
|
|
316
321
|
};
|
|
317
322
|
}
|
|
318
|
-
function
|
|
319
|
-
const e = new
|
|
323
|
+
function ee(o = {}) {
|
|
324
|
+
const e = new V(o);
|
|
320
325
|
return {
|
|
321
326
|
getSessionId: () => e.getSessionId(),
|
|
322
327
|
regenerateSessionId: () => e.regenerateSessionId(),
|
|
@@ -338,20 +343,20 @@ const $ = class $ {
|
|
|
338
343
|
* Render Action Card ad into container
|
|
339
344
|
*/
|
|
340
345
|
static renderActionCard(e, t, i = {}) {
|
|
341
|
-
t.innerHTML =
|
|
342
|
-
const
|
|
343
|
-
return
|
|
344
|
-
|
|
346
|
+
t.innerHTML = v.renderActionCard(e, i);
|
|
347
|
+
const n = t.querySelector(".ax-ad-card-link");
|
|
348
|
+
return n && n.addEventListener("click", (s) => {
|
|
349
|
+
s.preventDefault(), this.handleClick(e, i);
|
|
345
350
|
}), this.trackImpression(e, t, i), t;
|
|
346
351
|
}
|
|
347
352
|
/**
|
|
348
353
|
* Render Suffix ad into container
|
|
349
354
|
*/
|
|
350
355
|
static renderSuffixAd(e, t, i = {}) {
|
|
351
|
-
const
|
|
352
|
-
t.innerHTML = this.generateSuffixAdHTML(e,
|
|
353
|
-
const
|
|
354
|
-
return
|
|
356
|
+
const n = i.variant || "block";
|
|
357
|
+
t.innerHTML = this.generateSuffixAdHTML(e, n);
|
|
358
|
+
const s = t.querySelector(".ax-ad-suffix-link");
|
|
359
|
+
return s && s.addEventListener("click", (r) => {
|
|
355
360
|
r.preventDefault(), this.handleClick(e, i);
|
|
356
361
|
}), this.trackImpression(e, t, i), t;
|
|
357
362
|
}
|
|
@@ -359,10 +364,10 @@ const $ = class $ {
|
|
|
359
364
|
* Render Sponsored Source ad into container
|
|
360
365
|
*/
|
|
361
366
|
static renderSponsoredSource(e, t, i = {}) {
|
|
362
|
-
const
|
|
363
|
-
t.innerHTML = this.generateSponsoredSourceHTML(e,
|
|
364
|
-
const
|
|
365
|
-
return
|
|
367
|
+
const n = i.variant || "card";
|
|
368
|
+
t.innerHTML = this.generateSponsoredSourceHTML(e, n);
|
|
369
|
+
const s = t.querySelector(".ax-ad-source-link");
|
|
370
|
+
return s && s.addEventListener("click", (r) => {
|
|
366
371
|
r.preventDefault(), this.handleClick(e, i);
|
|
367
372
|
}), this.trackImpression(e, t, i), t;
|
|
368
373
|
}
|
|
@@ -370,73 +375,73 @@ const $ = class $ {
|
|
|
370
375
|
* Render Lead Gen ad into container
|
|
371
376
|
*/
|
|
372
377
|
static renderLeadGenAd(e, t, i = {}) {
|
|
373
|
-
t.innerHTML =
|
|
374
|
-
const
|
|
375
|
-
return
|
|
376
|
-
|
|
378
|
+
t.innerHTML = v.renderLeadGenAd(e);
|
|
379
|
+
const n = t.querySelector(".ax-ad-leadgen-cta");
|
|
380
|
+
return n && n.addEventListener("click", (s) => {
|
|
381
|
+
s.preventDefault(), this.handleClick(e, i);
|
|
377
382
|
}), this.trackImpression(e, t, i), t;
|
|
378
383
|
}
|
|
379
384
|
/**
|
|
380
385
|
* Render multiple ads into container
|
|
381
386
|
*/
|
|
382
|
-
static renderAds(e, t, i = this.renderActionCard.bind(this),
|
|
387
|
+
static renderAds(e, t, i = this.renderActionCard.bind(this), n) {
|
|
383
388
|
t.innerHTML = "";
|
|
384
|
-
const
|
|
385
|
-
return
|
|
389
|
+
const s = document.createElement("div");
|
|
390
|
+
return s.className = "ax-ads-container", e.forEach((r) => {
|
|
386
391
|
const a = document.createElement("div");
|
|
387
|
-
a.className = "ax-ad-wrapper", i.call(this, r, a,
|
|
388
|
-
}), t.appendChild(
|
|
392
|
+
a.className = "ax-ad-wrapper", i.call(this, r, a, n), s.appendChild(a);
|
|
393
|
+
}), t.appendChild(s), t;
|
|
389
394
|
}
|
|
390
395
|
/**
|
|
391
396
|
* Handle ad click
|
|
392
397
|
* Directly opens click_url in a new window.
|
|
393
398
|
*/
|
|
394
399
|
static handleClick(e, t) {
|
|
395
|
-
const { onClick: i } = t,
|
|
396
|
-
if (!
|
|
400
|
+
const { onClick: i } = t, n = this.getClickUrl(e);
|
|
401
|
+
if (!n) {
|
|
397
402
|
console.warn("[DOMRenderer] Missing click url for ad:", e.original.id);
|
|
398
403
|
return;
|
|
399
404
|
}
|
|
400
|
-
i && i(e), this.isDebugEnabled() && console.log("[DOMRenderer] Redirect URL:",
|
|
405
|
+
i && i(e), this.isDebugEnabled() && console.log("[DOMRenderer] Redirect URL:", n), window.open(n, "_blank", "noopener,noreferrer");
|
|
401
406
|
}
|
|
402
407
|
/**
|
|
403
408
|
* Track ad impression using IntersectionObserver (50% visible + 1000ms delay)
|
|
404
409
|
* Falls back to immediate tracking if IntersectionObserver is unavailable.
|
|
405
410
|
*/
|
|
406
411
|
static trackImpression(e, t, i) {
|
|
407
|
-
const { analytics:
|
|
412
|
+
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) || ""}`;
|
|
408
413
|
if (this.trackedImpressionKeys.has(r))
|
|
409
414
|
return;
|
|
410
415
|
let a = !1;
|
|
411
416
|
const c = () => {
|
|
412
|
-
a || this.trackedImpressionKeys.has(r) || (a = !0, this.trackedImpressionKeys.add(r),
|
|
413
|
-
},
|
|
414
|
-
|
|
415
|
-
requestId:
|
|
417
|
+
a || this.trackedImpressionKeys.has(r) || (a = !0, this.trackedImpressionKeys.add(r), d());
|
|
418
|
+
}, d = () => {
|
|
419
|
+
n ? T({
|
|
420
|
+
requestId: n.requestId,
|
|
416
421
|
adId: e.original.id,
|
|
417
|
-
slotId:
|
|
418
|
-
position:
|
|
419
|
-
totalAds:
|
|
420
|
-
sessionId:
|
|
422
|
+
slotId: n.slotId,
|
|
423
|
+
position: n.position,
|
|
424
|
+
totalAds: n.totalAds,
|
|
425
|
+
sessionId: w(),
|
|
421
426
|
adTitle: e.adapted.title,
|
|
422
427
|
format: e.original.type,
|
|
423
428
|
source: "internal",
|
|
424
429
|
viewToken: this.getViewToken(e),
|
|
425
|
-
userId:
|
|
426
|
-
}, { baseUrl:
|
|
427
|
-
|
|
430
|
+
userId: n.userId
|
|
431
|
+
}, { baseUrl: n.apiBaseUrl }).then(() => {
|
|
432
|
+
s && s(e);
|
|
428
433
|
}).catch((l) => {
|
|
429
434
|
console.error("[DOMRenderer] Analytics impression tracking failed:", l);
|
|
430
|
-
}) :
|
|
435
|
+
}) : s && s(e);
|
|
431
436
|
};
|
|
432
437
|
if (typeof IntersectionObserver < "u") {
|
|
433
|
-
let l = !1,
|
|
438
|
+
let l = !1, u = null;
|
|
434
439
|
const h = new IntersectionObserver(
|
|
435
440
|
(f) => {
|
|
436
|
-
f.forEach((
|
|
437
|
-
l =
|
|
438
|
-
|
|
439
|
-
}, 1e3)) :
|
|
441
|
+
f.forEach((k) => {
|
|
442
|
+
l = k.isIntersecting && k.intersectionRatio >= 0.5, l ? u || (u = setTimeout(() => {
|
|
443
|
+
u = null, l && (c(), h.disconnect());
|
|
444
|
+
}, 1e3)) : u && (clearTimeout(u), u = null);
|
|
440
445
|
});
|
|
441
446
|
},
|
|
442
447
|
{ threshold: 0.5 }
|
|
@@ -449,7 +454,7 @@ const $ = class $ {
|
|
|
449
454
|
* Generate HTML for Suffix Ad variants
|
|
450
455
|
*/
|
|
451
456
|
static generateSuffixAdHTML(e, t) {
|
|
452
|
-
const i = e.adapted.title || "",
|
|
457
|
+
const i = e.adapted.title || "", n = m(e.adapted.body || "");
|
|
453
458
|
return t === "inline" ? `
|
|
454
459
|
<span class="ax-ad-suffix-link ax-ad-variant-inline" data-ad-id="${e.original.id}">
|
|
455
460
|
${i}
|
|
@@ -472,7 +477,7 @@ const $ = class $ {
|
|
|
472
477
|
</svg>
|
|
473
478
|
</div>
|
|
474
479
|
</div>
|
|
475
|
-
${
|
|
480
|
+
${n ? `<p class="ax-ad-suffix-body">${n}</p>` : ""}
|
|
476
481
|
</div>
|
|
477
482
|
`;
|
|
478
483
|
}
|
|
@@ -481,10 +486,10 @@ const $ = class $ {
|
|
|
481
486
|
*/
|
|
482
487
|
static generateSponsoredSourceHTML(e, t) {
|
|
483
488
|
var r;
|
|
484
|
-
const i = e.adapted.title || "",
|
|
489
|
+
const i = e.adapted.title || "", n = m(e.adapted.body || ""), s = ((r = e.adapted.image) == null ? void 0 : r.url) || "";
|
|
485
490
|
return t === "list-item" ? `
|
|
486
491
|
<div class="ax-ad-source ax-ad-variant-list-item" data-ad-id="${e.original.id}">
|
|
487
|
-
${
|
|
492
|
+
${s ? `<img src="${s}" alt="${i}" loading="lazy" />` : ""}
|
|
488
493
|
<div class="ax-ad-source-info">
|
|
489
494
|
<span class="ax-ad-source-name">${i}</span>
|
|
490
495
|
<span class="ax-ad-sponsored-badge" style="font-size:9px">Ad</span>
|
|
@@ -496,20 +501,20 @@ const $ = class $ {
|
|
|
496
501
|
` : t === "minimal" ? `
|
|
497
502
|
<div data-ad-id="${e.original.id}" style="display:inline-flex">
|
|
498
503
|
<a class="ax-ad-source-link ax-ad-variant-minimal">
|
|
499
|
-
${
|
|
504
|
+
${s ? `<img src="${s}" alt="${i}" loading="lazy" style="width:16px;height:16px;border-radius:2px;object-fit:cover" />` : ""}
|
|
500
505
|
<span>${i}</span>
|
|
501
506
|
<span class="ax-ad-sponsored-badge" style="font-size:9px">Ad</span>
|
|
502
507
|
</a>
|
|
503
508
|
</div>
|
|
504
509
|
` : `
|
|
505
510
|
<div class="ax-ad-source ax-ad-variant-card" data-ad-id="${e.original.id}">
|
|
506
|
-
${
|
|
511
|
+
${s ? `<img src="${s}" alt="${i}" loading="lazy" style="width:48px;height:48px;border-radius:6px;object-fit:cover;flex-shrink:0" />` : ""}
|
|
507
512
|
<div class="ax-ad-source-info">
|
|
508
513
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:4px">
|
|
509
514
|
<span class="ax-ad-source-name">${i}</span>
|
|
510
515
|
<span class="ax-ad-sponsored-badge">Sponsored</span>
|
|
511
516
|
</div>
|
|
512
|
-
${
|
|
517
|
+
${n ? `<p class="ax-ad-source-desc">${n}</p>` : ""}
|
|
513
518
|
</div>
|
|
514
519
|
<svg width="16" height="16" fill="none" stroke="#d1d5db" viewBox="0 0 24 24" style="flex-shrink:0">
|
|
515
520
|
<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" />
|
|
@@ -519,8 +524,8 @@ const $ = class $ {
|
|
|
519
524
|
}
|
|
520
525
|
};
|
|
521
526
|
$.trackedImpressionKeys = /* @__PURE__ */ new Set();
|
|
522
|
-
let
|
|
523
|
-
class
|
|
527
|
+
let y = $;
|
|
528
|
+
class I {
|
|
524
529
|
constructor() {
|
|
525
530
|
this.cachedStaticInfo = null, this.cacheTimestamp = 0, this.CACHE_TTL = 36e5;
|
|
526
531
|
}
|
|
@@ -535,13 +540,13 @@ class S {
|
|
|
535
540
|
name: "Unknown App",
|
|
536
541
|
publisher: { id: "unknown" }
|
|
537
542
|
};
|
|
538
|
-
const e = this.inferBundle(), t = this.inferAppName(), i = this.inferAppVersion(),
|
|
543
|
+
const e = this.inferBundle(), t = this.inferAppName(), i = this.inferAppVersion(), n = this.inferPublisherId();
|
|
539
544
|
return {
|
|
540
545
|
bundle: e,
|
|
541
546
|
ver: i,
|
|
542
547
|
name: t,
|
|
543
548
|
publisher: {
|
|
544
|
-
id:
|
|
549
|
+
id: n,
|
|
545
550
|
domain: window.location.hostname
|
|
546
551
|
}
|
|
547
552
|
};
|
|
@@ -559,14 +564,14 @@ class S {
|
|
|
559
564
|
* 自动推断应用名称
|
|
560
565
|
*/
|
|
561
566
|
inferAppName() {
|
|
562
|
-
var
|
|
567
|
+
var n, s;
|
|
563
568
|
if (typeof document > "u") return "Unknown App";
|
|
564
569
|
const e = document.title;
|
|
565
570
|
if (e && e !== "Loading..." && e.length < 100)
|
|
566
571
|
return e;
|
|
567
|
-
const t = (
|
|
572
|
+
const t = (n = document.querySelector('meta[property="og:title"]')) == null ? void 0 : n.getAttribute("content");
|
|
568
573
|
if (t) return t;
|
|
569
|
-
const i = (
|
|
574
|
+
const i = (s = document.querySelector('meta[name="application-name"]')) == null ? void 0 : s.getAttribute("content");
|
|
570
575
|
return i || "Unknown App";
|
|
571
576
|
}
|
|
572
577
|
/**
|
|
@@ -586,8 +591,8 @@ class S {
|
|
|
586
591
|
if (i) {
|
|
587
592
|
if (t.includes("manifest"))
|
|
588
593
|
return "1.0.0";
|
|
589
|
-
const
|
|
590
|
-
if (
|
|
594
|
+
const n = i.getAttribute("content");
|
|
595
|
+
if (n) return n;
|
|
591
596
|
}
|
|
592
597
|
}
|
|
593
598
|
return "1.0.0";
|
|
@@ -596,18 +601,18 @@ class S {
|
|
|
596
601
|
* 自动推断publisher ID
|
|
597
602
|
*/
|
|
598
603
|
inferPublisherId() {
|
|
599
|
-
var i,
|
|
604
|
+
var i, n;
|
|
600
605
|
if (typeof document > "u") return "unknown";
|
|
601
606
|
const e = (i = document.querySelector('meta[name="publisher-id"]')) == null ? void 0 : i.getAttribute("content");
|
|
602
607
|
if (e) return e;
|
|
603
|
-
const t = (
|
|
608
|
+
const t = (n = window.location) == null ? void 0 : n.hostname;
|
|
604
609
|
return t ? t.replace(/\./g, "_") : "unknown";
|
|
605
610
|
}
|
|
606
611
|
/**
|
|
607
612
|
* Get singleton instance
|
|
608
613
|
*/
|
|
609
614
|
static getInstance() {
|
|
610
|
-
return this.instance || (this.instance = new
|
|
615
|
+
return this.instance || (this.instance = new I()), this.instance;
|
|
611
616
|
}
|
|
612
617
|
/**
|
|
613
618
|
* Collect all available client information
|
|
@@ -729,11 +734,11 @@ class S {
|
|
|
729
734
|
const i = e.match(/Android (\d+)\.(\d+)/);
|
|
730
735
|
if (i)
|
|
731
736
|
return `${i[1]}.${i[2]}`;
|
|
732
|
-
const
|
|
733
|
-
if (
|
|
734
|
-
return
|
|
735
|
-
const
|
|
736
|
-
return
|
|
737
|
+
const n = e.match(/Windows NT (\d+\.\d+)/);
|
|
738
|
+
if (n)
|
|
739
|
+
return n[1];
|
|
740
|
+
const s = e.match(/Mac OS X (\d+[._]\d+)/);
|
|
741
|
+
return s ? s[1].replace("_", ".") : "Unknown";
|
|
737
742
|
}
|
|
738
743
|
/**
|
|
739
744
|
* Detect Device Type (OpenRTB standard)
|
|
@@ -758,10 +763,10 @@ class S {
|
|
|
758
763
|
const i = e.match(/Samsung-.*(SM-\w+)/);
|
|
759
764
|
if (i) return i[1];
|
|
760
765
|
if (/Pixel/.test(e)) return "Google Pixel";
|
|
761
|
-
const
|
|
762
|
-
if (
|
|
763
|
-
const
|
|
764
|
-
if (
|
|
766
|
+
const n = e.match(/(BARC-|HUAWEI-)?([A-Z]{2}\-\w{4})/);
|
|
767
|
+
if (n) return n[2];
|
|
768
|
+
const s = e.match(/(MI|Redmi|POCO)\s([\w\s]+)/);
|
|
769
|
+
if (s) return s[1] + " " + s[2];
|
|
765
770
|
if (/OnePlus/.test(e)) {
|
|
766
771
|
const r = e.match(/OnePlus\s([A-Z\d]+)/);
|
|
767
772
|
return r ? "OnePlus " + r[1] : "OnePlus";
|
|
@@ -877,9 +882,9 @@ class S {
|
|
|
877
882
|
if (t && i[t])
|
|
878
883
|
return i[t];
|
|
879
884
|
if (e) {
|
|
880
|
-
const
|
|
881
|
-
if (
|
|
882
|
-
return
|
|
885
|
+
const n = e.split("-")[1];
|
|
886
|
+
if (n)
|
|
887
|
+
return n.toUpperCase();
|
|
883
888
|
}
|
|
884
889
|
}
|
|
885
890
|
/**
|
|
@@ -910,35 +915,35 @@ class S {
|
|
|
910
915
|
this.cachedStaticInfo = null, this.cacheTimestamp = 0;
|
|
911
916
|
}
|
|
912
917
|
}
|
|
913
|
-
const
|
|
914
|
-
function
|
|
915
|
-
return
|
|
918
|
+
const C = () => I.getInstance();
|
|
919
|
+
function b(o) {
|
|
920
|
+
return C().collect(o);
|
|
916
921
|
}
|
|
917
|
-
function
|
|
918
|
-
return
|
|
922
|
+
function te() {
|
|
923
|
+
return b().device;
|
|
919
924
|
}
|
|
920
|
-
function
|
|
921
|
-
return
|
|
925
|
+
function H() {
|
|
926
|
+
return b().user;
|
|
922
927
|
}
|
|
923
|
-
function
|
|
924
|
-
return
|
|
928
|
+
function ie() {
|
|
929
|
+
return b().app;
|
|
925
930
|
}
|
|
926
|
-
function
|
|
927
|
-
return
|
|
931
|
+
function ne() {
|
|
932
|
+
return b().geo;
|
|
928
933
|
}
|
|
929
|
-
function
|
|
930
|
-
return
|
|
934
|
+
function D() {
|
|
935
|
+
return H().id;
|
|
931
936
|
}
|
|
932
|
-
function
|
|
933
|
-
|
|
937
|
+
function se() {
|
|
938
|
+
C().clearCache();
|
|
934
939
|
}
|
|
935
|
-
function
|
|
936
|
-
const e =
|
|
940
|
+
function re(o) {
|
|
941
|
+
const e = b(o);
|
|
937
942
|
return JSON.stringify(e, null, 2);
|
|
938
943
|
}
|
|
939
|
-
function
|
|
944
|
+
function oe() {
|
|
940
945
|
var t;
|
|
941
|
-
const o =
|
|
946
|
+
const o = b();
|
|
942
947
|
return [
|
|
943
948
|
o.device.os,
|
|
944
949
|
o.device.osv,
|
|
@@ -947,11 +952,33 @@ function ie() {
|
|
|
947
952
|
((t = o.geo) == null ? void 0 : t.country) || "Unknown"
|
|
948
953
|
].join(" / ");
|
|
949
954
|
}
|
|
950
|
-
function
|
|
955
|
+
function B() {
|
|
956
|
+
if (typeof crypto > "u" || !crypto.subtle)
|
|
957
|
+
throw new Error("Web Crypto API not supported");
|
|
958
|
+
}
|
|
959
|
+
async function G(o, e) {
|
|
960
|
+
B();
|
|
961
|
+
const t = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(e)), i = await crypto.subtle.importKey("raw", t, "AES-GCM", !1, ["encrypt"]), n = crypto.getRandomValues(new Uint8Array(12)), s = await crypto.subtle.encrypt(
|
|
962
|
+
{ name: "AES-GCM", iv: n },
|
|
963
|
+
i,
|
|
964
|
+
new TextEncoder().encode(o)
|
|
965
|
+
), r = new Uint8Array(n.length + s.byteLength);
|
|
966
|
+
return r.set(n), r.set(new Uint8Array(s), n.length), btoa(String.fromCharCode(...r));
|
|
967
|
+
}
|
|
968
|
+
async function F(o, e) {
|
|
969
|
+
B();
|
|
970
|
+
const t = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(e)), i = await crypto.subtle.importKey("raw", t, "AES-GCM", !1, ["decrypt"]), n = Uint8Array.from(atob(o), (c) => c.charCodeAt(0)), s = n.slice(0, 12), r = n.slice(12), a = await crypto.subtle.decrypt(
|
|
971
|
+
{ name: "AES-GCM", iv: s },
|
|
972
|
+
i,
|
|
973
|
+
r
|
|
974
|
+
);
|
|
975
|
+
return new TextDecoder().decode(a);
|
|
976
|
+
}
|
|
977
|
+
function z() {
|
|
951
978
|
var o;
|
|
952
979
|
return typeof window < "u" ? !!((o = window.__AD_CONFIG__) != null && o.debug) : !1;
|
|
953
980
|
}
|
|
954
|
-
function
|
|
981
|
+
function ae(o) {
|
|
955
982
|
var e, t;
|
|
956
983
|
return {
|
|
957
984
|
id: o.original.id,
|
|
@@ -978,37 +1005,51 @@ function se(o) {
|
|
|
978
1005
|
}
|
|
979
1006
|
};
|
|
980
1007
|
}
|
|
981
|
-
async function
|
|
982
|
-
const t = e.apiBaseUrl || "/api/v1", s =
|
|
1008
|
+
async function j(o, e = {}) {
|
|
1009
|
+
const t = e.apiBaseUrl || "/api/v1", i = z(), s = C().collect(), r = {
|
|
983
1010
|
...o,
|
|
984
1011
|
clientInfo: s
|
|
985
|
-
|
|
986
|
-
}, r = {
|
|
1012
|
+
}, a = {
|
|
987
1013
|
"Content-Type": "application/json"
|
|
988
1014
|
};
|
|
989
|
-
e.apiKey && (
|
|
990
|
-
|
|
1015
|
+
e.apiKey && (a["X-API-Key"] = e.apiKey);
|
|
1016
|
+
let c;
|
|
1017
|
+
if (e.apiKey && !i)
|
|
1018
|
+
try {
|
|
1019
|
+
c = { encryptedPayload: await G(JSON.stringify(r), e.apiKey) };
|
|
1020
|
+
} catch (h) {
|
|
1021
|
+
i && console.warn("[fetchAds] Encryption failed, using plaintext:", h), c = r;
|
|
1022
|
+
}
|
|
1023
|
+
else
|
|
1024
|
+
c = r;
|
|
1025
|
+
i && (console.log("[fetchAds] Request URL:", `${t}/ads/request`), console.log("[fetchAds] Request body:", c));
|
|
1026
|
+
const d = await fetch(`${t}/ads/request`, {
|
|
991
1027
|
method: "POST",
|
|
992
|
-
headers:
|
|
993
|
-
body: JSON.stringify(
|
|
1028
|
+
headers: a,
|
|
1029
|
+
body: JSON.stringify(c)
|
|
994
1030
|
});
|
|
995
|
-
if (!
|
|
996
|
-
throw new Error(`Ad request failed: ${
|
|
997
|
-
const
|
|
998
|
-
if (
|
|
999
|
-
throw new Error(
|
|
1000
|
-
|
|
1031
|
+
if (!d.ok)
|
|
1032
|
+
throw new Error(`Ad request failed: ${d.status} ${d.statusText}`);
|
|
1033
|
+
const l = await d.json();
|
|
1034
|
+
if (!l.success)
|
|
1035
|
+
throw new Error(l.error || "Ad request failed");
|
|
1036
|
+
let u = l.data;
|
|
1037
|
+
if (l.data.encryptedPayload && e.apiKey) {
|
|
1038
|
+
const h = await F(l.data.encryptedPayload, e.apiKey);
|
|
1039
|
+
u = JSON.parse(h);
|
|
1040
|
+
}
|
|
1041
|
+
return i && console.log("[fetchAds] Response:", u), u;
|
|
1001
1042
|
}
|
|
1002
|
-
const
|
|
1043
|
+
const W = { width: 400, height: 200 }, S = {
|
|
1003
1044
|
variant: "horizontal",
|
|
1004
1045
|
count: 1,
|
|
1005
1046
|
preferences: {}
|
|
1006
1047
|
};
|
|
1007
1048
|
function M(o = {}) {
|
|
1008
1049
|
const e = {
|
|
1009
|
-
variant: o.variant ??
|
|
1010
|
-
count: o.count ??
|
|
1011
|
-
preferences: { ...
|
|
1050
|
+
variant: o.variant ?? S.variant,
|
|
1051
|
+
count: o.count ?? S.count,
|
|
1052
|
+
preferences: { ...S.preferences, ...o.preferences }
|
|
1012
1053
|
};
|
|
1013
1054
|
return [
|
|
1014
1055
|
{
|
|
@@ -1016,7 +1057,7 @@ function M(o = {}) {
|
|
|
1016
1057
|
slotName: "Action Card",
|
|
1017
1058
|
format: "action_card",
|
|
1018
1059
|
variant: e.variant,
|
|
1019
|
-
size:
|
|
1060
|
+
size: W,
|
|
1020
1061
|
count: e.count,
|
|
1021
1062
|
preferences: e.preferences,
|
|
1022
1063
|
placement: { position: "below_fold", context: "post_response" }
|
|
@@ -1025,7 +1066,7 @@ function M(o = {}) {
|
|
|
1025
1066
|
}
|
|
1026
1067
|
const g = class g {
|
|
1027
1068
|
constructor(e) {
|
|
1028
|
-
this.slots = {}, this.isLoading = !1, this.currentRequestId = null, this.adsAnalyticsMap = /* @__PURE__ */ new Map(), this.config = e, this.enabled = e.enabled !== !1, this.eventBus = new
|
|
1069
|
+
this.slots = {}, this.isLoading = !1, this.currentRequestId = null, this.adsAnalyticsMap = /* @__PURE__ */ new Map(), this.config = e, this.enabled = e.enabled !== !1, this.eventBus = new q(), this.slots_config = M(e.cardOption), typeof window < "u" && (window.__AD_CONFIG__ = {
|
|
1029
1070
|
...window.__AD_CONFIG__ || {},
|
|
1030
1071
|
apiKey: e.apiKey,
|
|
1031
1072
|
apiBaseUrl: e.apiBaseUrl,
|
|
@@ -1039,24 +1080,24 @@ const g = class g {
|
|
|
1039
1080
|
async requestAds(e) {
|
|
1040
1081
|
const t = "conversationContext" in e ? e : { conversationContext: e };
|
|
1041
1082
|
this.currentUserId = this.resolveUserId(t.userContext);
|
|
1042
|
-
const i = this.buildRequestKey(t),
|
|
1043
|
-
if (
|
|
1044
|
-
return this.config.debug && console.log("[AdManager] Reusing in-flight request for identical context"),
|
|
1083
|
+
const i = this.buildRequestKey(t), n = g.inFlightRequests.get(i);
|
|
1084
|
+
if (n)
|
|
1085
|
+
return this.config.debug && console.log("[AdManager] Reusing in-flight request for identical context"), n;
|
|
1045
1086
|
if (!this.enabled)
|
|
1046
1087
|
throw this.config.debug && console.warn("[AdManager] Ads are disabled, skipping request"), new Error("Ads are disabled");
|
|
1047
1088
|
if (this.isLoading)
|
|
1048
1089
|
throw this.config.debug && console.warn("[AdManager] Request already in progress"), new Error("Request already in progress");
|
|
1049
1090
|
this.isLoading = !0, this.eventBus.emit("adsLoading"), this.config.debug && console.log("[AdManager] Starting ad request...");
|
|
1050
|
-
const
|
|
1091
|
+
const s = (async () => {
|
|
1051
1092
|
try {
|
|
1052
|
-
const r = await
|
|
1093
|
+
const r = await j(
|
|
1053
1094
|
{
|
|
1054
1095
|
conversationContext: t.conversationContext,
|
|
1055
1096
|
userContext: t.userContext ? {
|
|
1056
1097
|
...t.userContext,
|
|
1057
1098
|
userId: t.userContext.userId ?? this.currentUserId,
|
|
1058
|
-
sessionId: t.userContext.sessionId ??
|
|
1059
|
-
} : { userId: this.currentUserId, sessionId:
|
|
1099
|
+
sessionId: t.userContext.sessionId ?? w()
|
|
1100
|
+
} : { userId: this.currentUserId, sessionId: w() },
|
|
1060
1101
|
slots: this.slots_config
|
|
1061
1102
|
},
|
|
1062
1103
|
{
|
|
@@ -1065,9 +1106,9 @@ const g = class g {
|
|
|
1065
1106
|
}
|
|
1066
1107
|
);
|
|
1067
1108
|
this.currentRequestId = r.requestId, this.adsAnalyticsMap.clear(), r.slots.forEach((c) => {
|
|
1068
|
-
c.ads.forEach((
|
|
1069
|
-
const
|
|
1070
|
-
this.adsAnalyticsMap.set(
|
|
1109
|
+
c.ads.forEach((d, l) => {
|
|
1110
|
+
const u = d.original.id;
|
|
1111
|
+
this.adsAnalyticsMap.set(u, {
|
|
1071
1112
|
requestId: r.requestId,
|
|
1072
1113
|
slotId: c.slotId,
|
|
1073
1114
|
position: l,
|
|
@@ -1091,7 +1132,7 @@ const g = class g {
|
|
|
1091
1132
|
this.isLoading = !1, g.inFlightRequests.delete(i);
|
|
1092
1133
|
}
|
|
1093
1134
|
})();
|
|
1094
|
-
return g.inFlightRequests.set(i,
|
|
1135
|
+
return g.inFlightRequests.set(i, s), s;
|
|
1095
1136
|
}
|
|
1096
1137
|
buildRequestKey(e) {
|
|
1097
1138
|
return JSON.stringify({
|
|
@@ -1106,30 +1147,30 @@ const g = class g {
|
|
|
1106
1147
|
if (e != null && e.userId)
|
|
1107
1148
|
return e.userId;
|
|
1108
1149
|
try {
|
|
1109
|
-
return
|
|
1150
|
+
return D();
|
|
1110
1151
|
} catch {
|
|
1111
1152
|
return;
|
|
1112
1153
|
}
|
|
1113
1154
|
}
|
|
1114
1155
|
render(e, t, i = {}) {
|
|
1115
|
-
const
|
|
1156
|
+
const n = typeof e != "string", s = n ? g.DEFAULT_SLOT_ID : e, r = n ? e : t, a = (n ? t : i) || {};
|
|
1116
1157
|
if (!r)
|
|
1117
1158
|
return this.config.debug && console.warn("[AdManager] Render container is required"), null;
|
|
1118
|
-
const c = this.slots[
|
|
1159
|
+
const c = this.slots[s];
|
|
1119
1160
|
if (!c || !c.ads || c.ads.length === 0)
|
|
1120
|
-
return this.config.debug && console.warn("[AdManager] No ads in slot:",
|
|
1121
|
-
const
|
|
1161
|
+
return this.config.debug && console.warn("[AdManager] No ads in slot:", s), null;
|
|
1162
|
+
const d = a.adIndex ?? 0, l = c.ads[d];
|
|
1122
1163
|
if (!l)
|
|
1123
|
-
return this.config.debug && console.warn(`[AdManager] Ad at index ${
|
|
1124
|
-
const
|
|
1164
|
+
return this.config.debug && console.warn(`[AdManager] Ad at index ${d} not found in slot:`, s), null;
|
|
1165
|
+
const u = this.adsAnalyticsMap.get(l.original.id), h = this.slots_config.find((k) => k.slotId === s), p = (h == null ? void 0 : h.format) || "action_card";
|
|
1125
1166
|
r.innerHTML = "";
|
|
1126
1167
|
const f = {
|
|
1127
|
-
analytics:
|
|
1168
|
+
analytics: u,
|
|
1128
1169
|
variant: a.variant,
|
|
1129
1170
|
onClick: a.onClick,
|
|
1130
1171
|
onImpression: a.onImpression
|
|
1131
1172
|
};
|
|
1132
|
-
return p === "suffix" ?
|
|
1173
|
+
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);
|
|
1133
1174
|
}
|
|
1134
1175
|
/**
|
|
1135
1176
|
* Get ad slots data
|
|
@@ -1212,29 +1253,29 @@ const g = class g {
|
|
|
1212
1253
|
getAdsAnalytics(e) {
|
|
1213
1254
|
const t = /* @__PURE__ */ new Map();
|
|
1214
1255
|
return e.forEach((i) => {
|
|
1215
|
-
const
|
|
1216
|
-
|
|
1256
|
+
const n = this.adsAnalyticsMap.get(i);
|
|
1257
|
+
n && t.set(i, n);
|
|
1217
1258
|
}), t;
|
|
1218
1259
|
}
|
|
1219
1260
|
/**
|
|
1220
1261
|
* Track ad click using Analytics API
|
|
1221
1262
|
*/
|
|
1222
1263
|
async trackAdClick(e, t, i) {
|
|
1223
|
-
const
|
|
1224
|
-
if (!
|
|
1264
|
+
const n = this.adsAnalyticsMap.get(e);
|
|
1265
|
+
if (!n || !this.currentRequestId)
|
|
1225
1266
|
return this.config.debug && console.warn("[AdManager] No analytics info for ad:", e), null;
|
|
1226
|
-
const
|
|
1267
|
+
const s = w(), r = await O({
|
|
1227
1268
|
requestId: this.currentRequestId,
|
|
1228
1269
|
adId: e,
|
|
1229
1270
|
destinationUrl: t,
|
|
1230
|
-
sessionId:
|
|
1231
|
-
slotId:
|
|
1271
|
+
sessionId: s,
|
|
1272
|
+
slotId: n.slotId,
|
|
1232
1273
|
adTitle: i == null ? void 0 : i.title,
|
|
1233
1274
|
format: i == null ? void 0 : i.format,
|
|
1234
1275
|
source: i == null ? void 0 : i.source,
|
|
1235
|
-
userId:
|
|
1236
|
-
}, { baseUrl:
|
|
1237
|
-
return r && (this.eventBus.emit("adClicked", e,
|
|
1276
|
+
userId: n.userId ?? this.currentUserId
|
|
1277
|
+
}, { baseUrl: n.apiBaseUrl });
|
|
1278
|
+
return r && (this.eventBus.emit("adClicked", e, n.slotId), this.config.debug && console.log("[AdManager] Click tracked via Analytics API:", r.eventId)), r;
|
|
1238
1279
|
}
|
|
1239
1280
|
/**
|
|
1240
1281
|
* Track ad impression using Analytics API
|
|
@@ -1243,20 +1284,20 @@ const g = class g {
|
|
|
1243
1284
|
const i = this.adsAnalyticsMap.get(e);
|
|
1244
1285
|
if (!i || !this.currentRequestId)
|
|
1245
1286
|
return this.config.debug && console.warn("[AdManager] No analytics info for ad:", e), null;
|
|
1246
|
-
const
|
|
1287
|
+
const n = w(), s = await T({
|
|
1247
1288
|
requestId: this.currentRequestId,
|
|
1248
1289
|
adId: e,
|
|
1249
1290
|
slotId: i.slotId,
|
|
1250
1291
|
position: i.position,
|
|
1251
1292
|
totalAds: i.totalAds,
|
|
1252
|
-
sessionId:
|
|
1293
|
+
sessionId: n,
|
|
1253
1294
|
adTitle: t == null ? void 0 : t.title,
|
|
1254
1295
|
format: t == null ? void 0 : t.format,
|
|
1255
1296
|
source: t == null ? void 0 : t.source,
|
|
1256
1297
|
viewToken: t == null ? void 0 : t.viewToken,
|
|
1257
1298
|
userId: i.userId ?? this.currentUserId
|
|
1258
1299
|
}, { baseUrl: i.apiBaseUrl });
|
|
1259
|
-
return
|
|
1300
|
+
return s && (this.eventBus.emit("adImpression", e, i.slotId), this.config.debug && console.log("[AdManager] Impression tracked via Analytics API:", s.eventId)), s;
|
|
1260
1301
|
}
|
|
1261
1302
|
/**
|
|
1262
1303
|
* Destroy the manager and clean up
|
|
@@ -1267,23 +1308,23 @@ const g = class g {
|
|
|
1267
1308
|
};
|
|
1268
1309
|
g.DEFAULT_SLOT_ID = "action_card", g.inFlightRequests = /* @__PURE__ */ new Map();
|
|
1269
1310
|
let _ = g;
|
|
1270
|
-
function
|
|
1311
|
+
function E() {
|
|
1271
1312
|
if (typeof window < "u") {
|
|
1272
1313
|
const o = window.__AD_CONFIG__;
|
|
1273
1314
|
if (o != null && o.apiKey)
|
|
1274
1315
|
return o.apiKey;
|
|
1275
1316
|
}
|
|
1276
1317
|
}
|
|
1277
|
-
class
|
|
1318
|
+
class Q {
|
|
1278
1319
|
constructor(e = {}, t = "/api/v1") {
|
|
1279
|
-
var i,
|
|
1320
|
+
var i, n;
|
|
1280
1321
|
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 = {
|
|
1281
1322
|
minVisiblePercentage: e.minVisiblePercentage ?? 50,
|
|
1282
1323
|
minViewableDuration: e.minViewableDuration ?? 1e3,
|
|
1283
1324
|
maxTrackingDuration: e.maxTrackingDuration ?? 6e4,
|
|
1284
1325
|
batchConfig: {
|
|
1285
1326
|
maxBatchSize: ((i = e.batchConfig) == null ? void 0 : i.maxBatchSize) ?? 5,
|
|
1286
|
-
maxBatchWaitMs: ((
|
|
1327
|
+
maxBatchWaitMs: ((n = e.batchConfig) == null ? void 0 : n.maxBatchWaitMs) ?? 1e4
|
|
1287
1328
|
},
|
|
1288
1329
|
debug: e.debug ?? !1
|
|
1289
1330
|
}, this.baseUrl = t;
|
|
@@ -1291,7 +1332,7 @@ class G {
|
|
|
1291
1332
|
/**
|
|
1292
1333
|
* Start tracking viewability for an ad element
|
|
1293
1334
|
*/
|
|
1294
|
-
startTracking(e, t, i,
|
|
1335
|
+
startTracking(e, t, i, n, s) {
|
|
1295
1336
|
if (this.isTracking.get(e)) {
|
|
1296
1337
|
this.log(`[ViewabilityTracker] Already tracking ${e}, skipping`);
|
|
1297
1338
|
return;
|
|
@@ -1309,14 +1350,14 @@ class G {
|
|
|
1309
1350
|
};
|
|
1310
1351
|
this.metrics.set(e, r), this.isTracking.set(e, !0), this.eventQueue.set(e, []);
|
|
1311
1352
|
const a = new IntersectionObserver(
|
|
1312
|
-
(
|
|
1353
|
+
(d) => this.handleIntersection(e, d[0], i, n, s),
|
|
1313
1354
|
{
|
|
1314
1355
|
threshold: this.createThresholds()
|
|
1315
1356
|
}
|
|
1316
1357
|
);
|
|
1317
|
-
a.observe(t), this.observers.set(e, a), this.startMonitoring(e, i,
|
|
1358
|
+
a.observe(t), this.observers.set(e, a), this.startMonitoring(e, i, n, s);
|
|
1318
1359
|
const c = setTimeout(() => {
|
|
1319
|
-
this.endTracking(e, i,
|
|
1360
|
+
this.endTracking(e, i, n, s);
|
|
1320
1361
|
}, this.config.maxTrackingDuration);
|
|
1321
1362
|
this.timers.set(e, c);
|
|
1322
1363
|
}
|
|
@@ -1336,36 +1377,36 @@ class G {
|
|
|
1336
1377
|
* Handle IntersectionObserver callback
|
|
1337
1378
|
* Event-driven: Only process when state actually changes
|
|
1338
1379
|
*/
|
|
1339
|
-
handleIntersection(e, t, i,
|
|
1380
|
+
handleIntersection(e, t, i, n, s) {
|
|
1340
1381
|
const r = this.metrics.get(e);
|
|
1341
1382
|
if (!r || !this.isTracking.get(e)) return;
|
|
1342
1383
|
const a = r.isViewable, c = Math.round(t.intersectionRatio * 100);
|
|
1343
1384
|
r.visiblePercentage = c, r.maxVisiblePercentage = Math.max(r.maxVisiblePercentage, c);
|
|
1344
|
-
const
|
|
1345
|
-
if (l && !r.enteredViewportAt && (r.enteredViewportAt =
|
|
1385
|
+
const d = Date.now(), l = c >= this.config.minVisiblePercentage;
|
|
1386
|
+
if (l && !r.enteredViewportAt && (r.enteredViewportAt = d, r.enterCount++, this.log(`[ViewabilityTracker] ${e} entered viewport at ${d}`), this.queueEvent(e, {
|
|
1346
1387
|
adId: e,
|
|
1347
1388
|
sessionId: i,
|
|
1348
|
-
requestId:
|
|
1349
|
-
viewToken:
|
|
1389
|
+
requestId: n,
|
|
1390
|
+
viewToken: s,
|
|
1350
1391
|
eventType: "enter_viewport",
|
|
1351
1392
|
visiblePercentage: c,
|
|
1352
1393
|
maxVisiblePercentage: r.maxVisiblePercentage,
|
|
1353
1394
|
totalVisibleTimeMs: r.totalVisibleTimeMs,
|
|
1354
1395
|
isViewable: !1,
|
|
1355
|
-
timestamp:
|
|
1396
|
+
timestamp: d
|
|
1356
1397
|
})), !l && r.enteredViewportAt) {
|
|
1357
|
-
const
|
|
1358
|
-
r.totalVisibleTimeMs +=
|
|
1398
|
+
const u = d - r.enteredViewportAt;
|
|
1399
|
+
r.totalVisibleTimeMs += u, r.enteredViewportAt = null, r.currentVisibleTimeMs = 0, this.log(`[ViewabilityTracker] ${e} exited viewport, total visible: ${r.totalVisibleTimeMs}ms`), this.queueEvent(e, {
|
|
1359
1400
|
adId: e,
|
|
1360
1401
|
sessionId: i,
|
|
1361
|
-
requestId:
|
|
1362
|
-
viewToken:
|
|
1402
|
+
requestId: n,
|
|
1403
|
+
viewToken: s,
|
|
1363
1404
|
eventType: "exit_viewport",
|
|
1364
1405
|
visiblePercentage: c,
|
|
1365
1406
|
maxVisiblePercentage: r.maxVisiblePercentage,
|
|
1366
1407
|
totalVisibleTimeMs: r.totalVisibleTimeMs,
|
|
1367
1408
|
isViewable: r.isViewable,
|
|
1368
|
-
timestamp:
|
|
1409
|
+
timestamp: d
|
|
1369
1410
|
});
|
|
1370
1411
|
}
|
|
1371
1412
|
r.isViewable !== a && r.isViewable && this.log(`[ViewabilityTracker] ${e} became VIEWABLE!`), this.metrics.set(e, r);
|
|
@@ -1374,21 +1415,21 @@ class G {
|
|
|
1374
1415
|
* Start monitoring loop for tracking duration
|
|
1375
1416
|
* Runs every 100ms to track continuous visible time
|
|
1376
1417
|
*/
|
|
1377
|
-
startMonitoring(e, t, i,
|
|
1418
|
+
startMonitoring(e, t, i, n) {
|
|
1378
1419
|
const r = setInterval(() => {
|
|
1379
1420
|
const a = this.metrics.get(e);
|
|
1380
1421
|
if (!a || !this.isTracking.get(e)) {
|
|
1381
1422
|
clearInterval(r);
|
|
1382
1423
|
return;
|
|
1383
1424
|
}
|
|
1384
|
-
const c = Date.now(),
|
|
1425
|
+
const c = Date.now(), d = a.isViewable;
|
|
1385
1426
|
if (a.enteredViewportAt) {
|
|
1386
1427
|
const l = c - a.enteredViewportAt;
|
|
1387
|
-
a.currentVisibleTimeMs = l, l >= this.config.minViewableDuration && a.visiblePercentage >= this.config.minVisiblePercentage && (a.isViewable = !0,
|
|
1428
|
+
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, {
|
|
1388
1429
|
adId: e,
|
|
1389
1430
|
sessionId: t,
|
|
1390
1431
|
requestId: i,
|
|
1391
|
-
viewToken:
|
|
1432
|
+
viewToken: n,
|
|
1392
1433
|
eventType: "become_viewable",
|
|
1393
1434
|
visiblePercentage: a.visiblePercentage,
|
|
1394
1435
|
maxVisiblePercentage: a.maxVisiblePercentage,
|
|
@@ -1405,23 +1446,23 @@ class G {
|
|
|
1405
1446
|
* Queue an event to be sent (batched)
|
|
1406
1447
|
*/
|
|
1407
1448
|
queueEvent(e, t) {
|
|
1408
|
-
var
|
|
1449
|
+
var s, r;
|
|
1409
1450
|
const i = this.eventQueue.get(e);
|
|
1410
1451
|
if (!i) {
|
|
1411
1452
|
this.log(`[ViewabilityTracker] No queue found for ${e}, skipping event`);
|
|
1412
1453
|
return;
|
|
1413
1454
|
}
|
|
1414
1455
|
i.push(t), this.log(`[ViewabilityTracker] Queued ${t.eventType} for ${e} (queue size: ${i.length})`);
|
|
1415
|
-
const
|
|
1416
|
-
if (i.length >=
|
|
1456
|
+
const n = ((s = this.config.batchConfig) == null ? void 0 : s.maxBatchSize) ?? 5;
|
|
1457
|
+
if (i.length >= n)
|
|
1417
1458
|
this.flushQueue(e);
|
|
1418
1459
|
else {
|
|
1419
1460
|
const a = this.batchTimers.get(e);
|
|
1420
1461
|
a && clearTimeout(a);
|
|
1421
|
-
const c = ((r = this.config.batchConfig) == null ? void 0 : r.maxBatchWaitMs) ?? 1e4,
|
|
1462
|
+
const c = ((r = this.config.batchConfig) == null ? void 0 : r.maxBatchWaitMs) ?? 1e4, d = setTimeout(() => {
|
|
1422
1463
|
this.flushQueue(e);
|
|
1423
1464
|
}, c);
|
|
1424
|
-
this.batchTimers.set(e,
|
|
1465
|
+
this.batchTimers.set(e, d);
|
|
1425
1466
|
}
|
|
1426
1467
|
}
|
|
1427
1468
|
/**
|
|
@@ -1433,36 +1474,36 @@ class G {
|
|
|
1433
1474
|
const i = [...t];
|
|
1434
1475
|
t.length = 0, this.log(`[ViewabilityTracker] Flushing ${i.length} events for ${e}`);
|
|
1435
1476
|
try {
|
|
1436
|
-
const
|
|
1477
|
+
const n = {
|
|
1437
1478
|
"Content-Type": "application/json"
|
|
1438
|
-
},
|
|
1439
|
-
|
|
1479
|
+
}, s = E();
|
|
1480
|
+
s && (n["x-api-key"] = s), await fetch(`${this.baseUrl}/ads/viewability/batch`, {
|
|
1440
1481
|
method: "POST",
|
|
1441
|
-
headers:
|
|
1482
|
+
headers: n,
|
|
1442
1483
|
body: JSON.stringify({ events: i })
|
|
1443
1484
|
}), this.log(`[ViewabilityTracker] Successfully sent ${i.length} events for ${e}`);
|
|
1444
|
-
} catch (
|
|
1445
|
-
this.log(`[ViewabilityTracker] Failed to send events for ${e}:`,
|
|
1485
|
+
} catch (n) {
|
|
1486
|
+
this.log(`[ViewabilityTracker] Failed to send events for ${e}:`, n), t.unshift(...i);
|
|
1446
1487
|
}
|
|
1447
1488
|
}
|
|
1448
1489
|
/**
|
|
1449
1490
|
* End tracking and send final metrics
|
|
1450
1491
|
*/
|
|
1451
|
-
endTracking(e, t, i,
|
|
1452
|
-
const
|
|
1453
|
-
if (!
|
|
1492
|
+
endTracking(e, t, i, n) {
|
|
1493
|
+
const s = this.metrics.get(e);
|
|
1494
|
+
if (!s) return;
|
|
1454
1495
|
this.log(`[ViewabilityTracker] Ending tracking for ${e}`), this.flushQueue(e);
|
|
1455
1496
|
const r = Date.now();
|
|
1456
1497
|
this.queueEvent(e, {
|
|
1457
1498
|
adId: e,
|
|
1458
1499
|
sessionId: t,
|
|
1459
1500
|
requestId: i,
|
|
1460
|
-
viewToken:
|
|
1501
|
+
viewToken: n,
|
|
1461
1502
|
eventType: "end_tracking",
|
|
1462
|
-
visiblePercentage:
|
|
1463
|
-
maxVisiblePercentage:
|
|
1464
|
-
totalVisibleTimeMs:
|
|
1465
|
-
isViewable:
|
|
1503
|
+
visiblePercentage: s.visiblePercentage,
|
|
1504
|
+
maxVisiblePercentage: s.maxVisiblePercentage,
|
|
1505
|
+
totalVisibleTimeMs: s.totalVisibleTimeMs,
|
|
1506
|
+
isViewable: s.isViewable,
|
|
1466
1507
|
timestamp: r
|
|
1467
1508
|
}), this.flushQueue(e), this.cleanup(e);
|
|
1468
1509
|
}
|
|
@@ -1474,10 +1515,10 @@ class G {
|
|
|
1474
1515
|
t && (t.disconnect(), this.observers.delete(e));
|
|
1475
1516
|
const i = this.timers.get(e);
|
|
1476
1517
|
i && (clearTimeout(i), this.timers.delete(e));
|
|
1477
|
-
const
|
|
1478
|
-
|
|
1479
|
-
const
|
|
1480
|
-
|
|
1518
|
+
const n = this.timers.get(`${e}_monitoring`);
|
|
1519
|
+
n && (clearInterval(n), this.timers.delete(`${e}_monitoring`));
|
|
1520
|
+
const s = this.batchTimers.get(e);
|
|
1521
|
+
s && (clearTimeout(s), this.batchTimers.delete(e)), this.flushQueue(e), this.isTracking.delete(e), this.metrics.delete(e), this.eventQueue.delete(e);
|
|
1481
1522
|
}
|
|
1482
1523
|
/**
|
|
1483
1524
|
* Create thresholds for IntersectionObserver
|
|
@@ -1509,8 +1550,8 @@ class G {
|
|
|
1509
1550
|
if (t && t.length > 0) {
|
|
1510
1551
|
const i = {
|
|
1511
1552
|
"Content-Type": "application/json"
|
|
1512
|
-
},
|
|
1513
|
-
|
|
1553
|
+
}, n = E();
|
|
1554
|
+
n && (i["x-api-key"] = n), fetch(`${this.baseUrl}/ads/viewability/batch`, {
|
|
1514
1555
|
method: "POST",
|
|
1515
1556
|
headers: i,
|
|
1516
1557
|
keepalive: !0,
|
|
@@ -1521,39 +1562,41 @@ class G {
|
|
|
1521
1562
|
});
|
|
1522
1563
|
}
|
|
1523
1564
|
}
|
|
1524
|
-
let
|
|
1525
|
-
function
|
|
1526
|
-
return
|
|
1565
|
+
let A = null;
|
|
1566
|
+
function ce(o, e) {
|
|
1567
|
+
return A || (A = new Q(o, e), A.setupBeforeUnload()), A;
|
|
1527
1568
|
}
|
|
1528
|
-
const
|
|
1569
|
+
const le = "0.1.0";
|
|
1529
1570
|
export {
|
|
1530
1571
|
_ as AdManager,
|
|
1531
1572
|
x as AnalyticsSender,
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1573
|
+
I as ClientInfoCollector,
|
|
1574
|
+
y as DOMRenderer,
|
|
1575
|
+
v as HTMLRenderer,
|
|
1576
|
+
le as SDK_VERSION,
|
|
1577
|
+
V as SessionManager,
|
|
1578
|
+
Q as ViewabilityTracker,
|
|
1579
|
+
ae as adaptAdToKoahAd,
|
|
1580
|
+
se as clearClientInfoCache,
|
|
1581
|
+
Y as createAnalytics,
|
|
1582
|
+
ee as createSession,
|
|
1583
|
+
F as decryptAES,
|
|
1584
|
+
G as encryptAES,
|
|
1585
|
+
j as fetchAds,
|
|
1586
|
+
J as generateViewToken,
|
|
1587
|
+
ie as getAppInfo,
|
|
1588
|
+
b as getClientInfo,
|
|
1589
|
+
C as getClientInfoCollector,
|
|
1590
|
+
re as getClientInfoJSON,
|
|
1591
|
+
oe as getClientInfoSummary,
|
|
1592
|
+
te as getDeviceInfo,
|
|
1593
|
+
ne as getGeoInfo,
|
|
1594
|
+
w as getSessionId,
|
|
1595
|
+
D as getUserId,
|
|
1596
|
+
H as getUserInfo,
|
|
1597
|
+
ce as getViewabilityTracker,
|
|
1555
1598
|
O as trackAdClick,
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1599
|
+
T as trackAdImpression,
|
|
1600
|
+
Z as trackClicksBatch,
|
|
1601
|
+
X as trackImpressionsBatch
|
|
1559
1602
|
};
|