@bidkernel/analytics 0.16.0 → 0.18.0
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/analytics.global.js +1 -1
- package/dist/index.d.mts +67 -56
- package/dist/index.d.ts +67 -56
- package/dist/index.js +1794 -1819
- package/dist/index.mjs +1791 -1819
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21,7 +21,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
BidkernelPrebidAnalytics: () => BidkernelPrebidAnalytics,
|
|
24
|
+
CACHED_BID_TTL_MS: () => CACHED_BID_TTL_MS,
|
|
24
25
|
PrebidEventDeduper: () => PrebidEventDeduper,
|
|
26
|
+
attachImaAdsManager: () => attachImaAdsManager,
|
|
27
|
+
attachVideoPlayer: () => attachVideoPlayer,
|
|
25
28
|
getOrCreateSessionId: () => getOrCreateSessionId,
|
|
26
29
|
getPrebidEventKey: () => getPrebidEventKey,
|
|
27
30
|
getbidkernel: () => getbidkernel,
|
|
@@ -545,28 +548,6 @@ var TraceEventBatch = {
|
|
|
545
548
|
reader.skip(tag & 7);
|
|
546
549
|
}
|
|
547
550
|
return message;
|
|
548
|
-
},
|
|
549
|
-
create(base) {
|
|
550
|
-
return TraceEventBatch.fromPartial(base ?? {});
|
|
551
|
-
},
|
|
552
|
-
fromPartial(object) {
|
|
553
|
-
const message = createBaseTraceEventBatch();
|
|
554
|
-
message.propertyId = object.propertyId ?? "";
|
|
555
|
-
message.pageviewId = object.pageviewId ?? "";
|
|
556
|
-
message.sessionId = object.sessionId ?? "";
|
|
557
|
-
message.pageUrl = object.pageUrl ?? "";
|
|
558
|
-
message.country = object.country ?? "";
|
|
559
|
-
message.region = object.region ?? "";
|
|
560
|
-
message.deviceType = object.deviceType ?? "";
|
|
561
|
-
message.userId = object.userId ?? "";
|
|
562
|
-
message.domain = object.domain ?? "";
|
|
563
|
-
message.events = object.events?.map((e) => TraceEvent.fromPartial(e)) || [];
|
|
564
|
-
message.browser = object.browser ?? "";
|
|
565
|
-
message.deviceManufacturer = object.deviceManufacturer ?? "";
|
|
566
|
-
message.deviceModel = object.deviceModel ?? "";
|
|
567
|
-
message.city = object.city ?? "";
|
|
568
|
-
message.postalCode = object.postalCode ?? "";
|
|
569
|
-
return message;
|
|
570
551
|
}
|
|
571
552
|
};
|
|
572
553
|
function createBaseTraceEvent() {
|
|
@@ -731,33 +712,6 @@ var TraceEvent = {
|
|
|
731
712
|
reader.skip(tag & 7);
|
|
732
713
|
}
|
|
733
714
|
return message;
|
|
734
|
-
},
|
|
735
|
-
create(base) {
|
|
736
|
-
return TraceEvent.fromPartial(base ?? {});
|
|
737
|
-
},
|
|
738
|
-
fromPartial(object) {
|
|
739
|
-
const message = createBaseTraceEvent();
|
|
740
|
-
message.timestampMs = object.timestampMs ?? 0;
|
|
741
|
-
message.type = object.type ?? 0;
|
|
742
|
-
message.eventName = object.eventName ?? "";
|
|
743
|
-
message.auctionId = object.auctionId ?? "";
|
|
744
|
-
message.transactionId = object.transactionId ?? "";
|
|
745
|
-
message.adUnitCode = object.adUnitCode ?? "";
|
|
746
|
-
message.bid = object.bid !== void 0 && object.bid !== null ? BidTrace.fromPartial(object.bid) : void 0;
|
|
747
|
-
message.namespace = object.namespace ?? "";
|
|
748
|
-
message.eventId = object.eventId ?? "";
|
|
749
|
-
message.metadata = globalThis.Object.entries(object.metadata ?? {}).reduce(
|
|
750
|
-
(acc, [key, value]) => {
|
|
751
|
-
if (value !== void 0) {
|
|
752
|
-
acc[key] = globalThis.String(value);
|
|
753
|
-
}
|
|
754
|
-
return acc;
|
|
755
|
-
},
|
|
756
|
-
{}
|
|
757
|
-
);
|
|
758
|
-
message.viewableDurationMs = object.viewableDurationMs ?? void 0;
|
|
759
|
-
message.bidderOutcomes = object.bidderOutcomes?.map((e) => BidderOutcomeEntry.fromPartial(e)) || [];
|
|
760
|
-
return message;
|
|
761
715
|
}
|
|
762
716
|
};
|
|
763
717
|
function createBaseTraceEvent_MetadataEntry() {
|
|
@@ -801,15 +755,6 @@ var TraceEvent_MetadataEntry = {
|
|
|
801
755
|
reader.skip(tag & 7);
|
|
802
756
|
}
|
|
803
757
|
return message;
|
|
804
|
-
},
|
|
805
|
-
create(base) {
|
|
806
|
-
return TraceEvent_MetadataEntry.fromPartial(base ?? {});
|
|
807
|
-
},
|
|
808
|
-
fromPartial(object) {
|
|
809
|
-
const message = createBaseTraceEvent_MetadataEntry();
|
|
810
|
-
message.key = object.key ?? "";
|
|
811
|
-
message.value = object.value ?? "";
|
|
812
|
-
return message;
|
|
813
758
|
}
|
|
814
759
|
};
|
|
815
760
|
function createBaseBidderOutcomeEntry() {
|
|
@@ -883,18 +828,6 @@ var BidderOutcomeEntry = {
|
|
|
883
828
|
reader.skip(tag & 7);
|
|
884
829
|
}
|
|
885
830
|
return message;
|
|
886
|
-
},
|
|
887
|
-
create(base) {
|
|
888
|
-
return BidderOutcomeEntry.fromPartial(base ?? {});
|
|
889
|
-
},
|
|
890
|
-
fromPartial(object) {
|
|
891
|
-
const message = createBaseBidderOutcomeEntry();
|
|
892
|
-
message.bidder = object.bidder ?? "";
|
|
893
|
-
message.adUnitCode = object.adUnitCode ?? "";
|
|
894
|
-
message.mediaType = object.mediaType ?? "";
|
|
895
|
-
message.outcome = object.outcome ?? 0;
|
|
896
|
-
message.latencyMs = object.latencyMs ?? 0;
|
|
897
|
-
return message;
|
|
898
831
|
}
|
|
899
832
|
};
|
|
900
833
|
function createBaseBidTrace() {
|
|
@@ -1029,23 +962,6 @@ var BidTrace = {
|
|
|
1029
962
|
reader.skip(tag & 7);
|
|
1030
963
|
}
|
|
1031
964
|
return message;
|
|
1032
|
-
},
|
|
1033
|
-
create(base) {
|
|
1034
|
-
return BidTrace.fromPartial(base ?? {});
|
|
1035
|
-
},
|
|
1036
|
-
fromPartial(object) {
|
|
1037
|
-
const message = createBaseBidTrace();
|
|
1038
|
-
message.bidder = object.bidder ?? "";
|
|
1039
|
-
message.cpm = object.cpm ?? 0;
|
|
1040
|
-
message.currency = object.currency ?? "";
|
|
1041
|
-
message.width = object.width ?? 0;
|
|
1042
|
-
message.height = object.height ?? 0;
|
|
1043
|
-
message.dealId = object.dealId ?? "";
|
|
1044
|
-
message.mediaType = object.mediaType ?? "";
|
|
1045
|
-
message.latencyMs = object.latencyMs ?? 0;
|
|
1046
|
-
message.advertiserDomain = object.advertiserDomain ?? "";
|
|
1047
|
-
message.creativeId = object.creativeId ?? "";
|
|
1048
|
-
return message;
|
|
1049
965
|
}
|
|
1050
966
|
};
|
|
1051
967
|
|
|
@@ -1159,17 +1075,24 @@ function sameEndpointOrigin(a, b) {
|
|
|
1159
1075
|
return false;
|
|
1160
1076
|
}
|
|
1161
1077
|
}
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
if (!storageAllowed) return;
|
|
1078
|
+
var SESSION_PERSIST_INTERVAL_MS = 60 * 1e3;
|
|
1079
|
+
var persistedSessionTs = 0;
|
|
1080
|
+
function persistSessionTs(now) {
|
|
1166
1081
|
try {
|
|
1167
1082
|
if (typeof localStorage !== "undefined") {
|
|
1168
1083
|
localStorage.setItem(SESSION_TS_KEY, now.toString());
|
|
1084
|
+
persistedSessionTs = now;
|
|
1169
1085
|
}
|
|
1170
1086
|
} catch {
|
|
1171
1087
|
}
|
|
1172
1088
|
}
|
|
1089
|
+
function extendSession() {
|
|
1090
|
+
const now = Date.now();
|
|
1091
|
+
inMemorySessionTs = now;
|
|
1092
|
+
if (!storageAllowed) return;
|
|
1093
|
+
if (now - persistedSessionTs < SESSION_PERSIST_INTERVAL_MS) return;
|
|
1094
|
+
persistSessionTs(now);
|
|
1095
|
+
}
|
|
1173
1096
|
function getOrCreateSessionId() {
|
|
1174
1097
|
const now = Date.now();
|
|
1175
1098
|
const memoryFresh = !!inMemorySessionId && !!inMemorySessionTs && now - inMemorySessionTs <= THIRTY_MINUTES_MS;
|
|
@@ -1179,14 +1102,14 @@ function getOrCreateSessionId() {
|
|
|
1179
1102
|
const tsStr = localStorage.getItem(SESSION_TS_KEY);
|
|
1180
1103
|
const storedTs = tsStr ? parseInt(tsStr, 10) || 0 : 0;
|
|
1181
1104
|
if (storedId && storedTs && now - storedTs <= THIRTY_MINUTES_MS) {
|
|
1182
|
-
|
|
1105
|
+
persistSessionTs(now);
|
|
1183
1106
|
inMemorySessionId = storedId;
|
|
1184
1107
|
inMemorySessionTs = now;
|
|
1185
1108
|
return storedId;
|
|
1186
1109
|
}
|
|
1187
1110
|
const newId = memoryFresh ? inMemorySessionId : generateUUID();
|
|
1188
1111
|
localStorage.setItem(SESSION_KEY, newId);
|
|
1189
|
-
|
|
1112
|
+
persistSessionTs(now);
|
|
1190
1113
|
inMemorySessionId = newId;
|
|
1191
1114
|
inMemorySessionTs = now;
|
|
1192
1115
|
return newId;
|
|
@@ -1296,537 +1219,281 @@ function parseBidDimensions(bid) {
|
|
|
1296
1219
|
height: Number.isFinite(bid?.height) ? bid.height : 0
|
|
1297
1220
|
};
|
|
1298
1221
|
}
|
|
1299
|
-
var
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
const loadedProto = loadedEventCtor.prototype;
|
|
1304
|
-
if (loadedProto && typeof loadedProto.getAdsManager === "function" && !loadedProto.getAdsManager.__bidkernelHooked) {
|
|
1305
|
-
const originalGetAdsManager = loadedProto.getAdsManager;
|
|
1306
|
-
const hookedGetAdsManager = function(...args) {
|
|
1307
|
-
const adsManager = originalGetAdsManager.apply(this, args);
|
|
1308
|
-
if (adsManager) {
|
|
1309
|
-
for (const instance of BidkernelPrebidAnalytics.getActiveInstances()) {
|
|
1310
|
-
try {
|
|
1311
|
-
instance.attachImaAdsManager(adsManager);
|
|
1312
|
-
} catch {
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
|
-
return adsManager;
|
|
1317
|
-
};
|
|
1318
|
-
hookedGetAdsManager.__bidkernelHooked = true;
|
|
1319
|
-
loadedProto.getAdsManager = hookedGetAdsManager;
|
|
1320
|
-
}
|
|
1321
|
-
} catch {
|
|
1222
|
+
var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
1223
|
+
static activeInstances = /* @__PURE__ */ new Set();
|
|
1224
|
+
static getActiveInstances() {
|
|
1225
|
+
return _BidkernelPrebidAnalytics.activeInstances;
|
|
1322
1226
|
}
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1227
|
+
/**
|
|
1228
|
+
* Allows or forbids localStorage writes for every instance on the page.
|
|
1229
|
+
* Collection and delivery are unaffected: only the persisted session id,
|
|
1230
|
+
* its timestamp and exit batches are gated.
|
|
1231
|
+
*/
|
|
1232
|
+
static setStorageAllowed(allowed) {
|
|
1233
|
+
storageAllowed = allowed;
|
|
1234
|
+
persistedSessionTs = 0;
|
|
1235
|
+
}
|
|
1236
|
+
static isStorageAllowed() {
|
|
1237
|
+
return storageAllowed;
|
|
1238
|
+
}
|
|
1239
|
+
config;
|
|
1240
|
+
queue = [];
|
|
1241
|
+
errorCount = 0;
|
|
1242
|
+
flushTimer = null;
|
|
1243
|
+
boundFlushBeacon;
|
|
1244
|
+
boundVisibilityChange;
|
|
1245
|
+
isEnabled = false;
|
|
1246
|
+
boundPbjsHandlers = [];
|
|
1247
|
+
// Captured at enable()/navigate() so events queued before an SPA route
|
|
1248
|
+
// change flush with the page they occurred on, not the new URL.
|
|
1249
|
+
pageUrl = "";
|
|
1250
|
+
deduper = new PrebidEventDeduper();
|
|
1251
|
+
consecutiveSendFailures = 0;
|
|
1252
|
+
nextSendAllowedAt = 0;
|
|
1253
|
+
replayedEventCount = 0;
|
|
1254
|
+
revenueFlushTimer = null;
|
|
1255
|
+
// localStorage keys of exit batches this instance persisted, so a page that
|
|
1256
|
+
// survives its own pagehide/hidden (bfcache restore, tab re-focus) can
|
|
1257
|
+
// remove them instead of leaving them for a duplicate resend.
|
|
1258
|
+
persistedBatchKeys = [];
|
|
1259
|
+
persistedCleanupTimer = null;
|
|
1260
|
+
// Viewability & refresh tracking
|
|
1261
|
+
intersectionObserver = null;
|
|
1262
|
+
slotViewabilityRecords = /* @__PURE__ */ new Map();
|
|
1263
|
+
elementToSlotId = /* @__PURE__ */ new Map();
|
|
1264
|
+
slotRefreshIndices = /* @__PURE__ */ new Map();
|
|
1265
|
+
slotLastAuctionIds = /* @__PURE__ */ new Map();
|
|
1266
|
+
pendingThresholdListeners = /* @__PURE__ */ new Map();
|
|
1267
|
+
pendingViewableListeners = /* @__PURE__ */ new Map();
|
|
1268
|
+
// Element a subscriber named for a slot (a sticky bar or interstitial that
|
|
1269
|
+
// renders outside the slot element); wins over document.getElementById.
|
|
1270
|
+
slotElements = /* @__PURE__ */ new Map();
|
|
1271
|
+
clickDetachCleanups = /* @__PURE__ */ new Set();
|
|
1272
|
+
lastClickAt = /* @__PURE__ */ new Map();
|
|
1273
|
+
// Impression deduplication per slot and refresh cycle (strictly 1 impression per cycle)
|
|
1274
|
+
slotEmittedImpressionKeys = /* @__PURE__ */ new Set();
|
|
1275
|
+
// Winning bid cache from bidWon to marry with subsequent render triggers (adRenderSucceeded, video, etc.)
|
|
1276
|
+
cachedWinningBids = /* @__PURE__ */ new Map();
|
|
1277
|
+
// Active video player attachment cleanup routines (registered by ./video)
|
|
1278
|
+
videoDetachCleanups = /* @__PURE__ */ new Set();
|
|
1279
|
+
// Compacted bidder participation map per auction: auctionId -> Map<`${bidder}:${adUnitCode}`, BidderOutcomeEntry>
|
|
1280
|
+
auctionBidderOutcomes = /* @__PURE__ */ new Map();
|
|
1281
|
+
logger;
|
|
1282
|
+
constructor(config) {
|
|
1283
|
+
if (config.storageAllowed !== void 0) {
|
|
1284
|
+
_BidkernelPrebidAnalytics.setStorageAllowed(config.storageAllowed);
|
|
1341
1285
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1286
|
+
const requestedEndpoint = config.endpoint || "";
|
|
1287
|
+
const endpoint = !requestedEndpoint || isTrustedEndpoint(requestedEndpoint) ? requestedEndpoint : "";
|
|
1288
|
+
this.config = {
|
|
1289
|
+
endpoint,
|
|
1290
|
+
propertyId: config.propertyId || "",
|
|
1291
|
+
pageviewId: config.pageviewId || generateUUID(),
|
|
1292
|
+
sessionId: config.sessionId || getOrCreateSessionId(),
|
|
1293
|
+
userId: config.userId || "",
|
|
1294
|
+
deviceType: config.deviceType || "desktop",
|
|
1295
|
+
country: config.country || "",
|
|
1296
|
+
region: config.region || "",
|
|
1297
|
+
auctionEnabled: config.auctionEnabled ?? true,
|
|
1298
|
+
warningsEnabled: config.warningsEnabled ?? true,
|
|
1299
|
+
errorsEnabled: config.errorsEnabled ?? true,
|
|
1300
|
+
viewabilityEnabled: config.viewabilityEnabled ?? true,
|
|
1301
|
+
storageAllowed: config.storageAllowed ?? true,
|
|
1302
|
+
logLevel: config.logLevel || "INFO",
|
|
1303
|
+
pbjsGlobalName: config.pbjsGlobalName || "pbjs",
|
|
1304
|
+
attachPbjsListeners: config.attachPbjsListeners ?? true,
|
|
1305
|
+
revenueFlushDelayMs: Number.isFinite(config.revenueFlushDelayMs) && config.revenueFlushDelayMs >= 0 ? config.revenueFlushDelayMs : REVENUE_FLUSH_DELAY_MS
|
|
1306
|
+
};
|
|
1307
|
+
this.logger = createLogger({
|
|
1308
|
+
prefix: "[bidkernel][prebid-analytics]",
|
|
1309
|
+
logLevel: this.config.logLevel
|
|
1310
|
+
});
|
|
1311
|
+
if (requestedEndpoint && !endpoint) {
|
|
1312
|
+
this.log(
|
|
1313
|
+
"ERROR",
|
|
1314
|
+
`Rejected untrusted analytics endpoint ${requestedEndpoint}: must be an absolute https:// URL without embedded credentials. Events will not be transmitted.`
|
|
1315
|
+
);
|
|
1355
1316
|
}
|
|
1356
|
-
|
|
1317
|
+
this.boundFlushBeacon = () => {
|
|
1318
|
+
this.flushAllSlotsTimeInView();
|
|
1319
|
+
this.flushBeacon();
|
|
1320
|
+
};
|
|
1321
|
+
this.boundVisibilityChange = () => this.handleVisibilityChange();
|
|
1357
1322
|
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
if (!
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
const adsManager = origGetAdsManager.apply(this, args);
|
|
1375
|
-
if (adsManager) {
|
|
1376
|
-
for (const sdk of BidkernelPrebidAnalytics.getActiveInstances()) {
|
|
1377
|
-
try {
|
|
1378
|
-
sdk.attachImaAdsManager(adsManager);
|
|
1379
|
-
} catch {
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
return adsManager;
|
|
1384
|
-
};
|
|
1385
|
-
event.getAdsManager.__bidkernelHooked = true;
|
|
1386
|
-
}
|
|
1387
|
-
return listener.apply(this, arguments);
|
|
1388
|
-
};
|
|
1389
|
-
adsLoaderListenerMap.set(listener, wrappedListener);
|
|
1323
|
+
enable() {
|
|
1324
|
+
if (this.isEnabled) return;
|
|
1325
|
+
this.isEnabled = true;
|
|
1326
|
+
_BidkernelPrebidAnalytics.activeInstances.add(this);
|
|
1327
|
+
this.logger.setLevel(this.config.logLevel);
|
|
1328
|
+
if (!this.config.endpoint) {
|
|
1329
|
+
this.log("WARN", "Endpoint is empty. Analytics events will not be transmitted.");
|
|
1330
|
+
}
|
|
1331
|
+
if (this.config.viewabilityEnabled) {
|
|
1332
|
+
if (typeof IntersectionObserver !== "undefined") {
|
|
1333
|
+
this.intersectionObserver = new IntersectionObserver(this.handleIntersection.bind(this), {
|
|
1334
|
+
threshold: [0.5]
|
|
1335
|
+
});
|
|
1336
|
+
for (const record of this.slotViewabilityRecords.values()) {
|
|
1337
|
+
if (record.element) {
|
|
1338
|
+
this.intersectionObserver.observe(record.element);
|
|
1390
1339
|
}
|
|
1391
|
-
return origAddEventListener.call(this, type, wrappedListener, ...rest);
|
|
1392
1340
|
}
|
|
1393
|
-
|
|
1394
|
-
};
|
|
1395
|
-
hookedAddEventListener.__bidkernelHooked = true;
|
|
1396
|
-
proto.addEventListener = hookedAddEventListener;
|
|
1397
|
-
}
|
|
1398
|
-
if (typeof proto.removeEventListener === "function" && !proto.removeEventListener.__bidkernelHooked) {
|
|
1399
|
-
const origRemoveEventListener = proto.removeEventListener;
|
|
1400
|
-
const hookedRemoveEventListener = function(type, listener, ...rest) {
|
|
1401
|
-
const wrapped = typeof listener === "function" ? adsLoaderListenerMap.get(listener) : void 0;
|
|
1402
|
-
return origRemoveEventListener.call(this, type, wrapped || listener, ...rest);
|
|
1403
|
-
};
|
|
1404
|
-
hookedRemoveEventListener.__bidkernelHooked = true;
|
|
1405
|
-
proto.removeEventListener = hookedRemoveEventListener;
|
|
1341
|
+
}
|
|
1406
1342
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
}
|
|
1410
|
-
function hookImaPrototype(ima) {
|
|
1411
|
-
if (!ima || typeof ima !== "object") return;
|
|
1412
|
-
try {
|
|
1413
|
-
if (ima.AdsManagerLoadedEvent) {
|
|
1414
|
-
hookAdsManagerLoadedEvent(ima.AdsManagerLoadedEvent);
|
|
1343
|
+
if (this.config.attachPbjsListeners) {
|
|
1344
|
+
this.attachPbjs();
|
|
1415
1345
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
hookAdsManagerLoadedEvent(val);
|
|
1429
|
-
}
|
|
1430
|
-
});
|
|
1431
|
-
} catch {
|
|
1346
|
+
if (typeof window !== "undefined") {
|
|
1347
|
+
if (!this.pageUrl) {
|
|
1348
|
+
try {
|
|
1349
|
+
const u = new URL(window.location.href);
|
|
1350
|
+
this.pageUrl = u.origin + u.pathname;
|
|
1351
|
+
} catch {
|
|
1352
|
+
this.pageUrl = window.location.href;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
window.addEventListener("pagehide", this.boundFlushBeacon);
|
|
1356
|
+
if (typeof document !== "undefined") {
|
|
1357
|
+
document.addEventListener("visibilitychange", this.boundVisibilityChange);
|
|
1432
1358
|
}
|
|
1359
|
+
this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
|
|
1360
|
+
this.resendPersistedBatches();
|
|
1433
1361
|
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Binds the Prebid event handlers on the configured global and replays
|
|
1365
|
+
* pbjs.getEvents() history. enable() calls this when attachPbjsListeners is
|
|
1366
|
+
* true. An integration that loads Prebid lazily constructs the instance with
|
|
1367
|
+
* attachPbjsListeners=false and calls this once pbjs exists; disable() (and
|
|
1368
|
+
* therefore navigate()) drops the handlers, so call it again after either.
|
|
1369
|
+
* Idempotent. Returns true when handlers are bound after the call, false
|
|
1370
|
+
* when the instance is disabled or pbjs.onEvent is not available yet.
|
|
1371
|
+
*/
|
|
1372
|
+
attachPbjs() {
|
|
1373
|
+
if (!this.isEnabled) return false;
|
|
1374
|
+
if (this.boundPbjsHandlers.length > 0) return true;
|
|
1375
|
+
const win = typeof window !== "undefined" ? window : {};
|
|
1376
|
+
const pbjs = win[this.config.pbjsGlobalName] || {};
|
|
1377
|
+
if (typeof pbjs.onEvent !== "function") {
|
|
1378
|
+
this.log("WARN", "pbjs.onEvent is not defined. Prebid analytics will not function.");
|
|
1379
|
+
return false;
|
|
1436
1380
|
}
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1381
|
+
this.log("DEBUG", "Attaching event listeners to pbjs");
|
|
1382
|
+
const handlerMap = {
|
|
1383
|
+
auctionInit: this.handleAuctionInit.bind(this),
|
|
1384
|
+
auctionEnd: this.handleAuctionEnd.bind(this),
|
|
1385
|
+
bidRequested: this.handleBidRequested.bind(this),
|
|
1386
|
+
bidResponse: this.handleBidResponse.bind(this),
|
|
1387
|
+
bidTimeout: this.handleBidTimeout.bind(this),
|
|
1388
|
+
bidWon: this.handleBidWon.bind(this),
|
|
1389
|
+
noBid: this.handleNoBid.bind(this),
|
|
1390
|
+
adRenderFailed: this.handleAdRenderFailed.bind(this),
|
|
1391
|
+
adRenderSucceeded: this.handleAdRenderSucceeded.bind(this)
|
|
1392
|
+
};
|
|
1393
|
+
for (const [name, fn] of Object.entries(handlerMap)) {
|
|
1394
|
+
pbjs.onEvent(name, fn);
|
|
1395
|
+
this.boundPbjsHandlers.push({ event: name, handler: fn });
|
|
1396
|
+
}
|
|
1397
|
+
if (typeof pbjs.getEvents === "function") {
|
|
1440
1398
|
try {
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1399
|
+
const pastEvents = pbjs.getEvents();
|
|
1400
|
+
if (Array.isArray(pastEvents)) {
|
|
1401
|
+
const newPastEvents = pastEvents.slice(this.replayedEventCount);
|
|
1402
|
+
this.replayedEventCount = pastEvents.length;
|
|
1403
|
+
if (newPastEvents.length > 0) {
|
|
1404
|
+
this.log(
|
|
1405
|
+
"DEBUG",
|
|
1406
|
+
`Replaying ${newPastEvents.length} historical events from pbjs.getEvents()`
|
|
1407
|
+
);
|
|
1408
|
+
for (const ev of newPastEvents) {
|
|
1409
|
+
if (!ev) continue;
|
|
1410
|
+
const eventType = ev.eventType || ev.event || ev.name;
|
|
1411
|
+
const args = ev.args !== void 0 ? ev.args : ev.data !== void 0 ? ev.data : ev;
|
|
1412
|
+
const handler = handlerMap[eventType];
|
|
1413
|
+
if (handler) {
|
|
1414
|
+
handler(args);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1450
1417
|
}
|
|
1451
|
-
}
|
|
1452
|
-
} catch {
|
|
1418
|
+
}
|
|
1419
|
+
} catch (e) {
|
|
1420
|
+
this.log("WARN", "Failed to replay historical events from pbjs.getEvents()", e);
|
|
1453
1421
|
}
|
|
1454
1422
|
}
|
|
1455
|
-
|
|
1456
|
-
|
|
1423
|
+
return true;
|
|
1424
|
+
}
|
|
1425
|
+
disable() {
|
|
1426
|
+
if (!this.isEnabled) return;
|
|
1427
|
+
this.flushAllSlotsTimeInView();
|
|
1428
|
+
this.isEnabled = false;
|
|
1429
|
+
_BidkernelPrebidAnalytics.activeInstances.delete(this);
|
|
1430
|
+
for (const record of this.slotViewabilityRecords.values()) {
|
|
1431
|
+
if (record.dwellTimer) {
|
|
1432
|
+
clearTimeout(record.dwellTimer);
|
|
1433
|
+
record.dwellTimer = null;
|
|
1434
|
+
record.dwellStartedAt = null;
|
|
1435
|
+
}
|
|
1436
|
+
for (const l of record.thresholdListeners) {
|
|
1437
|
+
if (l.timer) {
|
|
1438
|
+
clearTimeout(l.timer);
|
|
1439
|
+
l.timer = null;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1457
1442
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1443
|
+
if (this.intersectionObserver) {
|
|
1444
|
+
this.intersectionObserver.disconnect();
|
|
1445
|
+
this.intersectionObserver = null;
|
|
1446
|
+
}
|
|
1447
|
+
this.slotViewabilityRecords.clear();
|
|
1448
|
+
this.elementToSlotId.clear();
|
|
1449
|
+
for (const cleanup of this.videoDetachCleanups) {
|
|
1461
1450
|
try {
|
|
1462
|
-
|
|
1463
|
-
configurable: true,
|
|
1464
|
-
enumerable: true,
|
|
1465
|
-
get() {
|
|
1466
|
-
return _mgr;
|
|
1467
|
-
},
|
|
1468
|
-
set(val) {
|
|
1469
|
-
_mgr = val;
|
|
1470
|
-
hookAdsManagerPrototype(val);
|
|
1471
|
-
}
|
|
1472
|
-
});
|
|
1451
|
+
cleanup();
|
|
1473
1452
|
} catch {
|
|
1474
1453
|
}
|
|
1475
1454
|
}
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
}
|
|
1479
|
-
function initImaInterception() {
|
|
1480
|
-
if (typeof window === "undefined") return;
|
|
1481
|
-
const win = window;
|
|
1482
|
-
if (win.google?.ima) {
|
|
1483
|
-
hookImaPrototype(win.google.ima);
|
|
1484
|
-
}
|
|
1485
|
-
if (win.google && typeof win.google === "object") {
|
|
1486
|
-
const imaDesc = Object.getOwnPropertyDescriptor(win.google, "ima");
|
|
1487
|
-
if (!imaDesc || imaDesc.configurable) {
|
|
1488
|
-
let _ima = win.google.ima;
|
|
1455
|
+
this.videoDetachCleanups.clear();
|
|
1456
|
+
for (const cleanup of Array.from(this.clickDetachCleanups)) {
|
|
1489
1457
|
try {
|
|
1490
|
-
|
|
1491
|
-
configurable: true,
|
|
1492
|
-
enumerable: true,
|
|
1493
|
-
get() {
|
|
1494
|
-
return _ima;
|
|
1495
|
-
},
|
|
1496
|
-
set(val) {
|
|
1497
|
-
_ima = val;
|
|
1498
|
-
hookImaPrototype(val);
|
|
1499
|
-
}
|
|
1500
|
-
});
|
|
1458
|
+
cleanup();
|
|
1501
1459
|
} catch {
|
|
1502
1460
|
}
|
|
1503
1461
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
}
|
|
1535
|
-
});
|
|
1536
|
-
} catch {
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1462
|
+
this.clickDetachCleanups.clear();
|
|
1463
|
+
this.slotElements.clear();
|
|
1464
|
+
this.cachedWinningBids.clear();
|
|
1465
|
+
this.slotEmittedImpressionKeys.clear();
|
|
1466
|
+
this.slotLastAuctionIds.clear();
|
|
1467
|
+
this.auctionBidderOutcomes.clear();
|
|
1468
|
+
if (this.flushTimer) {
|
|
1469
|
+
clearInterval(this.flushTimer);
|
|
1470
|
+
this.flushTimer = null;
|
|
1471
|
+
}
|
|
1472
|
+
if (this.revenueFlushTimer) {
|
|
1473
|
+
clearTimeout(this.revenueFlushTimer);
|
|
1474
|
+
this.revenueFlushTimer = null;
|
|
1475
|
+
}
|
|
1476
|
+
if (this.persistedCleanupTimer) {
|
|
1477
|
+
clearTimeout(this.persistedCleanupTimer);
|
|
1478
|
+
this.persistedCleanupTimer = null;
|
|
1479
|
+
}
|
|
1480
|
+
if (typeof window !== "undefined") {
|
|
1481
|
+
window.removeEventListener("pagehide", this.boundFlushBeacon);
|
|
1482
|
+
if (typeof document !== "undefined") {
|
|
1483
|
+
document.removeEventListener("visibilitychange", this.boundVisibilityChange);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
if (this.boundPbjsHandlers.length > 0) {
|
|
1487
|
+
const win = typeof window !== "undefined" ? window : {};
|
|
1488
|
+
const pbjs = win[this.config.pbjsGlobalName] || {};
|
|
1489
|
+
if (typeof pbjs.offEvent === "function") {
|
|
1490
|
+
for (const { event, handler } of this.boundPbjsHandlers) {
|
|
1491
|
+
pbjs.offEvent(event, handler);
|
|
1540
1492
|
}
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1493
|
+
}
|
|
1494
|
+
this.boundPbjsHandlers = [];
|
|
1543
1495
|
}
|
|
1544
|
-
|
|
1545
|
-
}
|
|
1546
|
-
if (typeof window !== "undefined") {
|
|
1547
|
-
initImaInterception();
|
|
1548
|
-
}
|
|
1549
|
-
var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
1550
|
-
static activeInstances = /* @__PURE__ */ new Set();
|
|
1551
|
-
static getActiveInstances() {
|
|
1552
|
-
return _BidkernelPrebidAnalytics.activeInstances;
|
|
1553
|
-
}
|
|
1554
|
-
static initImaInterception() {
|
|
1555
|
-
initImaInterception();
|
|
1556
|
-
}
|
|
1557
|
-
/**
|
|
1558
|
-
* Allows or forbids localStorage writes for every instance on the page.
|
|
1559
|
-
* Collection and delivery are unaffected: only the persisted session id,
|
|
1560
|
-
* its timestamp and exit batches are gated.
|
|
1561
|
-
*/
|
|
1562
|
-
static setStorageAllowed(allowed) {
|
|
1563
|
-
storageAllowed = allowed;
|
|
1564
|
-
}
|
|
1565
|
-
static isStorageAllowed() {
|
|
1566
|
-
return storageAllowed;
|
|
1567
|
-
}
|
|
1568
|
-
config;
|
|
1569
|
-
queue = [];
|
|
1570
|
-
errorCount = 0;
|
|
1571
|
-
flushTimer = null;
|
|
1572
|
-
boundFlushBeacon;
|
|
1573
|
-
boundVisibilityChange;
|
|
1574
|
-
isEnabled = false;
|
|
1575
|
-
boundPbjsHandlers = [];
|
|
1576
|
-
// Captured at enable()/navigate() so events queued before an SPA route
|
|
1577
|
-
// change flush with the page they occurred on, not the new URL.
|
|
1578
|
-
pageUrl = "";
|
|
1579
|
-
deduper = new PrebidEventDeduper();
|
|
1580
|
-
consecutiveSendFailures = 0;
|
|
1581
|
-
nextSendAllowedAt = 0;
|
|
1582
|
-
replayedEventCount = 0;
|
|
1583
|
-
revenueFlushTimer = null;
|
|
1584
|
-
// localStorage keys of exit batches this instance persisted, so a page that
|
|
1585
|
-
// survives its own pagehide/hidden (bfcache restore, tab re-focus) can
|
|
1586
|
-
// remove them instead of leaving them for a duplicate resend.
|
|
1587
|
-
persistedBatchKeys = [];
|
|
1588
|
-
persistedCleanupTimer = null;
|
|
1589
|
-
// Viewability & refresh tracking
|
|
1590
|
-
intersectionObserver = null;
|
|
1591
|
-
slotViewabilityRecords = /* @__PURE__ */ new Map();
|
|
1592
|
-
elementToSlotId = /* @__PURE__ */ new Map();
|
|
1593
|
-
slotRefreshIndices = /* @__PURE__ */ new Map();
|
|
1594
|
-
slotLastAuctionIds = /* @__PURE__ */ new Map();
|
|
1595
|
-
pendingThresholdListeners = /* @__PURE__ */ new Map();
|
|
1596
|
-
pendingViewableListeners = /* @__PURE__ */ new Map();
|
|
1597
|
-
// Element a subscriber named for a slot (a sticky bar or interstitial that
|
|
1598
|
-
// renders outside the slot element); wins over document.getElementById.
|
|
1599
|
-
slotElements = /* @__PURE__ */ new Map();
|
|
1600
|
-
clickDetachCleanups = /* @__PURE__ */ new Set();
|
|
1601
|
-
lastClickAt = /* @__PURE__ */ new Map();
|
|
1602
|
-
// Impression deduplication per slot and refresh cycle (strictly 1 impression per cycle)
|
|
1603
|
-
slotEmittedImpressionKeys = /* @__PURE__ */ new Set();
|
|
1604
|
-
// Winning bid cache from bidWon to marry with subsequent render triggers (adRenderSucceeded, video, etc.)
|
|
1605
|
-
cachedWinningBids = /* @__PURE__ */ new Map();
|
|
1606
|
-
// Active video player attachment cleanup routines
|
|
1607
|
-
videoDetachCleanups = /* @__PURE__ */ new Set();
|
|
1608
|
-
// Compacted bidder participation map per auction: auctionId -> Map<`${bidder}:${adUnitCode}`, BidderOutcomeEntry>
|
|
1609
|
-
auctionBidderOutcomes = /* @__PURE__ */ new Map();
|
|
1610
|
-
logger;
|
|
1611
|
-
constructor(config) {
|
|
1612
|
-
if (config.storageAllowed !== void 0) {
|
|
1613
|
-
_BidkernelPrebidAnalytics.setStorageAllowed(config.storageAllowed);
|
|
1614
|
-
}
|
|
1615
|
-
const requestedEndpoint = config.endpoint || "";
|
|
1616
|
-
const endpoint = !requestedEndpoint || isTrustedEndpoint(requestedEndpoint) ? requestedEndpoint : "";
|
|
1617
|
-
this.config = {
|
|
1618
|
-
endpoint,
|
|
1619
|
-
propertyId: config.propertyId || "",
|
|
1620
|
-
pageviewId: config.pageviewId || generateUUID(),
|
|
1621
|
-
sessionId: config.sessionId || getOrCreateSessionId(),
|
|
1622
|
-
userId: config.userId || "",
|
|
1623
|
-
deviceType: config.deviceType || "desktop",
|
|
1624
|
-
country: config.country || "",
|
|
1625
|
-
region: config.region || "",
|
|
1626
|
-
auctionEnabled: config.auctionEnabled ?? true,
|
|
1627
|
-
warningsEnabled: config.warningsEnabled ?? true,
|
|
1628
|
-
errorsEnabled: config.errorsEnabled ?? true,
|
|
1629
|
-
viewabilityEnabled: config.viewabilityEnabled ?? true,
|
|
1630
|
-
storageAllowed: config.storageAllowed ?? true,
|
|
1631
|
-
logLevel: config.logLevel || "INFO",
|
|
1632
|
-
pbjsGlobalName: config.pbjsGlobalName || "pbjs",
|
|
1633
|
-
attachPbjsListeners: config.attachPbjsListeners ?? true,
|
|
1634
|
-
revenueFlushDelayMs: Number.isFinite(config.revenueFlushDelayMs) && config.revenueFlushDelayMs >= 0 ? config.revenueFlushDelayMs : REVENUE_FLUSH_DELAY_MS
|
|
1635
|
-
};
|
|
1636
|
-
this.logger = createLogger({
|
|
1637
|
-
prefix: "[bidkernel][prebid-analytics]",
|
|
1638
|
-
logLevel: this.config.logLevel
|
|
1639
|
-
});
|
|
1640
|
-
if (requestedEndpoint && !endpoint) {
|
|
1641
|
-
this.log(
|
|
1642
|
-
"ERROR",
|
|
1643
|
-
`Rejected untrusted analytics endpoint ${requestedEndpoint}: must be an absolute https:// URL without embedded credentials. Events will not be transmitted.`
|
|
1644
|
-
);
|
|
1645
|
-
}
|
|
1646
|
-
this.boundFlushBeacon = () => {
|
|
1647
|
-
this.flushAllSlotsTimeInView();
|
|
1648
|
-
this.flushBeacon();
|
|
1649
|
-
};
|
|
1650
|
-
this.boundVisibilityChange = () => this.handleVisibilityChange();
|
|
1651
|
-
}
|
|
1652
|
-
enable() {
|
|
1653
|
-
if (this.isEnabled) return;
|
|
1654
|
-
this.isEnabled = true;
|
|
1655
|
-
_BidkernelPrebidAnalytics.activeInstances.add(this);
|
|
1656
|
-
this.logger.setLevel(this.config.logLevel);
|
|
1657
|
-
if (!this.config.endpoint) {
|
|
1658
|
-
this.log("WARN", "Endpoint is empty. Analytics events will not be transmitted.");
|
|
1659
|
-
}
|
|
1660
|
-
if (this.config.viewabilityEnabled) {
|
|
1661
|
-
if (typeof IntersectionObserver !== "undefined") {
|
|
1662
|
-
this.intersectionObserver = new IntersectionObserver(this.handleIntersection.bind(this), {
|
|
1663
|
-
threshold: [0.5]
|
|
1664
|
-
});
|
|
1665
|
-
for (const record of this.slotViewabilityRecords.values()) {
|
|
1666
|
-
if (record.element) {
|
|
1667
|
-
this.intersectionObserver.observe(record.element);
|
|
1668
|
-
}
|
|
1669
|
-
}
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
|
-
if (this.config.attachPbjsListeners) {
|
|
1673
|
-
this.attachPbjs();
|
|
1674
|
-
}
|
|
1675
|
-
if (typeof window !== "undefined") {
|
|
1676
|
-
if (!this.pageUrl) {
|
|
1677
|
-
try {
|
|
1678
|
-
const u = new URL(window.location.href);
|
|
1679
|
-
this.pageUrl = u.origin + u.pathname;
|
|
1680
|
-
} catch {
|
|
1681
|
-
this.pageUrl = window.location.href;
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
1684
|
-
window.addEventListener("pagehide", this.boundFlushBeacon);
|
|
1685
|
-
if (typeof document !== "undefined") {
|
|
1686
|
-
document.addEventListener("visibilitychange", this.boundVisibilityChange);
|
|
1687
|
-
}
|
|
1688
|
-
this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
|
|
1689
|
-
this.resendPersistedBatches();
|
|
1690
|
-
initImaInterception();
|
|
1691
|
-
if (window.google?.ima) {
|
|
1692
|
-
hookImaPrototype(window.google.ima);
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
/**
|
|
1697
|
-
* Binds the Prebid event handlers on the configured global and replays
|
|
1698
|
-
* pbjs.getEvents() history. enable() calls this when attachPbjsListeners is
|
|
1699
|
-
* true. An integration that loads Prebid lazily constructs the instance with
|
|
1700
|
-
* attachPbjsListeners=false and calls this once pbjs exists; disable() (and
|
|
1701
|
-
* therefore navigate()) drops the handlers, so call it again after either.
|
|
1702
|
-
* Idempotent. Returns true when handlers are bound after the call, false
|
|
1703
|
-
* when the instance is disabled or pbjs.onEvent is not available yet.
|
|
1704
|
-
*/
|
|
1705
|
-
attachPbjs() {
|
|
1706
|
-
if (!this.isEnabled) return false;
|
|
1707
|
-
if (this.boundPbjsHandlers.length > 0) return true;
|
|
1708
|
-
const win = typeof window !== "undefined" ? window : {};
|
|
1709
|
-
const pbjs = win[this.config.pbjsGlobalName] || {};
|
|
1710
|
-
if (typeof pbjs.onEvent !== "function") {
|
|
1711
|
-
this.log("WARN", "pbjs.onEvent is not defined. Prebid analytics will not function.");
|
|
1712
|
-
return false;
|
|
1713
|
-
}
|
|
1714
|
-
this.log("DEBUG", "Attaching event listeners to pbjs");
|
|
1715
|
-
const handlerMap = {
|
|
1716
|
-
auctionInit: this.handleAuctionInit.bind(this),
|
|
1717
|
-
auctionEnd: this.handleAuctionEnd.bind(this),
|
|
1718
|
-
bidRequested: this.handleBidRequested.bind(this),
|
|
1719
|
-
bidResponse: this.handleBidResponse.bind(this),
|
|
1720
|
-
bidTimeout: this.handleBidTimeout.bind(this),
|
|
1721
|
-
bidWon: this.handleBidWon.bind(this),
|
|
1722
|
-
noBid: this.handleNoBid.bind(this),
|
|
1723
|
-
adRenderFailed: this.handleAdRenderFailed.bind(this),
|
|
1724
|
-
adRenderSucceeded: this.handleAdRenderSucceeded.bind(this)
|
|
1725
|
-
};
|
|
1726
|
-
for (const [name, fn] of Object.entries(handlerMap)) {
|
|
1727
|
-
pbjs.onEvent(name, fn);
|
|
1728
|
-
this.boundPbjsHandlers.push({ event: name, handler: fn });
|
|
1729
|
-
}
|
|
1730
|
-
if (typeof pbjs.getEvents === "function") {
|
|
1731
|
-
try {
|
|
1732
|
-
const pastEvents = pbjs.getEvents();
|
|
1733
|
-
if (Array.isArray(pastEvents)) {
|
|
1734
|
-
const newPastEvents = pastEvents.slice(this.replayedEventCount);
|
|
1735
|
-
this.replayedEventCount = pastEvents.length;
|
|
1736
|
-
if (newPastEvents.length > 0) {
|
|
1737
|
-
this.log(
|
|
1738
|
-
"DEBUG",
|
|
1739
|
-
`Replaying ${newPastEvents.length} historical events from pbjs.getEvents()`
|
|
1740
|
-
);
|
|
1741
|
-
for (const ev of newPastEvents) {
|
|
1742
|
-
if (!ev) continue;
|
|
1743
|
-
const eventType = ev.eventType || ev.event || ev.name;
|
|
1744
|
-
const args = ev.args !== void 0 ? ev.args : ev.data !== void 0 ? ev.data : ev;
|
|
1745
|
-
const handler = handlerMap[eventType];
|
|
1746
|
-
if (handler) {
|
|
1747
|
-
handler(args);
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
|
-
} catch (e) {
|
|
1753
|
-
this.log("WARN", "Failed to replay historical events from pbjs.getEvents()", e);
|
|
1754
|
-
}
|
|
1755
|
-
}
|
|
1756
|
-
return true;
|
|
1757
|
-
}
|
|
1758
|
-
disable() {
|
|
1759
|
-
if (!this.isEnabled) return;
|
|
1760
|
-
this.flushAllSlotsTimeInView();
|
|
1761
|
-
this.isEnabled = false;
|
|
1762
|
-
_BidkernelPrebidAnalytics.activeInstances.delete(this);
|
|
1763
|
-
for (const record of this.slotViewabilityRecords.values()) {
|
|
1764
|
-
if (record.dwellTimer) {
|
|
1765
|
-
clearTimeout(record.dwellTimer);
|
|
1766
|
-
record.dwellTimer = null;
|
|
1767
|
-
record.dwellStartedAt = null;
|
|
1768
|
-
}
|
|
1769
|
-
for (const l of record.thresholdListeners) {
|
|
1770
|
-
if (l.timer) {
|
|
1771
|
-
clearTimeout(l.timer);
|
|
1772
|
-
l.timer = null;
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
}
|
|
1776
|
-
if (this.intersectionObserver) {
|
|
1777
|
-
this.intersectionObserver.disconnect();
|
|
1778
|
-
this.intersectionObserver = null;
|
|
1779
|
-
}
|
|
1780
|
-
this.slotViewabilityRecords.clear();
|
|
1781
|
-
this.elementToSlotId.clear();
|
|
1782
|
-
for (const cleanup of this.videoDetachCleanups) {
|
|
1783
|
-
try {
|
|
1784
|
-
cleanup();
|
|
1785
|
-
} catch {
|
|
1786
|
-
}
|
|
1787
|
-
}
|
|
1788
|
-
this.videoDetachCleanups.clear();
|
|
1789
|
-
for (const cleanup of Array.from(this.clickDetachCleanups)) {
|
|
1790
|
-
try {
|
|
1791
|
-
cleanup();
|
|
1792
|
-
} catch {
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
this.clickDetachCleanups.clear();
|
|
1796
|
-
this.slotElements.clear();
|
|
1797
|
-
this.cachedWinningBids.clear();
|
|
1798
|
-
this.slotEmittedImpressionKeys.clear();
|
|
1799
|
-
this.slotLastAuctionIds.clear();
|
|
1800
|
-
this.auctionBidderOutcomes.clear();
|
|
1801
|
-
if (this.flushTimer) {
|
|
1802
|
-
clearInterval(this.flushTimer);
|
|
1803
|
-
this.flushTimer = null;
|
|
1804
|
-
}
|
|
1805
|
-
if (this.revenueFlushTimer) {
|
|
1806
|
-
clearTimeout(this.revenueFlushTimer);
|
|
1807
|
-
this.revenueFlushTimer = null;
|
|
1808
|
-
}
|
|
1809
|
-
if (this.persistedCleanupTimer) {
|
|
1810
|
-
clearTimeout(this.persistedCleanupTimer);
|
|
1811
|
-
this.persistedCleanupTimer = null;
|
|
1812
|
-
}
|
|
1813
|
-
if (typeof window !== "undefined") {
|
|
1814
|
-
window.removeEventListener("pagehide", this.boundFlushBeacon);
|
|
1815
|
-
if (typeof document !== "undefined") {
|
|
1816
|
-
document.removeEventListener("visibilitychange", this.boundVisibilityChange);
|
|
1817
|
-
}
|
|
1818
|
-
}
|
|
1819
|
-
if (this.boundPbjsHandlers.length > 0) {
|
|
1820
|
-
const win = typeof window !== "undefined" ? window : {};
|
|
1821
|
-
const pbjs = win[this.config.pbjsGlobalName] || {};
|
|
1822
|
-
if (typeof pbjs.offEvent === "function") {
|
|
1823
|
-
for (const { event, handler } of this.boundPbjsHandlers) {
|
|
1824
|
-
pbjs.offEvent(event, handler);
|
|
1825
|
-
}
|
|
1826
|
-
}
|
|
1827
|
-
this.boundPbjsHandlers = [];
|
|
1828
|
-
}
|
|
1829
|
-
this.flush();
|
|
1496
|
+
this.flush();
|
|
1830
1497
|
}
|
|
1831
1498
|
handleVisibilityChange() {
|
|
1832
1499
|
if (typeof document === "undefined") return;
|
|
@@ -2346,74 +2013,8 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2346
2013
|
while (this.cachedWinningBids.size > MAX_CACHED_BID_KEYS) {
|
|
2347
2014
|
const oldest = this.cachedWinningBids.keys().next();
|
|
2348
2015
|
if (oldest.done) break;
|
|
2349
|
-
this.cachedWinningBids.delete(oldest.value);
|
|
2350
|
-
}
|
|
2351
|
-
}
|
|
2352
|
-
/**
|
|
2353
|
-
* Searches cached winning bids for an entry matching the given filters (creativeId,
|
|
2354
|
-
* adId, or mediaType), falling back to the most recent winning video bid.
|
|
2355
|
-
*/
|
|
2356
|
-
findCachedWinningBid(filter) {
|
|
2357
|
-
const now = Date.now();
|
|
2358
|
-
const isConsumed = (entry) => {
|
|
2359
|
-
return this.hasImpressionEmitted(
|
|
2360
|
-
entry.adUnitCode,
|
|
2361
|
-
void 0,
|
|
2362
|
-
entry.auctionId,
|
|
2363
|
-
entry.bidTrace.creativeId
|
|
2364
|
-
);
|
|
2365
|
-
};
|
|
2366
|
-
if (filter?.creativeId || filter?.adId) {
|
|
2367
|
-
for (const entry of Array.from(this.cachedWinningBids.values())) {
|
|
2368
|
-
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
2369
|
-
if (filter.creativeId && (entry.bidTrace.creativeId === filter.creativeId || entry.rawBid?.creativeId === filter.creativeId) || filter.adId && (entry.rawBid?.adId === filter.adId || entry.adUnitCode === filter.adId)) {
|
|
2370
|
-
return entry;
|
|
2371
|
-
}
|
|
2372
|
-
}
|
|
2373
|
-
}
|
|
2374
|
-
let bestUnconsumedMatch;
|
|
2375
|
-
if (filter?.mediaType) {
|
|
2376
|
-
for (const entry of Array.from(this.cachedWinningBids.values())) {
|
|
2377
|
-
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
2378
|
-
const isEntryVideo = entry.bidTrace.mediaType === filter.mediaType || entry.rawBid?.mediaType === filter.mediaType || filter.mediaType === "video" && entry.rawBid?.mediaTypes?.video !== void 0;
|
|
2379
|
-
if (isEntryVideo) {
|
|
2380
|
-
if (!isConsumed(entry)) {
|
|
2381
|
-
if (!bestUnconsumedMatch || entry.timestamp < bestUnconsumedMatch.timestamp) {
|
|
2382
|
-
bestUnconsumedMatch = entry;
|
|
2383
|
-
}
|
|
2384
|
-
}
|
|
2385
|
-
}
|
|
2386
|
-
}
|
|
2387
|
-
if (bestUnconsumedMatch) return bestUnconsumedMatch;
|
|
2388
|
-
let bestFallbackMatch;
|
|
2389
|
-
for (const entry of Array.from(this.cachedWinningBids.values())) {
|
|
2390
|
-
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
2391
|
-
const isEntryVideo = entry.bidTrace.mediaType === filter.mediaType || entry.rawBid?.mediaType === filter.mediaType || filter.mediaType === "video" && entry.rawBid?.mediaTypes?.video !== void 0;
|
|
2392
|
-
if (isEntryVideo) {
|
|
2393
|
-
if (!bestFallbackMatch || entry.timestamp > bestFallbackMatch.timestamp) {
|
|
2394
|
-
bestFallbackMatch = entry;
|
|
2395
|
-
}
|
|
2396
|
-
}
|
|
2397
|
-
}
|
|
2398
|
-
if (bestFallbackMatch) return bestFallbackMatch;
|
|
2399
|
-
}
|
|
2400
|
-
for (const entry of Array.from(this.cachedWinningBids.values())) {
|
|
2401
|
-
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
2402
|
-
if (!isConsumed(entry)) {
|
|
2403
|
-
if (!bestUnconsumedMatch || entry.timestamp > bestUnconsumedMatch.timestamp) {
|
|
2404
|
-
bestUnconsumedMatch = entry;
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
}
|
|
2408
|
-
if (bestUnconsumedMatch) return bestUnconsumedMatch;
|
|
2409
|
-
let bestRecent;
|
|
2410
|
-
for (const entry of Array.from(this.cachedWinningBids.values())) {
|
|
2411
|
-
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
2412
|
-
if (!bestRecent || entry.timestamp > bestRecent.timestamp) {
|
|
2413
|
-
bestRecent = entry;
|
|
2414
|
-
}
|
|
2016
|
+
this.cachedWinningBids.delete(oldest.value);
|
|
2415
2017
|
}
|
|
2416
|
-
return bestRecent;
|
|
2417
2018
|
}
|
|
2418
2019
|
/** Records a dedup key, evicting oldest-first at the cap. */
|
|
2419
2020
|
addImpressionKey(key) {
|
|
@@ -2562,1301 +2163,1672 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2562
2163
|
}
|
|
2563
2164
|
return true;
|
|
2564
2165
|
}
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
attachImaAdsManager(adsManager, options) {
|
|
2572
|
-
if (!adsManager || typeof adsManager.addEventListener !== "function") {
|
|
2573
|
-
this.log("WARN", "Invalid AdsManager passed to attachImaAdsManager");
|
|
2574
|
-
return () => {
|
|
2575
|
-
};
|
|
2576
|
-
}
|
|
2577
|
-
if (!adsManager.__bidkernelAttachedInstances) {
|
|
2578
|
-
try {
|
|
2579
|
-
Object.defineProperty(adsManager, "__bidkernelAttachedInstances", {
|
|
2580
|
-
value: /* @__PURE__ */ new Map(),
|
|
2581
|
-
configurable: true,
|
|
2582
|
-
writable: true
|
|
2583
|
-
});
|
|
2584
|
-
} catch {
|
|
2585
|
-
adsManager.__bidkernelAttachedInstances = /* @__PURE__ */ new Map();
|
|
2586
|
-
}
|
|
2587
|
-
}
|
|
2588
|
-
const attached = adsManager.__bidkernelAttachedInstances;
|
|
2589
|
-
const previous = attached.get(this);
|
|
2590
|
-
if (previous) {
|
|
2591
|
-
if (!options) return () => {
|
|
2592
|
-
};
|
|
2593
|
-
previous();
|
|
2594
|
-
}
|
|
2595
|
-
const slotId = options?.slotId || options?.adUnitCode || "video";
|
|
2596
|
-
const adUnitCode = options?.adUnitCode || slotId;
|
|
2597
|
-
const auctionId = options?.auctionId || "";
|
|
2598
|
-
const transactionId = options?.transactionId || "";
|
|
2599
|
-
const getWinningBid = (adData) => {
|
|
2600
|
-
let cached = (auctionId ? this.getCachedBid(`${auctionId}:${adUnitCode}`) : void 0) || (auctionId && slotId !== adUnitCode ? this.getCachedBid(`${auctionId}:${slotId}`) : void 0) || (adUnitCode !== "video" ? this.getCachedBid(adUnitCode) : void 0) || (slotId !== adUnitCode && slotId !== "video" ? this.getCachedBid(slotId) : void 0);
|
|
2601
|
-
if (!cached) {
|
|
2602
|
-
cached = this.findCachedWinningBid({
|
|
2603
|
-
creativeId: adData?.creativeId,
|
|
2604
|
-
adId: adData?.adId,
|
|
2605
|
-
mediaType: "video"
|
|
2606
|
-
});
|
|
2607
|
-
}
|
|
2608
|
-
return cached;
|
|
2166
|
+
onTimeInViewThreshold(slotId, thresholdMs, callback) {
|
|
2167
|
+
const listener = {
|
|
2168
|
+
thresholdMs,
|
|
2169
|
+
callback,
|
|
2170
|
+
timer: null,
|
|
2171
|
+
fired: false
|
|
2609
2172
|
};
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
this.
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
if (ad) {
|
|
2617
|
-
adData.creativeId = (typeof ad.getCreativeId === "function" ? ad.getCreativeId() : ad.creativeId) || "";
|
|
2618
|
-
adData.adId = (typeof ad.getAdId === "function" ? ad.getAdId() : ad.id) || "";
|
|
2619
|
-
adData.title = (typeof ad.getTitle === "function" ? ad.getTitle() : ad.title) || "";
|
|
2620
|
-
adData.duration = typeof ad.getDuration === "function" ? ad.getDuration() : ad.duration;
|
|
2621
|
-
adData.advertiserName = typeof ad.getAdvertiserName === "function" ? ad.getAdvertiserName() : "";
|
|
2622
|
-
}
|
|
2623
|
-
const cached = getWinningBid(adData);
|
|
2624
|
-
const resolvedSlotId = options?.slotId && options.slotId !== "video" ? options.slotId : options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || options?.slotId || options?.adUnitCode || "video";
|
|
2625
|
-
const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || resolvedSlotId;
|
|
2626
|
-
const resolvedAuctionId = auctionId || cached?.auctionId || "";
|
|
2627
|
-
const resolvedTransactionId = transactionId || cached?.transactionId || "";
|
|
2628
|
-
const mergedBid = {
|
|
2629
|
-
...cached?.rawBid || options?.bid,
|
|
2630
|
-
mediaType: "video",
|
|
2631
|
-
creativeId: adData.creativeId || cached?.bidTrace?.creativeId || options?.bid?.creativeId || ""
|
|
2632
|
-
};
|
|
2633
|
-
const emitted = this.recordImpression(resolvedSlotId, {
|
|
2634
|
-
adUnitCode: resolvedAdUnitCode,
|
|
2635
|
-
auctionId: resolvedAuctionId,
|
|
2636
|
-
transactionId: resolvedTransactionId,
|
|
2637
|
-
mediaType: "video",
|
|
2638
|
-
bid: mergedBid,
|
|
2639
|
-
metadata: {
|
|
2640
|
-
...options?.metadata,
|
|
2641
|
-
media_type: "video",
|
|
2642
|
-
...adData.title ? { ad_title: adData.title } : {},
|
|
2643
|
-
...Number.isFinite(adData.duration) ? { ad_duration_s: String(adData.duration) } : {}
|
|
2644
|
-
}
|
|
2645
|
-
});
|
|
2646
|
-
if (emitted && options?.onImpression) {
|
|
2173
|
+
const record = this.slotViewabilityRecords.get(slotId);
|
|
2174
|
+
if (record) {
|
|
2175
|
+
record.thresholdListeners.add(listener);
|
|
2176
|
+
const currentTotal = this.getCurrentTimeInView(record);
|
|
2177
|
+
if (currentTotal >= thresholdMs) {
|
|
2178
|
+
listener.fired = true;
|
|
2647
2179
|
try {
|
|
2648
|
-
|
|
2180
|
+
callback(slotId, currentTotal);
|
|
2649
2181
|
} catch (e) {
|
|
2650
|
-
this.log("ERROR", "Error in
|
|
2182
|
+
this.log("ERROR", "Error in threshold callback", e);
|
|
2651
2183
|
}
|
|
2184
|
+
} else if (record.inView && (typeof document === "undefined" || document.visibilityState === "visible")) {
|
|
2185
|
+
const remaining = thresholdMs - currentTotal;
|
|
2186
|
+
listener.timer = setTimeout(() => {
|
|
2187
|
+
listener.timer = null;
|
|
2188
|
+
if (!listener.fired && record.inView && (typeof document === "undefined" || document.visibilityState === "visible")) {
|
|
2189
|
+
const nowTotal = this.getCurrentTimeInView(record);
|
|
2190
|
+
if (nowTotal >= thresholdMs) {
|
|
2191
|
+
listener.fired = true;
|
|
2192
|
+
try {
|
|
2193
|
+
callback(slotId, nowTotal);
|
|
2194
|
+
} catch (e) {
|
|
2195
|
+
this.log("ERROR", "Error in threshold callback", e);
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
}, remaining);
|
|
2652
2200
|
}
|
|
2653
|
-
}
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2201
|
+
} else {
|
|
2202
|
+
if (!this.pendingThresholdListeners.has(slotId)) {
|
|
2203
|
+
this.pendingThresholdListeners.set(slotId, /* @__PURE__ */ new Set());
|
|
2204
|
+
}
|
|
2205
|
+
this.pendingThresholdListeners.get(slotId).add(listener);
|
|
2206
|
+
}
|
|
2207
|
+
return () => {
|
|
2208
|
+
if (listener.timer) {
|
|
2209
|
+
clearTimeout(listener.timer);
|
|
2210
|
+
listener.timer = null;
|
|
2211
|
+
}
|
|
2212
|
+
if (record) {
|
|
2213
|
+
record.thresholdListeners.delete(listener);
|
|
2214
|
+
}
|
|
2215
|
+
const pending = this.pendingThresholdListeners.get(slotId);
|
|
2216
|
+
if (pending) {
|
|
2217
|
+
pending.delete(listener);
|
|
2662
2218
|
}
|
|
2663
2219
|
};
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2220
|
+
}
|
|
2221
|
+
getViewabilityState(slotId) {
|
|
2222
|
+
const record = this.slotViewabilityRecords.get(slotId);
|
|
2223
|
+
if (!record) return void 0;
|
|
2224
|
+
const timeInViewMs = Math.round(this.getCurrentTimeInView(record));
|
|
2225
|
+
return {
|
|
2226
|
+
inView: record.inView,
|
|
2227
|
+
viewable: record.viewableFired,
|
|
2228
|
+
timeInViewMs,
|
|
2229
|
+
refreshIndex: record.refreshIndex
|
|
2230
|
+
};
|
|
2231
|
+
}
|
|
2232
|
+
/** The validated ingest endpoint in use, or "" when none passed validation. */
|
|
2233
|
+
getEndpoint() {
|
|
2234
|
+
return this.config.endpoint;
|
|
2235
|
+
}
|
|
2236
|
+
getRefreshIndex(slotId) {
|
|
2237
|
+
return this.slotRefreshIndices.get(slotId) ?? 0;
|
|
2238
|
+
}
|
|
2239
|
+
triggerSlotRefresh(slotId, options) {
|
|
2240
|
+
const record = this.slotViewabilityRecords.get(slotId);
|
|
2241
|
+
if (record && record.element) {
|
|
2242
|
+
this.observeSlot(record.element, slotId, options);
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
trackRawEvent(level, eventName, data) {
|
|
2246
|
+
const eventType = EVENT_NAME_TO_TYPE[eventName] ?? (level === "ERROR" ? TraceEventType.ERROR : TraceEventType.TRACE_EVENT_TYPE_UNSPECIFIED);
|
|
2247
|
+
this.enqueue(eventType, eventName, data, level);
|
|
2248
|
+
}
|
|
2249
|
+
setUserId(userId) {
|
|
2250
|
+
this.config.userId = userId;
|
|
2251
|
+
}
|
|
2252
|
+
setSessionId(sessionId) {
|
|
2253
|
+
this.config.sessionId = sessionId;
|
|
2254
|
+
}
|
|
2255
|
+
navigate(pageviewId, pageUrl) {
|
|
2256
|
+
this.disable();
|
|
2257
|
+
this.slotRefreshIndices.clear();
|
|
2258
|
+
this.pendingThresholdListeners.clear();
|
|
2259
|
+
this.config.pageviewId = pageviewId || generateUUID();
|
|
2260
|
+
if (pageUrl) {
|
|
2261
|
+
try {
|
|
2262
|
+
const u = new URL(pageUrl);
|
|
2263
|
+
this.pageUrl = u.origin + u.pathname;
|
|
2264
|
+
} catch {
|
|
2265
|
+
this.pageUrl = pageUrl;
|
|
2266
|
+
}
|
|
2267
|
+
} else {
|
|
2268
|
+
this.pageUrl = "";
|
|
2269
|
+
}
|
|
2270
|
+
this.logger.resetTiming();
|
|
2271
|
+
extendSession();
|
|
2272
|
+
this.enable();
|
|
2273
|
+
}
|
|
2274
|
+
handleAuctionInit(data) {
|
|
2275
|
+
if (this.isDuplicate("auctionInit", data)) return;
|
|
2276
|
+
this.log("DEBUG", "auctionInit", data);
|
|
2277
|
+
const auctionId = data.auctionId || "";
|
|
2278
|
+
if (auctionId) {
|
|
2279
|
+
this.auctionBidderOutcomes.delete(auctionId);
|
|
2280
|
+
this.getOrCreateAuctionOutcomes(auctionId);
|
|
2281
|
+
}
|
|
2282
|
+
this.enqueue(TraceEventType.AUCTION_START, "auctionStart", {
|
|
2283
|
+
auctionId,
|
|
2284
|
+
transactionId: data.transactionId || ""
|
|
2285
|
+
});
|
|
2286
|
+
}
|
|
2287
|
+
handleAuctionEnd(data) {
|
|
2288
|
+
if (this.isDuplicate("auctionEnd", data)) return;
|
|
2289
|
+
this.log("DEBUG", "auctionEnd", data);
|
|
2290
|
+
const auctionId = data.auctionId || "";
|
|
2291
|
+
const outcomesMap = auctionId ? this.auctionBidderOutcomes.get(auctionId) : void 0;
|
|
2292
|
+
const bidderOutcomes = outcomesMap ? Array.from(outcomesMap.values()) : [];
|
|
2293
|
+
if (auctionId) {
|
|
2294
|
+
this.auctionBidderOutcomes.delete(auctionId);
|
|
2295
|
+
}
|
|
2296
|
+
this.enqueue(TraceEventType.AUCTION_END, "auctionEnd", {
|
|
2297
|
+
auctionId,
|
|
2298
|
+
transactionId: data.transactionId || "",
|
|
2299
|
+
bidderOutcomes
|
|
2300
|
+
});
|
|
2301
|
+
}
|
|
2302
|
+
handleBidRequested(data) {
|
|
2303
|
+
if (this.isDuplicate("bidRequested", data)) return;
|
|
2304
|
+
this.log("DEBUG", "bidRequested", data);
|
|
2305
|
+
const auctionId = data.auctionId || "";
|
|
2306
|
+
const bidder = data.bidderCode || data.bidder || "";
|
|
2307
|
+
if (!auctionId || !bidder) return;
|
|
2308
|
+
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
2309
|
+
if (Array.isArray(data.bids)) {
|
|
2310
|
+
data.bids.forEach((bid) => {
|
|
2311
|
+
const adUnitCode = bid.adUnitCode || "";
|
|
2312
|
+
const mediaTypes = Object.keys(bid.mediaTypes || {});
|
|
2313
|
+
const mediaType = mediaTypes.length > 0 ? mediaTypes[0] : bid.mediaType || "banner";
|
|
2314
|
+
const key = `${bidder}:${adUnitCode}`;
|
|
2315
|
+
if (!auctionMap.has(key)) {
|
|
2316
|
+
auctionMap.set(key, {
|
|
2317
|
+
bidder,
|
|
2318
|
+
adUnitCode,
|
|
2319
|
+
mediaType,
|
|
2320
|
+
outcome: BidderOutcome.BIDDER_OUTCOME_NO_RESPONSE,
|
|
2321
|
+
latencyMs: 0
|
|
2322
|
+
});
|
|
2676
2323
|
}
|
|
2677
2324
|
});
|
|
2678
|
-
}
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
handleBidResponse(data) {
|
|
2328
|
+
if (this.isDuplicate("bidResponse", data)) return;
|
|
2329
|
+
this.log("DEBUG", "bidResponse", data);
|
|
2330
|
+
const auctionId = data.auctionId || "";
|
|
2331
|
+
const bidder = data.bidderCode || data.bidder || "";
|
|
2332
|
+
const adUnitCode = data.adUnitCode || "";
|
|
2333
|
+
const cpm = Number.isFinite(data.originalCpm) ? data.originalCpm : Number.isFinite(data.cpm) ? data.cpm : 0;
|
|
2334
|
+
const latencyMs = Number.isFinite(data.timeToRespond) ? data.timeToRespond : 0;
|
|
2335
|
+
const resMediaTypes = Object.keys(data.mediaTypes || {});
|
|
2336
|
+
const mediaType = data.mediaType || (data.mediaTypes?.video ? "video" : resMediaTypes.length > 0 ? resMediaTypes[0] : "banner");
|
|
2337
|
+
if (auctionId && bidder) {
|
|
2338
|
+
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
2339
|
+
const key = `${bidder}:${adUnitCode}`;
|
|
2340
|
+
const entry = auctionMap.get(key);
|
|
2341
|
+
const outcome = cpm > 0 ? BidderOutcome.BIDDER_OUTCOME_BID : BidderOutcome.BIDDER_OUTCOME_NO_BID;
|
|
2342
|
+
if (entry) {
|
|
2343
|
+
entry.outcome = outcome;
|
|
2344
|
+
entry.latencyMs = latencyMs;
|
|
2345
|
+
if (mediaType) entry.mediaType = mediaType;
|
|
2346
|
+
} else {
|
|
2347
|
+
auctionMap.set(key, {
|
|
2348
|
+
bidder,
|
|
2349
|
+
adUnitCode,
|
|
2350
|
+
mediaType,
|
|
2351
|
+
outcome,
|
|
2352
|
+
latencyMs
|
|
2353
|
+
});
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
if (cpm > 0) {
|
|
2357
|
+
this.enqueue(TraceEventType.BID_RESPONSE, "bidResponse", {
|
|
2358
|
+
auctionId,
|
|
2359
|
+
transactionId: data.transactionId || "",
|
|
2360
|
+
adUnitCode,
|
|
2361
|
+
bid: {
|
|
2362
|
+
bidder,
|
|
2363
|
+
cpm,
|
|
2364
|
+
currency: data.originalCurrency ?? data.currency ?? "USD",
|
|
2365
|
+
...parseBidDimensions(data),
|
|
2366
|
+
dealId: data.dealId || "",
|
|
2367
|
+
mediaType,
|
|
2368
|
+
latencyMs,
|
|
2369
|
+
advertiserDomain: data.meta?.advertiserDomains?.[0] || "",
|
|
2370
|
+
creativeId: data.creativeId || ""
|
|
2371
|
+
}
|
|
2699
2372
|
});
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
handleBidTimeout(data) {
|
|
2376
|
+
if (this.isDuplicate("bidTimeout", data)) return;
|
|
2377
|
+
this.log("DEBUG", "bidTimeout", data);
|
|
2378
|
+
const items = Array.isArray(data) ? data : data ? [data] : [];
|
|
2379
|
+
for (const t of items) {
|
|
2380
|
+
const auctionId = t.auctionId || "";
|
|
2381
|
+
const bidder = t.bidderCode || t.bidder || "";
|
|
2382
|
+
const adUnitCode = t.adUnitCode || "";
|
|
2383
|
+
const latencyMs = Number.isFinite(t.timeout) ? t.timeout : 0;
|
|
2384
|
+
if (auctionId && bidder) {
|
|
2385
|
+
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
2386
|
+
const key = `${bidder}:${adUnitCode}`;
|
|
2387
|
+
const entry = auctionMap.get(key);
|
|
2388
|
+
if (entry) {
|
|
2389
|
+
entry.outcome = BidderOutcome.BIDDER_OUTCOME_TIMEOUT;
|
|
2390
|
+
if (latencyMs > 0) entry.latencyMs = latencyMs;
|
|
2391
|
+
} else {
|
|
2392
|
+
auctionMap.set(key, {
|
|
2393
|
+
bidder,
|
|
2394
|
+
adUnitCode,
|
|
2395
|
+
mediaType: "banner",
|
|
2396
|
+
outcome: BidderOutcome.BIDDER_OUTCOME_TIMEOUT,
|
|
2397
|
+
latencyMs
|
|
2398
|
+
});
|
|
2705
2399
|
}
|
|
2706
2400
|
}
|
|
2707
|
-
};
|
|
2708
|
-
const googleIma = typeof window !== "undefined" ? window.google?.ima : void 0;
|
|
2709
|
-
const adEventType = googleIma?.AdEvent?.Type || {};
|
|
2710
|
-
const adErrorEventType = googleIma?.AdErrorEvent?.Type || {};
|
|
2711
|
-
const rawListeners = [
|
|
2712
|
-
{ type: adEventType.STARTED || "started", handler: onAdStartedOrImpression },
|
|
2713
|
-
{ type: adEventType.IMPRESSION || "impression", handler: onAdStartedOrImpression },
|
|
2714
|
-
{
|
|
2715
|
-
type: adEventType.FIRST_QUARTILE || "firstQuartile",
|
|
2716
|
-
handler: () => onMilestone("firstQuartile")
|
|
2717
|
-
},
|
|
2718
|
-
{ type: adEventType.MIDPOINT || "midpoint", handler: () => onMilestone("midpoint") },
|
|
2719
|
-
{
|
|
2720
|
-
type: adEventType.THIRD_QUARTILE || "thirdQuartile",
|
|
2721
|
-
handler: () => onMilestone("thirdQuartile")
|
|
2722
|
-
},
|
|
2723
|
-
{ type: adEventType.COMPLETE || "complete", handler: () => onMilestone("complete") },
|
|
2724
|
-
{ type: adEventType.CLICK || "click", handler: onAdClick },
|
|
2725
|
-
{ type: adErrorEventType.AD_ERROR || "adError", handler: onAdError }
|
|
2726
|
-
];
|
|
2727
|
-
const seenListenerTypes = /* @__PURE__ */ new Set();
|
|
2728
|
-
const listeners = [];
|
|
2729
|
-
for (const l of rawListeners) {
|
|
2730
|
-
if (!seenListenerTypes.has(l.type)) {
|
|
2731
|
-
seenListenerTypes.add(l.type);
|
|
2732
|
-
listeners.push(l);
|
|
2733
|
-
}
|
|
2734
2401
|
}
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2402
|
+
}
|
|
2403
|
+
handleBidWon(data) {
|
|
2404
|
+
if (this.isDuplicate("bidWon", data)) return;
|
|
2405
|
+
this.log("DEBUG", "bidWon", data);
|
|
2406
|
+
const auctionId = data.auctionId || "";
|
|
2407
|
+
const transactionId = data.transactionId || "";
|
|
2408
|
+
const adUnitCode = data.adUnitCode || data.adId || "";
|
|
2409
|
+
const wonMediaTypes = Object.keys(data.mediaTypes || {});
|
|
2410
|
+
const resolvedMediaType = data.mediaType || (data.mediaTypes?.video ? "video" : wonMediaTypes.length > 0 ? wonMediaTypes[0] : "banner");
|
|
2411
|
+
const bidTrace = {
|
|
2412
|
+
bidder: data.bidderCode || data.bidder || "",
|
|
2413
|
+
cpm: Number.isFinite(data.originalCpm) ? data.originalCpm : Number.isFinite(data.cpm) ? data.cpm : 0,
|
|
2414
|
+
currency: data.originalCurrency ?? data.currency ?? "USD",
|
|
2415
|
+
...parseBidDimensions(data),
|
|
2416
|
+
dealId: data.dealId || "",
|
|
2417
|
+
mediaType: resolvedMediaType,
|
|
2418
|
+
latencyMs: Number.isFinite(data.timeToRespond) ? data.timeToRespond : 0,
|
|
2419
|
+
advertiserDomain: data.meta?.advertiserDomains?.[0] || "",
|
|
2420
|
+
creativeId: data.creativeId || ""
|
|
2421
|
+
};
|
|
2422
|
+
if (adUnitCode) {
|
|
2423
|
+
const cachedEntry = {
|
|
2424
|
+
bidTrace,
|
|
2425
|
+
auctionId,
|
|
2426
|
+
transactionId,
|
|
2427
|
+
adUnitCode,
|
|
2428
|
+
rawBid: data,
|
|
2429
|
+
timestamp: Date.now()
|
|
2430
|
+
};
|
|
2431
|
+
this.cachedWinningBids.set(adUnitCode, cachedEntry);
|
|
2432
|
+
if (data.adId && data.adId !== adUnitCode) {
|
|
2433
|
+
this.cachedWinningBids.set(data.adId, cachedEntry);
|
|
2739
2434
|
}
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
try {
|
|
2745
|
-
adsManager.removeEventListener(type, handler);
|
|
2746
|
-
} catch {
|
|
2435
|
+
if (auctionId) {
|
|
2436
|
+
this.cachedWinningBids.set(`${auctionId}:${adUnitCode}`, cachedEntry);
|
|
2437
|
+
if (data.adId && data.adId !== adUnitCode) {
|
|
2438
|
+
this.cachedWinningBids.set(`${auctionId}:${data.adId}`, cachedEntry);
|
|
2747
2439
|
}
|
|
2748
2440
|
}
|
|
2749
|
-
this.
|
|
2750
|
-
};
|
|
2751
|
-
attached.set(this, cleanup);
|
|
2752
|
-
this.videoDetachCleanups.add(cleanup);
|
|
2753
|
-
return cleanup;
|
|
2754
|
-
}
|
|
2755
|
-
/**
|
|
2756
|
-
* Attaches render hooks and viewability tracking to a video player.
|
|
2757
|
-
* Supports HTML5 <video> elements, container elements, or Google IMA AdsManager.
|
|
2758
|
-
*/
|
|
2759
|
-
attachVideoPlayer(target, options) {
|
|
2760
|
-
if (!target) {
|
|
2761
|
-
this.log("WARN", "attachVideoPlayer called with null/undefined target");
|
|
2762
|
-
return () => {
|
|
2763
|
-
};
|
|
2441
|
+
this.pruneCachedWinningBids();
|
|
2764
2442
|
}
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
const slotId = options?.slotId || options?.adUnitCode || (el ? el.id : "") || "video";
|
|
2787
|
-
const adUnitCode = options?.adUnitCode || slotId;
|
|
2788
|
-
const auctionId = options?.auctionId || "";
|
|
2789
|
-
const transactionId = options?.transactionId || "";
|
|
2790
|
-
const getWinningBid = () => {
|
|
2791
|
-
return (auctionId ? this.getCachedBid(`${auctionId}:${adUnitCode}`) : void 0) || (auctionId && slotId !== adUnitCode ? this.getCachedBid(`${auctionId}:${slotId}`) : void 0) || this.getCachedBid(adUnitCode) || (slotId !== adUnitCode ? this.getCachedBid(slotId) : void 0);
|
|
2792
|
-
};
|
|
2793
|
-
let videoEl = null;
|
|
2794
|
-
if (el) {
|
|
2795
|
-
if (el.tagName && el.tagName.toLowerCase() === "video") {
|
|
2796
|
-
videoEl = el;
|
|
2443
|
+
this.enqueue(TraceEventType.BID_WIN, "bidWon", {
|
|
2444
|
+
auctionId,
|
|
2445
|
+
transactionId,
|
|
2446
|
+
adUnitCode,
|
|
2447
|
+
bid: bidTrace
|
|
2448
|
+
});
|
|
2449
|
+
}
|
|
2450
|
+
handleNoBid(data) {
|
|
2451
|
+
if (this.isDuplicate("noBid", data)) return;
|
|
2452
|
+
this.log("DEBUG", "noBid", data);
|
|
2453
|
+
const auctionId = data.auctionId || "";
|
|
2454
|
+
const bidder = data.bidderCode || data.bidder || "";
|
|
2455
|
+
const adUnitCode = data.adUnitCode || "";
|
|
2456
|
+
if (auctionId && bidder) {
|
|
2457
|
+
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
2458
|
+
const key = `${bidder}:${adUnitCode}`;
|
|
2459
|
+
const entry = auctionMap.get(key);
|
|
2460
|
+
if (entry) {
|
|
2461
|
+
if (entry.outcome === BidderOutcome.BIDDER_OUTCOME_NO_RESPONSE) {
|
|
2462
|
+
entry.outcome = BidderOutcome.BIDDER_OUTCOME_NO_BID;
|
|
2463
|
+
}
|
|
2797
2464
|
} else {
|
|
2798
|
-
|
|
2465
|
+
auctionMap.set(key, {
|
|
2466
|
+
bidder,
|
|
2467
|
+
adUnitCode,
|
|
2468
|
+
mediaType: "banner",
|
|
2469
|
+
outcome: BidderOutcome.BIDDER_OUTCOME_NO_BID,
|
|
2470
|
+
latencyMs: 0
|
|
2471
|
+
});
|
|
2799
2472
|
}
|
|
2800
2473
|
}
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2474
|
+
}
|
|
2475
|
+
handleAdRenderFailed(data) {
|
|
2476
|
+
if (this.isDuplicate("adRenderFailed", data)) return;
|
|
2477
|
+
this.log("DEBUG", "adRenderFailed", data);
|
|
2478
|
+
const bid = data.bid || {};
|
|
2479
|
+
this.enqueue(TraceEventType.AD_RENDER_FAILED, "adRenderFailed", {
|
|
2480
|
+
auctionId: bid.auctionId || data.auctionId || "",
|
|
2481
|
+
transactionId: bid.transactionId || data.transactionId || "",
|
|
2482
|
+
adUnitCode: bid.adUnitCode || data.adUnitCode || "",
|
|
2483
|
+
bid: {
|
|
2484
|
+
bidder: bid.bidderCode || bid.bidder || data.bidderCode || data.bidder || ""
|
|
2485
|
+
},
|
|
2486
|
+
metadata: {
|
|
2487
|
+
reason: String(data.reason || ""),
|
|
2488
|
+
message: String(data.message || data.error?.message || "")
|
|
2489
|
+
},
|
|
2490
|
+
error: data.error || (data.message ? new Error(String(data.message)) : void 0)
|
|
2491
|
+
});
|
|
2492
|
+
}
|
|
2493
|
+
// Prebid's adRenderSucceeded payload is { doc, bid, adId }: the winning bid
|
|
2494
|
+
// lives under data.bid, unlike the flat bidWon payload.
|
|
2495
|
+
handleAdRenderSucceeded(data) {
|
|
2496
|
+
if (this.isDuplicate("adRenderSucceeded", data)) return;
|
|
2497
|
+
this.log("DEBUG", "adRenderSucceeded", data);
|
|
2498
|
+
const bid = data.bid || data || {};
|
|
2499
|
+
const adUnitCode = data.adUnitCode || bid.adUnitCode || data.adId || bid.adId || "";
|
|
2500
|
+
const auctionId = bid.auctionId || data.auctionId || "";
|
|
2501
|
+
const transactionId = bid.transactionId || data.transactionId || "";
|
|
2502
|
+
const mediaType = bid.mediaType || "banner";
|
|
2503
|
+
const slotKey = data.adUnitCode || bid.adUnitCode || "";
|
|
2504
|
+
const altKey = data.adId || bid.adId || "";
|
|
2505
|
+
const cached = (auctionId && slotKey ? this.getCachedBid(`${auctionId}:${slotKey}`) : void 0) || (auctionId && altKey ? this.getCachedBid(`${auctionId}:${altKey}`) : void 0) || (slotKey ? this.getCachedBid(slotKey) : void 0) || (altKey ? this.getCachedBid(altKey) : void 0);
|
|
2506
|
+
const isVideo = mediaType === "video" || bid.mediaType === "video" || bid.mediaTypes?.video !== void 0 || cached?.bidTrace?.mediaType === "video" || cached?.rawBid?.mediaType === "video" || adUnitCode && this.slotViewabilityRecords.get(adUnitCode)?.mediaType === "video";
|
|
2507
|
+
const resolvedMediaType = isVideo ? "video" : mediaType || cached?.bidTrace?.mediaType || "banner";
|
|
2508
|
+
const bidTrace = {
|
|
2509
|
+
bidder: bid.bidderCode || bid.bidder || cached?.bidTrace?.bidder || "",
|
|
2510
|
+
cpm: Number.isFinite(bid.originalCpm) ? bid.originalCpm : Number.isFinite(bid.cpm) ? bid.cpm : cached?.bidTrace?.cpm ?? 0,
|
|
2511
|
+
currency: bid.originalCurrency ?? bid.currency ?? cached?.bidTrace?.currency ?? "USD",
|
|
2512
|
+
...parseBidDimensions(bid).width ? parseBidDimensions(bid) : cached?.bidTrace ? { width: cached.bidTrace.width, height: cached.bidTrace.height } : parseBidDimensions(bid),
|
|
2513
|
+
dealId: bid.dealId || cached?.bidTrace?.dealId || "",
|
|
2514
|
+
mediaType: resolvedMediaType,
|
|
2515
|
+
latencyMs: Number.isFinite(bid.timeToRespond) ? bid.timeToRespond : cached?.bidTrace?.latencyMs ?? 0,
|
|
2516
|
+
advertiserDomain: bid.meta?.advertiserDomains?.[0] || cached?.bidTrace?.advertiserDomain || "",
|
|
2517
|
+
creativeId: bid.creativeId || cached?.bidTrace?.creativeId || ""
|
|
2806
2518
|
};
|
|
2807
|
-
const
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
const
|
|
2519
|
+
const resolvedAuctionId = auctionId || cached?.auctionId || "";
|
|
2520
|
+
const resolvedTransactionId = transactionId || cached?.transactionId || "";
|
|
2521
|
+
const record = adUnitCode ? this.slotViewabilityRecords.get(adUnitCode) : void 0;
|
|
2522
|
+
const isDifferentAuction = record && resolvedAuctionId && record.auctionId && record.auctionId !== resolvedAuctionId;
|
|
2523
|
+
const isRefresh = Boolean(adUnitCode && record && isDifferentAuction);
|
|
2524
|
+
if (isRefresh) {
|
|
2525
|
+
this.flushSlotTimeInView(adUnitCode);
|
|
2526
|
+
const nextRefreshIndex = (this.slotRefreshIndices.get(adUnitCode) ?? (record ? record.refreshIndex : 0)) + 1;
|
|
2527
|
+
this.slotRefreshIndices.set(adUnitCode, nextRefreshIndex);
|
|
2528
|
+
this.slotLastAuctionIds.set(adUnitCode, resolvedAuctionId);
|
|
2529
|
+
if (record) {
|
|
2530
|
+
record.refreshIndex = nextRefreshIndex;
|
|
2531
|
+
record.auctionId = resolvedAuctionId;
|
|
2532
|
+
record.transactionId = resolvedTransactionId;
|
|
2533
|
+
record.bidPayload = bidTrace;
|
|
2534
|
+
record.viewableFired = false;
|
|
2535
|
+
record.accumulatedTimeInViewMs = 0;
|
|
2536
|
+
}
|
|
2537
|
+
this.enqueue(TraceEventType.REFRESH, "refresh", {
|
|
2538
|
+
auctionId: resolvedAuctionId,
|
|
2539
|
+
transactionId: resolvedTransactionId,
|
|
2815
2540
|
adUnitCode,
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
mediaType: "video",
|
|
2819
|
-
bid: mergedBid,
|
|
2820
|
-
metadata: {
|
|
2821
|
-
...options?.metadata,
|
|
2822
|
-
media_type: "video",
|
|
2823
|
-
...vEl && Number.isFinite(vEl.duration) ? { video_duration_s: String(Math.round(vEl.duration)) } : {}
|
|
2824
|
-
}
|
|
2541
|
+
bid: bidTrace,
|
|
2542
|
+
metadata: { refresh_index: String(nextRefreshIndex) }
|
|
2825
2543
|
});
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
} catch (e) {
|
|
2830
|
-
this.log("ERROR", "Error in onImpression callback", e);
|
|
2831
|
-
}
|
|
2832
|
-
}
|
|
2833
|
-
};
|
|
2834
|
-
const handlePlaying = (e) => {
|
|
2835
|
-
this.log("DEBUG", "HTML5 video playing event received");
|
|
2836
|
-
const targetVideo = e?.target instanceof HTMLVideoElement ? e.target : videoEl;
|
|
2837
|
-
triggerPlaybackImpression(targetVideo);
|
|
2838
|
-
};
|
|
2839
|
-
const handleTimeUpdate = (e) => {
|
|
2840
|
-
const targetVideo = e?.target instanceof HTMLVideoElement ? e.target : videoEl;
|
|
2841
|
-
if (targetVideo && targetVideo.currentTime > 0) {
|
|
2842
|
-
triggerPlaybackImpression(targetVideo);
|
|
2843
|
-
if (Number.isFinite(targetVideo.duration) && targetVideo.duration > 0) {
|
|
2844
|
-
const progress = targetVideo.currentTime / targetVideo.duration;
|
|
2845
|
-
if (progress >= 0.25 && !quartilesFired.q1) {
|
|
2846
|
-
quartilesFired.q1 = true;
|
|
2847
|
-
options?.onMilestone?.("firstQuartile", slotId);
|
|
2848
|
-
}
|
|
2849
|
-
if (progress >= 0.5 && !quartilesFired.q2) {
|
|
2850
|
-
quartilesFired.q2 = true;
|
|
2851
|
-
options?.onMilestone?.("midpoint", slotId);
|
|
2852
|
-
}
|
|
2853
|
-
if (progress >= 0.75 && !quartilesFired.q3) {
|
|
2854
|
-
quartilesFired.q3 = true;
|
|
2855
|
-
options?.onMilestone?.("thirdQuartile", slotId);
|
|
2856
|
-
}
|
|
2857
|
-
}
|
|
2544
|
+
} else if (adUnitCode) {
|
|
2545
|
+
if (resolvedAuctionId) {
|
|
2546
|
+
this.slotLastAuctionIds.set(adUnitCode, resolvedAuctionId);
|
|
2858
2547
|
}
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
if (!quartilesFired.q4) {
|
|
2862
|
-
quartilesFired.q4 = true;
|
|
2863
|
-
options?.onMilestone?.("complete", slotId);
|
|
2548
|
+
if (!this.slotRefreshIndices.has(adUnitCode)) {
|
|
2549
|
+
this.slotRefreshIndices.set(adUnitCode, 0);
|
|
2864
2550
|
}
|
|
2865
|
-
}
|
|
2866
|
-
const
|
|
2867
|
-
|
|
2868
|
-
const
|
|
2869
|
-
const cached = getWinningBid();
|
|
2870
|
-
const msg = err ? `HTML5 video error code ${err.code}: ${err.message}` : "Video playback error";
|
|
2871
|
-
this.log("WARN", msg);
|
|
2872
|
-
this.enqueue(TraceEventType.AD_RENDER_FAILED, "adRenderFailed", {
|
|
2873
|
-
auctionId: auctionId || cached?.auctionId,
|
|
2874
|
-
transactionId: transactionId || cached?.transactionId,
|
|
2551
|
+
}
|
|
2552
|
+
const currentRefreshIndex = adUnitCode ? this.slotRefreshIndices.get(adUnitCode) ?? (record ? record.refreshIndex : 0) : 0;
|
|
2553
|
+
if (!isVideo) {
|
|
2554
|
+
const alreadyEmitted = adUnitCode ? this.hasImpressionEmitted(
|
|
2875
2555
|
adUnitCode,
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
} catch (e2) {
|
|
2889
|
-
this.log("ERROR", "Error in onError callback", e2);
|
|
2556
|
+
currentRefreshIndex,
|
|
2557
|
+
resolvedAuctionId,
|
|
2558
|
+
bidTrace.creativeId
|
|
2559
|
+
) : false;
|
|
2560
|
+
if (!alreadyEmitted) {
|
|
2561
|
+
if (adUnitCode) {
|
|
2562
|
+
this.markImpressionEmitted(
|
|
2563
|
+
adUnitCode,
|
|
2564
|
+
currentRefreshIndex,
|
|
2565
|
+
resolvedAuctionId,
|
|
2566
|
+
bidTrace.creativeId
|
|
2567
|
+
);
|
|
2890
2568
|
}
|
|
2569
|
+
this.enqueue(TraceEventType.IMPRESSION, "impression", {
|
|
2570
|
+
auctionId: resolvedAuctionId,
|
|
2571
|
+
transactionId: resolvedTransactionId,
|
|
2572
|
+
adUnitCode,
|
|
2573
|
+
bid: bidTrace,
|
|
2574
|
+
metadata: { refresh_index: String(currentRefreshIndex) }
|
|
2575
|
+
});
|
|
2891
2576
|
}
|
|
2892
|
-
}
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
targetVideo.addEventListener("ended", handleEnded);
|
|
2898
|
-
targetVideo.addEventListener("error", handleError);
|
|
2899
|
-
};
|
|
2900
|
-
const removeListenersFromVideo = (targetVideo) => {
|
|
2901
|
-
targetVideo.removeEventListener("playing", handlePlaying);
|
|
2902
|
-
targetVideo.removeEventListener("play", handlePlaying);
|
|
2903
|
-
targetVideo.removeEventListener("timeupdate", handleTimeUpdate);
|
|
2904
|
-
targetVideo.removeEventListener("ended", handleEnded);
|
|
2905
|
-
targetVideo.removeEventListener("error", handleError);
|
|
2906
|
-
};
|
|
2907
|
-
let mutationObserver = null;
|
|
2908
|
-
if (el && el !== videoEl) {
|
|
2909
|
-
el.addEventListener("playing", handlePlaying, true);
|
|
2910
|
-
el.addEventListener("play", handlePlaying, true);
|
|
2911
|
-
el.addEventListener("timeupdate", handleTimeUpdate, true);
|
|
2912
|
-
el.addEventListener("ended", handleEnded, true);
|
|
2913
|
-
el.addEventListener("error", handleError, true);
|
|
2914
|
-
}
|
|
2915
|
-
if (videoEl) {
|
|
2916
|
-
attachListenersToVideo(videoEl);
|
|
2917
|
-
}
|
|
2918
|
-
if (el && el.tagName?.toLowerCase() !== "video" && typeof MutationObserver !== "undefined") {
|
|
2919
|
-
mutationObserver = new MutationObserver(() => {
|
|
2920
|
-
const found = el.querySelector("video");
|
|
2921
|
-
if (found && found !== videoEl) {
|
|
2922
|
-
if (videoEl) removeListenersFromVideo(videoEl);
|
|
2923
|
-
videoEl = found;
|
|
2924
|
-
if (videoEl) attachListenersToVideo(videoEl);
|
|
2925
|
-
}
|
|
2926
|
-
});
|
|
2927
|
-
mutationObserver.observe(el, { childList: true, subtree: true });
|
|
2928
|
-
}
|
|
2929
|
-
const cachedInitial = getWinningBid();
|
|
2930
|
-
if (options?.trackViewability !== false && el) {
|
|
2931
|
-
this.observeSlot(el, slotId, {
|
|
2932
|
-
adUnitCode,
|
|
2933
|
-
auctionId: auctionId || cachedInitial?.auctionId,
|
|
2934
|
-
transactionId: transactionId || cachedInitial?.transactionId,
|
|
2935
|
-
mediaType: "video",
|
|
2936
|
-
bid: cachedInitial?.bidTrace || options?.bid,
|
|
2937
|
-
metadata: options?.metadata,
|
|
2938
|
-
emitRefreshEvent: false
|
|
2939
|
-
});
|
|
2577
|
+
} else {
|
|
2578
|
+
this.log(
|
|
2579
|
+
"DEBUG",
|
|
2580
|
+
`adRenderSucceeded received for video ad unit ${adUnitCode || "unknown"}: suppressing IMPRESSION until verified video lifecycle event`
|
|
2581
|
+
);
|
|
2940
2582
|
}
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
if (el && el !== videoEl) {
|
|
2947
|
-
el.removeEventListener("playing", handlePlaying, true);
|
|
2948
|
-
el.removeEventListener("play", handlePlaying, true);
|
|
2949
|
-
el.removeEventListener("timeupdate", handleTimeUpdate, true);
|
|
2950
|
-
el.removeEventListener("ended", handleEnded, true);
|
|
2951
|
-
el.removeEventListener("error", handleError, true);
|
|
2952
|
-
}
|
|
2953
|
-
if (videoEl) {
|
|
2954
|
-
removeListenersFromVideo(videoEl);
|
|
2583
|
+
if (this.config.viewabilityEnabled && typeof document !== "undefined") {
|
|
2584
|
+
let el = adUnitCode && this.slotElements.get(adUnitCode) || null;
|
|
2585
|
+
const targetId = adUnitCode || data.adId || bid.adId;
|
|
2586
|
+
if (!el && targetId) {
|
|
2587
|
+
el = document.getElementById(targetId);
|
|
2955
2588
|
}
|
|
2956
|
-
|
|
2957
|
-
};
|
|
2958
|
-
this.videoDetachCleanups.add(cleanup);
|
|
2959
|
-
return cleanup;
|
|
2960
|
-
}
|
|
2961
|
-
onTimeInViewThreshold(slotId, thresholdMs, callback) {
|
|
2962
|
-
const listener = {
|
|
2963
|
-
thresholdMs,
|
|
2964
|
-
callback,
|
|
2965
|
-
timer: null,
|
|
2966
|
-
fired: false
|
|
2967
|
-
};
|
|
2968
|
-
const record = this.slotViewabilityRecords.get(slotId);
|
|
2969
|
-
if (record) {
|
|
2970
|
-
record.thresholdListeners.add(listener);
|
|
2971
|
-
const currentTotal = this.getCurrentTimeInView(record);
|
|
2972
|
-
if (currentTotal >= thresholdMs) {
|
|
2973
|
-
listener.fired = true;
|
|
2589
|
+
if (!el && typeof window !== "undefined" && window.googletag?.pubads) {
|
|
2974
2590
|
try {
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2591
|
+
const slots = window.googletag.pubads().getSlots();
|
|
2592
|
+
for (const slot of slots) {
|
|
2593
|
+
if (slot.getAdUnitPath?.() === adUnitCode || slot.getSlotElementId?.() === adUnitCode) {
|
|
2594
|
+
el = document.getElementById(slot.getSlotElementId());
|
|
2595
|
+
if (el) break;
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
} catch {
|
|
2978
2599
|
}
|
|
2979
|
-
}
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
listener.fired = true;
|
|
2987
|
-
try {
|
|
2988
|
-
callback(slotId, nowTotal);
|
|
2989
|
-
} catch (e) {
|
|
2990
|
-
this.log("ERROR", "Error in threshold callback", e);
|
|
2991
|
-
}
|
|
2600
|
+
}
|
|
2601
|
+
if (!el && targetId) {
|
|
2602
|
+
try {
|
|
2603
|
+
for (const candidate of Array.from(document.querySelectorAll("[data-ad-slot]"))) {
|
|
2604
|
+
if (candidate.getAttribute("data-ad-slot") === targetId) {
|
|
2605
|
+
el = candidate;
|
|
2606
|
+
break;
|
|
2992
2607
|
}
|
|
2993
2608
|
}
|
|
2994
|
-
}
|
|
2609
|
+
} catch {
|
|
2610
|
+
}
|
|
2995
2611
|
}
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2612
|
+
if (el) {
|
|
2613
|
+
this.observeSlot(el, adUnitCode || targetId, {
|
|
2614
|
+
adUnitCode,
|
|
2615
|
+
auctionId: resolvedAuctionId,
|
|
2616
|
+
transactionId: resolvedTransactionId,
|
|
2617
|
+
mediaType: resolvedMediaType,
|
|
2618
|
+
bid: bidTrace,
|
|
2619
|
+
refreshIndex: currentRefreshIndex,
|
|
2620
|
+
emitRefreshEvent: false
|
|
2621
|
+
});
|
|
2999
2622
|
}
|
|
3000
|
-
this.pendingThresholdListeners.get(slotId).add(listener);
|
|
3001
2623
|
}
|
|
3002
|
-
return () => {
|
|
3003
|
-
if (listener.timer) {
|
|
3004
|
-
clearTimeout(listener.timer);
|
|
3005
|
-
listener.timer = null;
|
|
3006
|
-
}
|
|
3007
|
-
if (record) {
|
|
3008
|
-
record.thresholdListeners.delete(listener);
|
|
3009
|
-
}
|
|
3010
|
-
const pending = this.pendingThresholdListeners.get(slotId);
|
|
3011
|
-
if (pending) {
|
|
3012
|
-
pending.delete(listener);
|
|
3013
|
-
}
|
|
3014
|
-
};
|
|
3015
|
-
}
|
|
3016
|
-
getViewabilityState(slotId) {
|
|
3017
|
-
const record = this.slotViewabilityRecords.get(slotId);
|
|
3018
|
-
if (!record) return void 0;
|
|
3019
|
-
const timeInViewMs = Math.round(this.getCurrentTimeInView(record));
|
|
3020
|
-
return {
|
|
3021
|
-
inView: record.inView,
|
|
3022
|
-
viewable: record.viewableFired,
|
|
3023
|
-
timeInViewMs,
|
|
3024
|
-
refreshIndex: record.refreshIndex
|
|
3025
|
-
};
|
|
3026
|
-
}
|
|
3027
|
-
/** The validated ingest endpoint in use, or "" when none passed validation. */
|
|
3028
|
-
getEndpoint() {
|
|
3029
|
-
return this.config.endpoint;
|
|
3030
2624
|
}
|
|
3031
|
-
|
|
3032
|
-
return this.
|
|
2625
|
+
isDuplicate(eventName, data) {
|
|
2626
|
+
return this.deduper.isDuplicate(eventName, data);
|
|
3033
2627
|
}
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
if (
|
|
3037
|
-
|
|
2628
|
+
enqueue(type, eventName, data, level) {
|
|
2629
|
+
if (!this.isEnabled) return;
|
|
2630
|
+
if (!this.shouldSample(type, level)) return;
|
|
2631
|
+
extendSession();
|
|
2632
|
+
const protoEvent = {
|
|
2633
|
+
eventId: generateUUID(),
|
|
2634
|
+
timestampMs: Date.now(),
|
|
2635
|
+
type,
|
|
2636
|
+
eventName,
|
|
2637
|
+
auctionId: data.auctionId || "",
|
|
2638
|
+
transactionId: data.transactionId || "",
|
|
2639
|
+
adUnitCode: data.adUnitCode || data.elementId || "",
|
|
2640
|
+
namespace: data.ns || data.namespace || ""
|
|
2641
|
+
};
|
|
2642
|
+
if (data.bid) {
|
|
2643
|
+
protoEvent.bid = {
|
|
2644
|
+
bidder: data.bid.bidder || "",
|
|
2645
|
+
cpm: Number.isFinite(data.bid.cpm) ? data.bid.cpm : 0,
|
|
2646
|
+
// No defaults here: handlers with a priced bid (bidResponse, bidWon,
|
|
2647
|
+
// impression) set currency/mediaType themselves. Defaulting for the
|
|
2648
|
+
// rest would stamp fake "USD"/"banner" on noBid and bidRequest rows.
|
|
2649
|
+
currency: data.bid.currency || "",
|
|
2650
|
+
width: Number.isFinite(data.bid.width) ? data.bid.width : 0,
|
|
2651
|
+
height: Number.isFinite(data.bid.height) ? data.bid.height : 0,
|
|
2652
|
+
dealId: data.bid.dealId || "",
|
|
2653
|
+
mediaType: data.bid.mediaType || "",
|
|
2654
|
+
latencyMs: Number.isFinite(data.bid.latencyMs) ? data.bid.latencyMs : 0,
|
|
2655
|
+
advertiserDomain: data.bid.advertiserDomain || "",
|
|
2656
|
+
creativeId: data.bid.creativeId || ""
|
|
2657
|
+
};
|
|
2658
|
+
}
|
|
2659
|
+
if (data.viewableDurationMs !== void 0) {
|
|
2660
|
+
protoEvent.viewableDurationMs = Number.isFinite(data.viewableDurationMs) ? data.viewableDurationMs : 0;
|
|
2661
|
+
}
|
|
2662
|
+
if (data.bidderOutcomes && Array.isArray(data.bidderOutcomes)) {
|
|
2663
|
+
protoEvent.bidderOutcomes = data.bidderOutcomes;
|
|
2664
|
+
}
|
|
2665
|
+
const metadata = { ...data.metadata };
|
|
2666
|
+
if (data.error !== void 0 && data.error !== null) {
|
|
2667
|
+
metadata.error = typeof data.error === "object" && data.error.message ? String(data.error.message) : String(data.error);
|
|
2668
|
+
}
|
|
2669
|
+
if (data.gpid) {
|
|
2670
|
+
metadata.gpid = String(data.gpid);
|
|
2671
|
+
}
|
|
2672
|
+
if (data.refreshIndex !== void 0 && metadata.refresh_index === void 0) {
|
|
2673
|
+
metadata.refresh_index = String(data.refreshIndex);
|
|
2674
|
+
}
|
|
2675
|
+
const boundedMetadata = boundMetadata(metadata);
|
|
2676
|
+
if (Object.keys(boundedMetadata).length > 0) {
|
|
2677
|
+
protoEvent.metadata = boundedMetadata;
|
|
2678
|
+
}
|
|
2679
|
+
this.queue.push(protoEvent);
|
|
2680
|
+
if (this.queue.length > MAX_QUEUE_SIZE) {
|
|
2681
|
+
this.queue.splice(0, this.queue.length - MAX_QUEUE_SIZE);
|
|
2682
|
+
}
|
|
2683
|
+
if (this.queue.length >= BATCH_SIZE) {
|
|
2684
|
+
this.flush();
|
|
2685
|
+
} else if (REVENUE_FLUSH_TYPES.has(type) && this.revenueFlushTimer === null) {
|
|
2686
|
+
this.revenueFlushTimer = setTimeout(() => {
|
|
2687
|
+
this.revenueFlushTimer = null;
|
|
2688
|
+
this.flush();
|
|
2689
|
+
}, this.config.revenueFlushDelayMs);
|
|
3038
2690
|
}
|
|
3039
2691
|
}
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
2692
|
+
shouldSample(type, level) {
|
|
2693
|
+
if (type === TraceEventType.ERROR) {
|
|
2694
|
+
this.errorCount++;
|
|
2695
|
+
if (this.errorCount > MAX_ERRORS_PER_SESSION) return false;
|
|
2696
|
+
return this.config.errorsEnabled;
|
|
2697
|
+
}
|
|
2698
|
+
if (level === "WARN") {
|
|
2699
|
+
return this.config.warningsEnabled;
|
|
2700
|
+
}
|
|
2701
|
+
return this.config.auctionEnabled;
|
|
3043
2702
|
}
|
|
3044
|
-
|
|
3045
|
-
|
|
2703
|
+
requeue(events) {
|
|
2704
|
+
if (!events.length) return;
|
|
2705
|
+
this.queue = events.concat(this.queue);
|
|
2706
|
+
if (this.queue.length > MAX_QUEUE_SIZE) {
|
|
2707
|
+
this.queue.splice(0, this.queue.length - MAX_QUEUE_SIZE);
|
|
2708
|
+
}
|
|
3046
2709
|
}
|
|
3047
|
-
|
|
3048
|
-
|
|
2710
|
+
// Drains a batch (up to MAX_PAYLOAD_BYTES) from the queue.
|
|
2711
|
+
// Shared by flush() and flushBeacon().
|
|
2712
|
+
drainBatch() {
|
|
2713
|
+
if (this.queue.length === 0 || !this.config.endpoint) return null;
|
|
2714
|
+
const domain = typeof window !== "undefined" ? window.location.hostname : "";
|
|
2715
|
+
const buildBatch = (evts) => ({
|
|
2716
|
+
propertyId: this.config.propertyId,
|
|
2717
|
+
pageviewId: this.config.pageviewId,
|
|
2718
|
+
sessionId: this.config.sessionId,
|
|
2719
|
+
pageUrl: this.pageUrl,
|
|
2720
|
+
country: this.config.country,
|
|
2721
|
+
region: this.config.region,
|
|
2722
|
+
deviceType: this.config.deviceType,
|
|
2723
|
+
userId: this.config.userId,
|
|
2724
|
+
domain,
|
|
2725
|
+
events: evts
|
|
2726
|
+
});
|
|
2727
|
+
for (; ; ) {
|
|
2728
|
+
if (this.queue.length === 0) return null;
|
|
2729
|
+
let events = this.queue;
|
|
2730
|
+
let encoded = TraceEventBatch.encode(
|
|
2731
|
+
buildBatch(events)
|
|
2732
|
+
).finish();
|
|
2733
|
+
while (events.length > 1 && encoded.byteLength > MAX_PAYLOAD_BYTES) {
|
|
2734
|
+
events = events.slice(0, Math.floor(events.length / 2));
|
|
2735
|
+
encoded = TraceEventBatch.encode(
|
|
2736
|
+
buildBatch(events)
|
|
2737
|
+
).finish();
|
|
2738
|
+
}
|
|
2739
|
+
if (encoded.byteLength > MAX_PAYLOAD_BYTES) {
|
|
2740
|
+
const [dropped] = this.queue.splice(0, 1);
|
|
2741
|
+
this.log(
|
|
2742
|
+
"WARN",
|
|
2743
|
+
`Dropping oversized event ${dropped?.eventName || dropped?.eventId} (${encoded.byteLength} bytes exceeds the ${MAX_PAYLOAD_BYTES}-byte cap on its own)`
|
|
2744
|
+
);
|
|
2745
|
+
continue;
|
|
2746
|
+
}
|
|
2747
|
+
this.queue = this.queue.slice(events.length);
|
|
2748
|
+
return {
|
|
2749
|
+
url: `${this.config.endpoint}/${this.config.propertyId}`,
|
|
2750
|
+
// protobufjs types finish() as Uint8Array<ArrayBufferLike>; the buffer is
|
|
2751
|
+
// always a plain ArrayBuffer, so narrow for fetch/Blob compatibility.
|
|
2752
|
+
encoded,
|
|
2753
|
+
events
|
|
2754
|
+
};
|
|
2755
|
+
}
|
|
3049
2756
|
}
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
2757
|
+
sendFetch(payload, useKeepalive = false, onDelivered, isReplay = false) {
|
|
2758
|
+
const fetchOpts = {
|
|
2759
|
+
method: "POST",
|
|
2760
|
+
// Safelisted content type: no CORS preflight (see SAFE_CONTENT_TYPE).
|
|
2761
|
+
headers: { "Content-Type": SAFE_CONTENT_TYPE },
|
|
2762
|
+
body: payload.encoded
|
|
2763
|
+
};
|
|
2764
|
+
if (useKeepalive) {
|
|
2765
|
+
fetchOpts.keepalive = true;
|
|
2766
|
+
}
|
|
2767
|
+
fetch(payload.url, fetchOpts).then((res) => {
|
|
2768
|
+
if (!res.ok) {
|
|
2769
|
+
this.log("WARN", `Failed to send batch: HTTP ${res.status}`);
|
|
2770
|
+
if (res.status >= 400 && res.status < 500 && res.status !== 429 && res.status !== 408) {
|
|
2771
|
+
this.log("WARN", `Dropping batch due to non-retryable client error HTTP ${res.status}`);
|
|
2772
|
+
return;
|
|
2773
|
+
}
|
|
2774
|
+
if (!isReplay) this.handleSendFailure(payload.events);
|
|
2775
|
+
} else {
|
|
2776
|
+
if (!isReplay) {
|
|
2777
|
+
this.consecutiveSendFailures = 0;
|
|
2778
|
+
this.nextSendAllowedAt = 0;
|
|
2779
|
+
}
|
|
2780
|
+
if (onDelivered) onDelivered();
|
|
2781
|
+
}
|
|
2782
|
+
}).catch((err) => {
|
|
2783
|
+
this.log("ERROR", "Failed to send batch", err);
|
|
2784
|
+
if (!isReplay) this.handleSendFailure(payload.events);
|
|
2785
|
+
});
|
|
2786
|
+
const proc = typeof globalThis !== "undefined" ? globalThis.process : void 0;
|
|
2787
|
+
if (proc && typeof proc._tickCallback === "function") {
|
|
3056
2788
|
try {
|
|
3057
|
-
|
|
3058
|
-
this.pageUrl = u.origin + u.pathname;
|
|
2789
|
+
proc._tickCallback();
|
|
3059
2790
|
} catch {
|
|
3060
|
-
this.pageUrl = pageUrl;
|
|
3061
2791
|
}
|
|
3062
|
-
}
|
|
3063
|
-
this.pageUrl = "";
|
|
3064
|
-
}
|
|
3065
|
-
this.logger.resetTiming();
|
|
3066
|
-
extendSession();
|
|
3067
|
-
this.enable();
|
|
2792
|
+
}
|
|
3068
2793
|
}
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
2794
|
+
handleSendFailure(events) {
|
|
2795
|
+
this.consecutiveSendFailures++;
|
|
2796
|
+
const backoff = Math.min(
|
|
2797
|
+
FLUSH_INTERVAL_MS * 2 ** (this.consecutiveSendFailures - 1),
|
|
2798
|
+
MAX_SEND_BACKOFF_MS
|
|
2799
|
+
);
|
|
2800
|
+
this.nextSendAllowedAt = Date.now() + backoff;
|
|
2801
|
+
if (this.consecutiveSendFailures <= MAX_CONSECUTIVE_SEND_FAILURES) {
|
|
2802
|
+
this.requeue(events);
|
|
2803
|
+
} else {
|
|
2804
|
+
this.log(
|
|
2805
|
+
"WARN",
|
|
2806
|
+
`Dropping ${events.length} events after ${this.consecutiveSendFailures} consecutive send failures`
|
|
2807
|
+
);
|
|
3076
2808
|
}
|
|
3077
|
-
this.enqueue(TraceEventType.AUCTION_START, "auctionStart", {
|
|
3078
|
-
auctionId,
|
|
3079
|
-
transactionId: data.transactionId || ""
|
|
3080
|
-
});
|
|
3081
2809
|
}
|
|
3082
|
-
|
|
3083
|
-
if (
|
|
3084
|
-
this.
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
this.auctionBidderOutcomes.delete(auctionId);
|
|
2810
|
+
flush() {
|
|
2811
|
+
if (Date.now() < this.nextSendAllowedAt) return;
|
|
2812
|
+
let payload = this.drainBatch();
|
|
2813
|
+
while (payload) {
|
|
2814
|
+
this.sendFetch(payload, false);
|
|
2815
|
+
if (Date.now() < this.nextSendAllowedAt) break;
|
|
2816
|
+
payload = this.drainBatch();
|
|
3090
2817
|
}
|
|
3091
|
-
this.enqueue(TraceEventType.AUCTION_END, "auctionEnd", {
|
|
3092
|
-
auctionId,
|
|
3093
|
-
transactionId: data.transactionId || "",
|
|
3094
|
-
bidderOutcomes
|
|
3095
|
-
});
|
|
3096
2818
|
}
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
const mediaTypes = Object.keys(bid.mediaTypes || {});
|
|
3108
|
-
const mediaType = mediaTypes.length > 0 ? mediaTypes[0] : bid.mediaType || "banner";
|
|
3109
|
-
const key = `${bidder}:${adUnitCode}`;
|
|
3110
|
-
if (!auctionMap.has(key)) {
|
|
3111
|
-
auctionMap.set(key, {
|
|
3112
|
-
bidder,
|
|
3113
|
-
adUnitCode,
|
|
3114
|
-
mediaType,
|
|
3115
|
-
outcome: BidderOutcome.BIDDER_OUTCOME_NO_RESPONSE,
|
|
3116
|
-
latencyMs: 0
|
|
3117
|
-
});
|
|
2819
|
+
flushBeacon() {
|
|
2820
|
+
let payload = this.drainBatch();
|
|
2821
|
+
while (payload) {
|
|
2822
|
+
let sent = false;
|
|
2823
|
+
if (typeof navigator !== "undefined" && typeof navigator.sendBeacon === "function") {
|
|
2824
|
+
try {
|
|
2825
|
+
const blob = new Blob([payload.encoded], { type: SAFE_CONTENT_TYPE });
|
|
2826
|
+
sent = navigator.sendBeacon(payload.url, blob);
|
|
2827
|
+
} catch {
|
|
2828
|
+
sent = false;
|
|
3118
2829
|
}
|
|
3119
|
-
}
|
|
2830
|
+
}
|
|
2831
|
+
if (!sent) {
|
|
2832
|
+
const persistKey = this.persistBatch(payload.encoded);
|
|
2833
|
+
this.sendFetch(payload, true, () => this.removePersistedBatch(persistKey));
|
|
2834
|
+
}
|
|
2835
|
+
payload = this.drainBatch();
|
|
3120
2836
|
}
|
|
2837
|
+
this.schedulePersistedCleanup();
|
|
3121
2838
|
}
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
entry.outcome = outcome;
|
|
3139
|
-
entry.latencyMs = latencyMs;
|
|
3140
|
-
if (mediaType) entry.mediaType = mediaType;
|
|
3141
|
-
} else {
|
|
3142
|
-
auctionMap.set(key, {
|
|
3143
|
-
bidder,
|
|
3144
|
-
adUnitCode,
|
|
3145
|
-
mediaType,
|
|
3146
|
-
outcome,
|
|
3147
|
-
latencyMs
|
|
3148
|
-
});
|
|
2839
|
+
// --- Exit-batch persistence -----------------------------------------------
|
|
2840
|
+
// Key prefix for this instance's property. encodeURIComponent keeps the
|
|
2841
|
+
// property segment free of the "_" the timestamp suffix is parsed against,
|
|
2842
|
+
// so a property ID containing one cannot forge another property's prefix.
|
|
2843
|
+
pendingKeyPrefix() {
|
|
2844
|
+
return `${PENDING_BATCH_KEY_PREFIX}${encodeURIComponent(this.config.propertyId)}_`;
|
|
2845
|
+
}
|
|
2846
|
+
persistBatch(encoded) {
|
|
2847
|
+
if (!storageAllowed) return null;
|
|
2848
|
+
try {
|
|
2849
|
+
if (typeof localStorage === "undefined") return null;
|
|
2850
|
+
if (!this.config.propertyId) return null;
|
|
2851
|
+
let pendingCount = 0;
|
|
2852
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
2853
|
+
const k = localStorage.key(i);
|
|
2854
|
+
if (k && k.startsWith(PENDING_BATCH_KEY_PREFIX)) pendingCount++;
|
|
3149
2855
|
}
|
|
2856
|
+
if (pendingCount >= MAX_PENDING_BATCHES) return null;
|
|
2857
|
+
const key = `${this.pendingKeyPrefix()}${Date.now()}_${Math.floor(Math.random() * 1e6)}`;
|
|
2858
|
+
localStorage.setItem(key, bytesToBase64(encoded));
|
|
2859
|
+
this.persistedBatchKeys.push(key);
|
|
2860
|
+
return key;
|
|
2861
|
+
} catch {
|
|
2862
|
+
return null;
|
|
3150
2863
|
}
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
bidder,
|
|
3158
|
-
cpm,
|
|
3159
|
-
currency: data.originalCurrency ?? data.currency ?? "USD",
|
|
3160
|
-
...parseBidDimensions(data),
|
|
3161
|
-
dealId: data.dealId || "",
|
|
3162
|
-
mediaType,
|
|
3163
|
-
latencyMs,
|
|
3164
|
-
advertiserDomain: data.meta?.advertiserDomains?.[0] || "",
|
|
3165
|
-
creativeId: data.creativeId || ""
|
|
3166
|
-
}
|
|
3167
|
-
});
|
|
2864
|
+
}
|
|
2865
|
+
removePersistedBatch(key) {
|
|
2866
|
+
if (!key) return;
|
|
2867
|
+
try {
|
|
2868
|
+
if (typeof localStorage !== "undefined") localStorage.removeItem(key);
|
|
2869
|
+
} catch {
|
|
3168
2870
|
}
|
|
2871
|
+
const idx = this.persistedBatchKeys.indexOf(key);
|
|
2872
|
+
if (idx !== -1) this.persistedBatchKeys.splice(idx, 1);
|
|
3169
2873
|
}
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
const
|
|
3179
|
-
|
|
3180
|
-
const auctionMap = this.getOrCreateAuctionOutcomes(auctionId);
|
|
3181
|
-
const key = `${bidder}:${adUnitCode}`;
|
|
3182
|
-
const entry = auctionMap.get(key);
|
|
3183
|
-
if (entry) {
|
|
3184
|
-
entry.outcome = BidderOutcome.BIDDER_OUTCOME_TIMEOUT;
|
|
3185
|
-
if (latencyMs > 0) entry.latencyMs = latencyMs;
|
|
3186
|
-
} else {
|
|
3187
|
-
auctionMap.set(key, {
|
|
3188
|
-
bidder,
|
|
3189
|
-
adUnitCode,
|
|
3190
|
-
mediaType: "banner",
|
|
3191
|
-
outcome: BidderOutcome.BIDDER_OUTCOME_TIMEOUT,
|
|
3192
|
-
latencyMs
|
|
3193
|
-
});
|
|
3194
|
-
}
|
|
2874
|
+
// A page that is still running PENDING_BATCH_CLEANUP_DELAY_MS after an exit
|
|
2875
|
+
// flush was never torn down, so its beacons have long since gone out; drop
|
|
2876
|
+
// the persisted copies rather than letting a later pageview resend them.
|
|
2877
|
+
schedulePersistedCleanup() {
|
|
2878
|
+
if (this.persistedBatchKeys.length === 0) return;
|
|
2879
|
+
if (this.persistedCleanupTimer) clearTimeout(this.persistedCleanupTimer);
|
|
2880
|
+
this.persistedCleanupTimer = setTimeout(() => {
|
|
2881
|
+
this.persistedCleanupTimer = null;
|
|
2882
|
+
for (const key of Array.from(this.persistedBatchKeys)) {
|
|
2883
|
+
this.removePersistedBatch(key);
|
|
3195
2884
|
}
|
|
3196
|
-
}
|
|
2885
|
+
}, PENDING_BATCH_CLEANUP_DELAY_MS);
|
|
3197
2886
|
}
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
this.cachedWinningBids.set(adUnitCode, cachedEntry);
|
|
3227
|
-
if (data.adId && data.adId !== adUnitCode) {
|
|
3228
|
-
this.cachedWinningBids.set(data.adId, cachedEntry);
|
|
2887
|
+
// Resend batches a previous pageview persisted at exit but could not
|
|
2888
|
+
// confirm. Only this property's own keys are claimed: batches belonging to
|
|
2889
|
+
// another property sharing this origin are left for that property's next
|
|
2890
|
+
// pageview, since resending them here would file them under this property.
|
|
2891
|
+
// Keys are claimed (removed) before sending so concurrent tabs don't
|
|
2892
|
+
// double-send; the batch bytes carry their original pageview, session, and
|
|
2893
|
+
// event timestamps, so late rows attribute correctly.
|
|
2894
|
+
resendPersistedBatches() {
|
|
2895
|
+
try {
|
|
2896
|
+
if (typeof localStorage === "undefined" || !this.config.endpoint) return;
|
|
2897
|
+
if (!this.config.propertyId) return;
|
|
2898
|
+
const ownPrefix = this.pendingKeyPrefix();
|
|
2899
|
+
const keys = [];
|
|
2900
|
+
const legacyKeys = [];
|
|
2901
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
2902
|
+
const k = localStorage.key(i);
|
|
2903
|
+
if (!k || !k.startsWith(PENDING_BATCH_KEY_PREFIX)) continue;
|
|
2904
|
+
if (k.startsWith(ownPrefix)) {
|
|
2905
|
+
keys.push(k);
|
|
2906
|
+
} else if (isLegacyPendingKey(k)) {
|
|
2907
|
+
legacyKeys.push(k);
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
for (const key of legacyKeys) {
|
|
2911
|
+
try {
|
|
2912
|
+
localStorage.removeItem(key);
|
|
2913
|
+
} catch {
|
|
2914
|
+
}
|
|
3229
2915
|
}
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
2916
|
+
for (const key of keys) {
|
|
2917
|
+
let value = null;
|
|
2918
|
+
try {
|
|
2919
|
+
value = localStorage.getItem(key);
|
|
2920
|
+
localStorage.removeItem(key);
|
|
2921
|
+
} catch {
|
|
2922
|
+
continue;
|
|
2923
|
+
}
|
|
2924
|
+
if (!value) continue;
|
|
2925
|
+
const ts = parseInt(key.slice(ownPrefix.length), 10);
|
|
2926
|
+
if (!Number.isFinite(ts) || Date.now() - ts > PENDING_BATCH_MAX_AGE_MS) continue;
|
|
2927
|
+
let encoded;
|
|
2928
|
+
try {
|
|
2929
|
+
encoded = base64ToBytes(value);
|
|
2930
|
+
} catch {
|
|
2931
|
+
continue;
|
|
3234
2932
|
}
|
|
2933
|
+
if (encoded.byteLength === 0) continue;
|
|
2934
|
+
this.log("DEBUG", `Resending persisted exit batch (${encoded.byteLength} bytes)`);
|
|
2935
|
+
this.sendFetch(
|
|
2936
|
+
{
|
|
2937
|
+
url: `${this.config.endpoint}/${this.config.propertyId}`,
|
|
2938
|
+
encoded,
|
|
2939
|
+
events: []
|
|
2940
|
+
},
|
|
2941
|
+
false,
|
|
2942
|
+
void 0,
|
|
2943
|
+
true
|
|
2944
|
+
);
|
|
3235
2945
|
}
|
|
3236
|
-
|
|
2946
|
+
} catch {
|
|
3237
2947
|
}
|
|
3238
|
-
this.enqueue(TraceEventType.BID_WIN, "bidWon", {
|
|
3239
|
-
auctionId,
|
|
3240
|
-
transactionId,
|
|
3241
|
-
adUnitCode,
|
|
3242
|
-
bid: bidTrace
|
|
3243
|
-
});
|
|
3244
2948
|
}
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
2949
|
+
log(level, msg, ...args) {
|
|
2950
|
+
this.logger.log(level, msg, ...args);
|
|
2951
|
+
}
|
|
2952
|
+
};
|
|
2953
|
+
var defaultLogger = createLogger({
|
|
2954
|
+
prefix: "[bidkernel]",
|
|
2955
|
+
logLevel: "INFO"
|
|
2956
|
+
});
|
|
2957
|
+
function extractAnalyticsOptions(config, allowProviderless = true) {
|
|
2958
|
+
if (!config) return null;
|
|
2959
|
+
if (Array.isArray(config)) {
|
|
2960
|
+
const matched = config.find((item) => item?.provider === "bidkernel") ?? (allowProviderless ? config.find(
|
|
2961
|
+
(item) => item && typeof item === "object" && item.provider === void 0
|
|
2962
|
+
) : void 0);
|
|
2963
|
+
return matched ? extractAnalyticsOptions(matched, allowProviderless) : null;
|
|
2964
|
+
}
|
|
2965
|
+
if (typeof config === "object") {
|
|
2966
|
+
if (config.options && typeof config.options === "object") {
|
|
2967
|
+
return config.options;
|
|
2968
|
+
}
|
|
2969
|
+
if (config.config && typeof config.config === "object") {
|
|
2970
|
+
return config.config;
|
|
2971
|
+
}
|
|
2972
|
+
if (config.propertyId || config.endpoint) {
|
|
2973
|
+
return config;
|
|
3268
2974
|
}
|
|
3269
2975
|
}
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
adUnitCode: bid.adUnitCode || data.adUnitCode || "",
|
|
3278
|
-
bid: {
|
|
3279
|
-
bidder: bid.bidderCode || bid.bidder || data.bidderCode || data.bidder || ""
|
|
3280
|
-
},
|
|
3281
|
-
metadata: {
|
|
3282
|
-
reason: String(data.reason || ""),
|
|
3283
|
-
message: String(data.message || data.error?.message || "")
|
|
3284
|
-
},
|
|
3285
|
-
error: data.error || (data.message ? new Error(String(data.message)) : void 0)
|
|
3286
|
-
});
|
|
2976
|
+
return null;
|
|
2977
|
+
}
|
|
2978
|
+
function getStashedConfig(win) {
|
|
2979
|
+
if (!win) return null;
|
|
2980
|
+
if (win._bidkernelAnalyticsConfig) {
|
|
2981
|
+
const extracted = extractAnalyticsOptions(win._bidkernelAnalyticsConfig);
|
|
2982
|
+
if (extracted) return extracted;
|
|
3287
2983
|
}
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
2984
|
+
if (win.bidkernelPrebidAnalytics) {
|
|
2985
|
+
const bpa = win.bidkernelPrebidAnalytics;
|
|
2986
|
+
if (bpa.options) {
|
|
2987
|
+
const extracted = extractAnalyticsOptions(bpa.options);
|
|
2988
|
+
if (extracted) return extracted;
|
|
2989
|
+
}
|
|
2990
|
+
if (bpa.config) {
|
|
2991
|
+
const extracted = extractAnalyticsOptions(bpa.config);
|
|
2992
|
+
if (extracted) return extracted;
|
|
2993
|
+
}
|
|
2994
|
+
if (bpa.propertyId) {
|
|
2995
|
+
return extractAnalyticsOptions(bpa);
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
return null;
|
|
2999
|
+
}
|
|
3000
|
+
function registerPrebidAnalytics(pbjsGlobalName = "pbjs", defaults) {
|
|
3001
|
+
const win = typeof window !== "undefined" ? window : {};
|
|
3002
|
+
win[pbjsGlobalName] = win[pbjsGlobalName] || {};
|
|
3003
|
+
const pbjs = win[pbjsGlobalName];
|
|
3004
|
+
const enableInstance = (options) => {
|
|
3005
|
+
const previous = win._bidkernelPrebidAnalytics;
|
|
3006
|
+
const pinnedEndpoint = previous?.getEndpoint?.() || "";
|
|
3007
|
+
const merged = {
|
|
3008
|
+
pbjsGlobalName,
|
|
3009
|
+
...defaults,
|
|
3010
|
+
...options
|
|
3313
3011
|
};
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
record.refreshIndex = nextRefreshIndex;
|
|
3326
|
-
record.auctionId = resolvedAuctionId;
|
|
3327
|
-
record.transactionId = resolvedTransactionId;
|
|
3328
|
-
record.bidPayload = bidTrace;
|
|
3329
|
-
record.viewableFired = false;
|
|
3330
|
-
record.accumulatedTimeInViewMs = 0;
|
|
3331
|
-
}
|
|
3332
|
-
this.enqueue(TraceEventType.REFRESH, "refresh", {
|
|
3333
|
-
auctionId: resolvedAuctionId,
|
|
3334
|
-
transactionId: resolvedTransactionId,
|
|
3335
|
-
adUnitCode,
|
|
3336
|
-
bid: bidTrace,
|
|
3337
|
-
metadata: { refresh_index: String(nextRefreshIndex) }
|
|
3338
|
-
});
|
|
3339
|
-
} else if (adUnitCode) {
|
|
3340
|
-
if (resolvedAuctionId) {
|
|
3341
|
-
this.slotLastAuctionIds.set(adUnitCode, resolvedAuctionId);
|
|
3342
|
-
}
|
|
3343
|
-
if (!this.slotRefreshIndices.has(adUnitCode)) {
|
|
3344
|
-
this.slotRefreshIndices.set(adUnitCode, 0);
|
|
3012
|
+
if (pinnedEndpoint && !sameEndpointOrigin(merged.endpoint || "", pinnedEndpoint)) {
|
|
3013
|
+
defaultLogger.warn(
|
|
3014
|
+
`Ignoring analytics endpoint change to ${merged.endpoint || "(empty)"}: this page is pinned to ${pinnedEndpoint}. Reload to change the ingest endpoint.`
|
|
3015
|
+
);
|
|
3016
|
+
merged.endpoint = pinnedEndpoint;
|
|
3017
|
+
}
|
|
3018
|
+
if (previous) {
|
|
3019
|
+
try {
|
|
3020
|
+
previous.disable();
|
|
3021
|
+
} catch (e) {
|
|
3022
|
+
defaultLogger.warn("Failed to disable previous analytics instance:", e);
|
|
3345
3023
|
}
|
|
3346
3024
|
}
|
|
3347
|
-
const
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
)
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3025
|
+
const analytics = new BidkernelPrebidAnalytics(merged);
|
|
3026
|
+
analytics.enable();
|
|
3027
|
+
win._bidkernelPrebidAnalytics = analytics;
|
|
3028
|
+
return analytics;
|
|
3029
|
+
};
|
|
3030
|
+
const doRegister = () => {
|
|
3031
|
+
try {
|
|
3032
|
+
if (pbjs.adapterManager && typeof pbjs.adapterManager.registerAnalyticsAdapter === "function") {
|
|
3033
|
+
pbjs.adapterManager.registerAnalyticsAdapter({
|
|
3034
|
+
adapter: {
|
|
3035
|
+
enableAnalytics: (config) => {
|
|
3036
|
+
const options = extractAnalyticsOptions(config, !Array.isArray(config));
|
|
3037
|
+
if (options) {
|
|
3038
|
+
enableInstance(options);
|
|
3039
|
+
}
|
|
3040
|
+
},
|
|
3041
|
+
disableAnalytics: () => {
|
|
3042
|
+
if (win._bidkernelPrebidAnalytics) {
|
|
3043
|
+
win._bidkernelPrebidAnalytics.disable();
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
},
|
|
3047
|
+
code: "bidkernel"
|
|
3370
3048
|
});
|
|
3371
3049
|
}
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3050
|
+
const stashed = getStashedConfig(win);
|
|
3051
|
+
if (stashed && (stashed.propertyId || defaults?.propertyId)) {
|
|
3052
|
+
enableInstance(stashed);
|
|
3053
|
+
}
|
|
3054
|
+
} catch (e) {
|
|
3055
|
+
defaultLogger.warn("Failed to register standard Prebid analytics adapter:", e);
|
|
3377
3056
|
}
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3057
|
+
};
|
|
3058
|
+
if (pbjs.adapterManager && typeof pbjs.adapterManager.registerAnalyticsAdapter === "function") {
|
|
3059
|
+
doRegister();
|
|
3060
|
+
} else {
|
|
3061
|
+
pbjs.que = pbjs.que || [];
|
|
3062
|
+
pbjs.que.push(doRegister);
|
|
3063
|
+
}
|
|
3064
|
+
}
|
|
3065
|
+
|
|
3066
|
+
// src/video.ts
|
|
3067
|
+
var internals = (analytics) => analytics;
|
|
3068
|
+
var adsLoaderListenerMap = /* @__PURE__ */ new WeakMap();
|
|
3069
|
+
var adsLoadersWithErrorListener = /* @__PURE__ */ new WeakSet();
|
|
3070
|
+
function describeImaError(event) {
|
|
3071
|
+
const err = typeof event?.getError === "function" ? event.getError() : event?.error || event;
|
|
3072
|
+
const call = (name) => err && typeof err[name] === "function" ? err[name]() : void 0;
|
|
3073
|
+
const message = call("getMessage") || err?.message || String(err || "IMA ad error");
|
|
3074
|
+
const code = call("getErrorCode") ?? err?.code;
|
|
3075
|
+
const vastCode = call("getVastErrorCode");
|
|
3076
|
+
const type = call("getType") ?? err?.type;
|
|
3077
|
+
const metadata = { message };
|
|
3078
|
+
if (code) metadata.error_code = String(code);
|
|
3079
|
+
if (vastCode) metadata.vast_error_code = String(vastCode);
|
|
3080
|
+
if (type) metadata.error_type = String(type);
|
|
3081
|
+
return { err, message, metadata };
|
|
3082
|
+
}
|
|
3083
|
+
function recordImaAdError(analytics, event, ctx) {
|
|
3084
|
+
const self = internals(analytics);
|
|
3085
|
+
const { err, message, metadata } = describeImaError(event);
|
|
3086
|
+
self.log("WARN", "IMA AdErrorEvent received", event);
|
|
3087
|
+
self.enqueue(TraceEventType.AD_RENDER_FAILED, "adRenderFailed", {
|
|
3088
|
+
auctionId: ctx.auctionId,
|
|
3089
|
+
transactionId: ctx.transactionId,
|
|
3090
|
+
adUnitCode: ctx.adUnitCode,
|
|
3091
|
+
bid: ctx.bid,
|
|
3092
|
+
metadata: {
|
|
3093
|
+
...ctx.metadata,
|
|
3094
|
+
reason: ctx.adStarted ? "ima_playback_error" : "ima_ad_error",
|
|
3095
|
+
playback_phase: ctx.adStarted ? "post_start" : "pre_start",
|
|
3096
|
+
...metadata
|
|
3097
|
+
},
|
|
3098
|
+
error: typeof err === "object" ? err : new Error(message)
|
|
3099
|
+
});
|
|
3100
|
+
return err;
|
|
3101
|
+
}
|
|
3102
|
+
function ensureAdsLoaderErrorListener(loader, addEventListener) {
|
|
3103
|
+
if (!loader || typeof loader !== "object" || adsLoadersWithErrorListener.has(loader)) return;
|
|
3104
|
+
adsLoadersWithErrorListener.add(loader);
|
|
3105
|
+
const type = typeof window !== "undefined" && window.google?.ima?.AdErrorEvent?.Type?.AD_ERROR || "adError";
|
|
3106
|
+
try {
|
|
3107
|
+
addEventListener.call(loader, type, (event) => {
|
|
3108
|
+
for (const instance of BidkernelPrebidAnalytics.getActiveInstances()) {
|
|
3385
3109
|
try {
|
|
3386
|
-
const
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3110
|
+
const cached = findCachedWinningBid(instance, { mediaType: "video" });
|
|
3111
|
+
recordImaAdError(instance, event, {
|
|
3112
|
+
adStarted: false,
|
|
3113
|
+
adUnitCode: cached?.adUnitCode || "video",
|
|
3114
|
+
auctionId: cached?.auctionId,
|
|
3115
|
+
transactionId: cached?.transactionId,
|
|
3116
|
+
bid: cached?.bidTrace
|
|
3117
|
+
});
|
|
3393
3118
|
} catch {
|
|
3394
3119
|
}
|
|
3395
3120
|
}
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3121
|
+
});
|
|
3122
|
+
} catch {
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
function hookAdsManagerLoadedEvent(loadedEventCtor) {
|
|
3126
|
+
if (!loadedEventCtor) return;
|
|
3127
|
+
try {
|
|
3128
|
+
const loadedProto = loadedEventCtor.prototype;
|
|
3129
|
+
if (loadedProto && typeof loadedProto.getAdsManager === "function" && !loadedProto.getAdsManager.__bidkernelHooked) {
|
|
3130
|
+
const originalGetAdsManager = loadedProto.getAdsManager;
|
|
3131
|
+
const hookedGetAdsManager = function(...args) {
|
|
3132
|
+
const adsManager = originalGetAdsManager.apply(this, args);
|
|
3133
|
+
if (adsManager) {
|
|
3134
|
+
for (const instance of BidkernelPrebidAnalytics.getActiveInstances()) {
|
|
3135
|
+
try {
|
|
3136
|
+
instance.attachImaAdsManager(adsManager);
|
|
3137
|
+
} catch {
|
|
3402
3138
|
}
|
|
3403
3139
|
}
|
|
3404
|
-
} catch {
|
|
3405
3140
|
}
|
|
3406
|
-
|
|
3407
|
-
if (el) {
|
|
3408
|
-
this.observeSlot(el, adUnitCode || targetId, {
|
|
3409
|
-
adUnitCode,
|
|
3410
|
-
auctionId: resolvedAuctionId,
|
|
3411
|
-
transactionId: resolvedTransactionId,
|
|
3412
|
-
mediaType: resolvedMediaType,
|
|
3413
|
-
bid: bidTrace,
|
|
3414
|
-
refreshIndex: currentRefreshIndex,
|
|
3415
|
-
emitRefreshEvent: false
|
|
3416
|
-
});
|
|
3417
|
-
}
|
|
3418
|
-
}
|
|
3419
|
-
}
|
|
3420
|
-
isDuplicate(eventName, data) {
|
|
3421
|
-
return this.deduper.isDuplicate(eventName, data);
|
|
3422
|
-
}
|
|
3423
|
-
enqueue(type, eventName, data, level) {
|
|
3424
|
-
if (!this.isEnabled) return;
|
|
3425
|
-
if (!this.shouldSample(type, level)) return;
|
|
3426
|
-
extendSession();
|
|
3427
|
-
const protoEvent = {
|
|
3428
|
-
eventId: generateUUID(),
|
|
3429
|
-
timestampMs: Date.now(),
|
|
3430
|
-
type,
|
|
3431
|
-
eventName,
|
|
3432
|
-
auctionId: data.auctionId || "",
|
|
3433
|
-
transactionId: data.transactionId || "",
|
|
3434
|
-
adUnitCode: data.adUnitCode || data.elementId || "",
|
|
3435
|
-
namespace: data.ns || data.namespace || ""
|
|
3436
|
-
};
|
|
3437
|
-
if (data.bid) {
|
|
3438
|
-
protoEvent.bid = {
|
|
3439
|
-
bidder: data.bid.bidder || "",
|
|
3440
|
-
cpm: Number.isFinite(data.bid.cpm) ? data.bid.cpm : 0,
|
|
3441
|
-
// No defaults here: handlers with a priced bid (bidResponse, bidWon,
|
|
3442
|
-
// impression) set currency/mediaType themselves. Defaulting for the
|
|
3443
|
-
// rest would stamp fake "USD"/"banner" on noBid and bidRequest rows.
|
|
3444
|
-
currency: data.bid.currency || "",
|
|
3445
|
-
width: Number.isFinite(data.bid.width) ? data.bid.width : 0,
|
|
3446
|
-
height: Number.isFinite(data.bid.height) ? data.bid.height : 0,
|
|
3447
|
-
dealId: data.bid.dealId || "",
|
|
3448
|
-
mediaType: data.bid.mediaType || "",
|
|
3449
|
-
latencyMs: Number.isFinite(data.bid.latencyMs) ? data.bid.latencyMs : 0,
|
|
3450
|
-
advertiserDomain: data.bid.advertiserDomain || "",
|
|
3451
|
-
creativeId: data.bid.creativeId || ""
|
|
3141
|
+
return adsManager;
|
|
3452
3142
|
};
|
|
3143
|
+
hookedGetAdsManager.__bidkernelHooked = true;
|
|
3144
|
+
loadedProto.getAdsManager = hookedGetAdsManager;
|
|
3453
3145
|
}
|
|
3454
|
-
|
|
3455
|
-
protoEvent.viewableDurationMs = Number.isFinite(data.viewableDurationMs) ? data.viewableDurationMs : 0;
|
|
3456
|
-
}
|
|
3457
|
-
if (data.bidderOutcomes && Array.isArray(data.bidderOutcomes)) {
|
|
3458
|
-
protoEvent.bidderOutcomes = data.bidderOutcomes;
|
|
3459
|
-
}
|
|
3460
|
-
const metadata = { ...data.metadata };
|
|
3461
|
-
if (data.error !== void 0 && data.error !== null) {
|
|
3462
|
-
metadata.error = typeof data.error === "object" && data.error.message ? String(data.error.message) : String(data.error);
|
|
3463
|
-
}
|
|
3464
|
-
if (data.gpid) {
|
|
3465
|
-
metadata.gpid = String(data.gpid);
|
|
3466
|
-
}
|
|
3467
|
-
if (data.refreshIndex !== void 0 && metadata.refresh_index === void 0) {
|
|
3468
|
-
metadata.refresh_index = String(data.refreshIndex);
|
|
3469
|
-
}
|
|
3470
|
-
const boundedMetadata = boundMetadata(metadata);
|
|
3471
|
-
if (Object.keys(boundedMetadata).length > 0) {
|
|
3472
|
-
protoEvent.metadata = boundedMetadata;
|
|
3473
|
-
}
|
|
3474
|
-
this.queue.push(protoEvent);
|
|
3475
|
-
if (this.queue.length > MAX_QUEUE_SIZE) {
|
|
3476
|
-
this.queue.splice(0, this.queue.length - MAX_QUEUE_SIZE);
|
|
3477
|
-
}
|
|
3478
|
-
if (this.queue.length >= BATCH_SIZE) {
|
|
3479
|
-
this.flush();
|
|
3480
|
-
} else if (REVENUE_FLUSH_TYPES.has(type) && this.revenueFlushTimer === null) {
|
|
3481
|
-
this.revenueFlushTimer = setTimeout(() => {
|
|
3482
|
-
this.revenueFlushTimer = null;
|
|
3483
|
-
this.flush();
|
|
3484
|
-
}, this.config.revenueFlushDelayMs);
|
|
3485
|
-
}
|
|
3146
|
+
} catch {
|
|
3486
3147
|
}
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3148
|
+
}
|
|
3149
|
+
function hookAdsManagerPrototype(adsManagerCtorOrProto) {
|
|
3150
|
+
if (!adsManagerCtorOrProto) return;
|
|
3151
|
+
try {
|
|
3152
|
+
const proto = adsManagerCtorOrProto.prototype || adsManagerCtorOrProto;
|
|
3153
|
+
if (proto && typeof proto.init === "function" && !proto.init.__bidkernelHooked) {
|
|
3154
|
+
const origInit = proto.init;
|
|
3155
|
+
const hookedInit = function(...args) {
|
|
3156
|
+
for (const instance of BidkernelPrebidAnalytics.getActiveInstances()) {
|
|
3157
|
+
try {
|
|
3158
|
+
instance.attachImaAdsManager(this);
|
|
3159
|
+
} catch {
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
return origInit.apply(this, args);
|
|
3163
|
+
};
|
|
3164
|
+
hookedInit.__bidkernelHooked = true;
|
|
3165
|
+
proto.init = hookedInit;
|
|
3495
3166
|
}
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3167
|
+
if (proto && typeof proto.start === "function" && !proto.start.__bidkernelHooked) {
|
|
3168
|
+
const origStart = proto.start;
|
|
3169
|
+
const hookedStart = function(...args) {
|
|
3170
|
+
for (const instance of BidkernelPrebidAnalytics.getActiveInstances()) {
|
|
3171
|
+
try {
|
|
3172
|
+
instance.attachImaAdsManager(this);
|
|
3173
|
+
} catch {
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
return origStart.apply(this, args);
|
|
3177
|
+
};
|
|
3178
|
+
hookedStart.__bidkernelHooked = true;
|
|
3179
|
+
proto.start = hookedStart;
|
|
3503
3180
|
}
|
|
3181
|
+
} catch {
|
|
3504
3182
|
}
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
const
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3183
|
+
}
|
|
3184
|
+
function hookAdsLoader(adsLoaderCtor) {
|
|
3185
|
+
if (!adsLoaderCtor) return;
|
|
3186
|
+
try {
|
|
3187
|
+
const proto = adsLoaderCtor.prototype;
|
|
3188
|
+
if (!proto) return;
|
|
3189
|
+
if (typeof proto.addEventListener === "function" && !proto.addEventListener.__bidkernelHooked) {
|
|
3190
|
+
const origAddEventListener = proto.addEventListener;
|
|
3191
|
+
const hookedAddEventListener = function(type, listener, ...rest) {
|
|
3192
|
+
ensureAdsLoaderErrorListener(this, origAddEventListener);
|
|
3193
|
+
if ((type === "adsManagerLoaded" || type === (this?.AdsManagerLoadedEvent?.Type?.ADS_MANAGER_LOADED || "adsManagerLoaded")) && typeof listener === "function") {
|
|
3194
|
+
let wrappedListener = adsLoaderListenerMap.get(listener);
|
|
3195
|
+
if (!wrappedListener) {
|
|
3196
|
+
wrappedListener = function(event) {
|
|
3197
|
+
if (event && typeof event.getAdsManager === "function" && !event.getAdsManager.__bidkernelHooked) {
|
|
3198
|
+
const origGetAdsManager = event.getAdsManager;
|
|
3199
|
+
event.getAdsManager = function(...args) {
|
|
3200
|
+
const adsManager = origGetAdsManager.apply(this, args);
|
|
3201
|
+
if (adsManager) {
|
|
3202
|
+
for (const sdk of BidkernelPrebidAnalytics.getActiveInstances()) {
|
|
3203
|
+
try {
|
|
3204
|
+
sdk.attachImaAdsManager(adsManager);
|
|
3205
|
+
} catch {
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
return adsManager;
|
|
3210
|
+
};
|
|
3211
|
+
event.getAdsManager.__bidkernelHooked = true;
|
|
3212
|
+
}
|
|
3213
|
+
return listener.apply(this, arguments);
|
|
3214
|
+
};
|
|
3215
|
+
adsLoaderListenerMap.set(listener, wrappedListener);
|
|
3216
|
+
}
|
|
3217
|
+
return origAddEventListener.call(this, type, wrappedListener, ...rest);
|
|
3218
|
+
}
|
|
3219
|
+
return origAddEventListener.call(this, type, listener, ...rest);
|
|
3220
|
+
};
|
|
3221
|
+
hookedAddEventListener.__bidkernelHooked = true;
|
|
3222
|
+
proto.addEventListener = hookedAddEventListener;
|
|
3223
|
+
}
|
|
3224
|
+
if (typeof proto.removeEventListener === "function" && !proto.removeEventListener.__bidkernelHooked) {
|
|
3225
|
+
const origRemoveEventListener = proto.removeEventListener;
|
|
3226
|
+
const hookedRemoveEventListener = function(type, listener, ...rest) {
|
|
3227
|
+
const wrapped = typeof listener === "function" ? adsLoaderListenerMap.get(listener) : void 0;
|
|
3228
|
+
return origRemoveEventListener.call(this, type, wrapped || listener, ...rest);
|
|
3549
3229
|
};
|
|
3230
|
+
hookedRemoveEventListener.__bidkernelHooked = true;
|
|
3231
|
+
proto.removeEventListener = hookedRemoveEventListener;
|
|
3550
3232
|
}
|
|
3233
|
+
} catch {
|
|
3551
3234
|
}
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
};
|
|
3559
|
-
if (useKeepalive) {
|
|
3560
|
-
fetchOpts.keepalive = true;
|
|
3235
|
+
}
|
|
3236
|
+
function hookImaPrototype(ima) {
|
|
3237
|
+
if (!ima || typeof ima !== "object") return;
|
|
3238
|
+
try {
|
|
3239
|
+
if (ima.AdsManagerLoadedEvent) {
|
|
3240
|
+
hookAdsManagerLoadedEvent(ima.AdsManagerLoadedEvent);
|
|
3561
3241
|
}
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
if (res.status >= 400 && res.status < 500 && res.status !== 429 && res.status !== 408) {
|
|
3566
|
-
this.log("WARN", `Dropping batch due to non-retryable client error HTTP ${res.status}`);
|
|
3567
|
-
return;
|
|
3568
|
-
}
|
|
3569
|
-
if (!isReplay) this.handleSendFailure(payload.events);
|
|
3570
|
-
} else {
|
|
3571
|
-
if (!isReplay) {
|
|
3572
|
-
this.consecutiveSendFailures = 0;
|
|
3573
|
-
this.nextSendAllowedAt = 0;
|
|
3574
|
-
}
|
|
3575
|
-
if (onDelivered) onDelivered();
|
|
3576
|
-
}
|
|
3577
|
-
}).catch((err) => {
|
|
3578
|
-
this.log("ERROR", "Failed to send batch", err);
|
|
3579
|
-
if (!isReplay) this.handleSendFailure(payload.events);
|
|
3580
|
-
});
|
|
3581
|
-
const proc = typeof globalThis !== "undefined" ? globalThis.process : void 0;
|
|
3582
|
-
if (proc && typeof proc._tickCallback === "function") {
|
|
3242
|
+
const loadedDesc = Object.getOwnPropertyDescriptor(ima, "AdsManagerLoadedEvent");
|
|
3243
|
+
if (!loadedDesc || loadedDesc.configurable) {
|
|
3244
|
+
let _loadedEvent = ima.AdsManagerLoadedEvent;
|
|
3583
3245
|
try {
|
|
3584
|
-
|
|
3246
|
+
Object.defineProperty(ima, "AdsManagerLoadedEvent", {
|
|
3247
|
+
configurable: true,
|
|
3248
|
+
enumerable: true,
|
|
3249
|
+
get() {
|
|
3250
|
+
return _loadedEvent;
|
|
3251
|
+
},
|
|
3252
|
+
set(val) {
|
|
3253
|
+
_loadedEvent = val;
|
|
3254
|
+
hookAdsManagerLoadedEvent(val);
|
|
3255
|
+
}
|
|
3256
|
+
});
|
|
3585
3257
|
} catch {
|
|
3586
3258
|
}
|
|
3587
3259
|
}
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
this.consecutiveSendFailures++;
|
|
3591
|
-
const backoff = Math.min(
|
|
3592
|
-
FLUSH_INTERVAL_MS * 2 ** (this.consecutiveSendFailures - 1),
|
|
3593
|
-
MAX_SEND_BACKOFF_MS
|
|
3594
|
-
);
|
|
3595
|
-
this.nextSendAllowedAt = Date.now() + backoff;
|
|
3596
|
-
if (this.consecutiveSendFailures <= MAX_CONSECUTIVE_SEND_FAILURES) {
|
|
3597
|
-
this.requeue(events);
|
|
3598
|
-
} else {
|
|
3599
|
-
this.log(
|
|
3600
|
-
"WARN",
|
|
3601
|
-
`Dropping ${events.length} events after ${this.consecutiveSendFailures} consecutive send failures`
|
|
3602
|
-
);
|
|
3603
|
-
}
|
|
3604
|
-
}
|
|
3605
|
-
flush() {
|
|
3606
|
-
if (Date.now() < this.nextSendAllowedAt) return;
|
|
3607
|
-
let payload = this.drainBatch();
|
|
3608
|
-
while (payload) {
|
|
3609
|
-
this.sendFetch(payload, false);
|
|
3610
|
-
if (Date.now() < this.nextSendAllowedAt) break;
|
|
3611
|
-
payload = this.drainBatch();
|
|
3260
|
+
if (ima.AdsLoader) {
|
|
3261
|
+
hookAdsLoader(ima.AdsLoader);
|
|
3612
3262
|
}
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3263
|
+
const loaderDesc = Object.getOwnPropertyDescriptor(ima, "AdsLoader");
|
|
3264
|
+
if (!loaderDesc || loaderDesc.configurable) {
|
|
3265
|
+
let _loader = ima.AdsLoader;
|
|
3266
|
+
try {
|
|
3267
|
+
Object.defineProperty(ima, "AdsLoader", {
|
|
3268
|
+
configurable: true,
|
|
3269
|
+
enumerable: true,
|
|
3270
|
+
get() {
|
|
3271
|
+
return _loader;
|
|
3272
|
+
},
|
|
3273
|
+
set(val) {
|
|
3274
|
+
_loader = val;
|
|
3275
|
+
hookAdsLoader(val);
|
|
3276
|
+
}
|
|
3277
|
+
});
|
|
3278
|
+
} catch {
|
|
3625
3279
|
}
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3280
|
+
}
|
|
3281
|
+
if (ima.AdsManager) {
|
|
3282
|
+
hookAdsManagerPrototype(ima.AdsManager);
|
|
3283
|
+
}
|
|
3284
|
+
const mgrDesc = Object.getOwnPropertyDescriptor(ima, "AdsManager");
|
|
3285
|
+
if (!mgrDesc || mgrDesc.configurable) {
|
|
3286
|
+
let _mgr = ima.AdsManager;
|
|
3287
|
+
try {
|
|
3288
|
+
Object.defineProperty(ima, "AdsManager", {
|
|
3289
|
+
configurable: true,
|
|
3290
|
+
enumerable: true,
|
|
3291
|
+
get() {
|
|
3292
|
+
return _mgr;
|
|
3293
|
+
},
|
|
3294
|
+
set(val) {
|
|
3295
|
+
_mgr = val;
|
|
3296
|
+
hookAdsManagerPrototype(val);
|
|
3297
|
+
}
|
|
3298
|
+
});
|
|
3299
|
+
} catch {
|
|
3629
3300
|
}
|
|
3630
|
-
payload = this.drainBatch();
|
|
3631
3301
|
}
|
|
3632
|
-
|
|
3302
|
+
} catch {
|
|
3633
3303
|
}
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3304
|
+
}
|
|
3305
|
+
function initImaInterception() {
|
|
3306
|
+
if (typeof window === "undefined") return;
|
|
3307
|
+
const win = window;
|
|
3308
|
+
if (win.google?.ima) {
|
|
3309
|
+
hookImaPrototype(win.google.ima);
|
|
3640
3310
|
}
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3311
|
+
if (win.google && typeof win.google === "object") {
|
|
3312
|
+
const imaDesc = Object.getOwnPropertyDescriptor(win.google, "ima");
|
|
3313
|
+
if (!imaDesc || imaDesc.configurable) {
|
|
3314
|
+
let _ima = win.google.ima;
|
|
3315
|
+
try {
|
|
3316
|
+
Object.defineProperty(win.google, "ima", {
|
|
3317
|
+
configurable: true,
|
|
3318
|
+
enumerable: true,
|
|
3319
|
+
get() {
|
|
3320
|
+
return _ima;
|
|
3321
|
+
},
|
|
3322
|
+
set(val) {
|
|
3323
|
+
_ima = val;
|
|
3324
|
+
hookImaPrototype(val);
|
|
3325
|
+
}
|
|
3326
|
+
});
|
|
3327
|
+
} catch {
|
|
3650
3328
|
}
|
|
3651
|
-
if (pendingCount >= MAX_PENDING_BATCHES) return null;
|
|
3652
|
-
const key = `${this.pendingKeyPrefix()}${Date.now()}_${Math.floor(Math.random() * 1e6)}`;
|
|
3653
|
-
localStorage.setItem(key, bytesToBase64(encoded));
|
|
3654
|
-
this.persistedBatchKeys.push(key);
|
|
3655
|
-
return key;
|
|
3656
|
-
} catch {
|
|
3657
|
-
return null;
|
|
3658
3329
|
}
|
|
3659
3330
|
}
|
|
3660
|
-
|
|
3661
|
-
|
|
3331
|
+
const googleDesc = Object.getOwnPropertyDescriptor(win, "google");
|
|
3332
|
+
if (!googleDesc || googleDesc.configurable) {
|
|
3333
|
+
let _google = win.google;
|
|
3662
3334
|
try {
|
|
3663
|
-
|
|
3335
|
+
Object.defineProperty(win, "google", {
|
|
3336
|
+
configurable: true,
|
|
3337
|
+
enumerable: true,
|
|
3338
|
+
get() {
|
|
3339
|
+
return _google;
|
|
3340
|
+
},
|
|
3341
|
+
set(val) {
|
|
3342
|
+
_google = val;
|
|
3343
|
+
if (_google && typeof _google === "object") {
|
|
3344
|
+
if (_google.ima) {
|
|
3345
|
+
hookImaPrototype(_google.ima);
|
|
3346
|
+
}
|
|
3347
|
+
const iDesc = Object.getOwnPropertyDescriptor(_google, "ima");
|
|
3348
|
+
if (!iDesc || iDesc.configurable) {
|
|
3349
|
+
let _i = _google.ima;
|
|
3350
|
+
try {
|
|
3351
|
+
Object.defineProperty(_google, "ima", {
|
|
3352
|
+
configurable: true,
|
|
3353
|
+
enumerable: true,
|
|
3354
|
+
get() {
|
|
3355
|
+
return _i;
|
|
3356
|
+
},
|
|
3357
|
+
set(iv) {
|
|
3358
|
+
_i = iv;
|
|
3359
|
+
hookImaPrototype(iv);
|
|
3360
|
+
}
|
|
3361
|
+
});
|
|
3362
|
+
} catch {
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
}
|
|
3367
|
+
});
|
|
3664
3368
|
} catch {
|
|
3665
3369
|
}
|
|
3666
|
-
const idx = this.persistedBatchKeys.indexOf(key);
|
|
3667
|
-
if (idx !== -1) this.persistedBatchKeys.splice(idx, 1);
|
|
3668
3370
|
}
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3371
|
+
}
|
|
3372
|
+
if (typeof window !== "undefined") {
|
|
3373
|
+
initImaInterception();
|
|
3374
|
+
}
|
|
3375
|
+
function findCachedWinningBid(analytics, filter) {
|
|
3376
|
+
const self = internals(analytics);
|
|
3377
|
+
const now = Date.now();
|
|
3378
|
+
const isConsumed = (entry) => {
|
|
3379
|
+
return self.hasImpressionEmitted(
|
|
3380
|
+
entry.adUnitCode,
|
|
3381
|
+
void 0,
|
|
3382
|
+
entry.auctionId,
|
|
3383
|
+
entry.bidTrace.creativeId
|
|
3384
|
+
);
|
|
3385
|
+
};
|
|
3386
|
+
if (filter?.creativeId || filter?.adId) {
|
|
3387
|
+
for (const entry of Array.from(self.cachedWinningBids.values())) {
|
|
3388
|
+
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
3389
|
+
if (filter.creativeId && (entry.bidTrace.creativeId === filter.creativeId || entry.rawBid?.creativeId === filter.creativeId) || filter.adId && (entry.rawBid?.adId === filter.adId || entry.adUnitCode === filter.adId)) {
|
|
3390
|
+
return entry;
|
|
3679
3391
|
}
|
|
3680
|
-
}
|
|
3392
|
+
}
|
|
3681
3393
|
}
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
if (!this.config.propertyId) return;
|
|
3693
|
-
const ownPrefix = this.pendingKeyPrefix();
|
|
3694
|
-
const keys = [];
|
|
3695
|
-
const legacyKeys = [];
|
|
3696
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
3697
|
-
const k = localStorage.key(i);
|
|
3698
|
-
if (!k || !k.startsWith(PENDING_BATCH_KEY_PREFIX)) continue;
|
|
3699
|
-
if (k.startsWith(ownPrefix)) {
|
|
3700
|
-
keys.push(k);
|
|
3701
|
-
} else if (isLegacyPendingKey(k)) {
|
|
3702
|
-
legacyKeys.push(k);
|
|
3394
|
+
let bestUnconsumedMatch;
|
|
3395
|
+
if (filter?.mediaType) {
|
|
3396
|
+
for (const entry of Array.from(self.cachedWinningBids.values())) {
|
|
3397
|
+
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
3398
|
+
const isEntryVideo = entry.bidTrace.mediaType === filter.mediaType || entry.rawBid?.mediaType === filter.mediaType || filter.mediaType === "video" && entry.rawBid?.mediaTypes?.video !== void 0;
|
|
3399
|
+
if (isEntryVideo) {
|
|
3400
|
+
if (!isConsumed(entry)) {
|
|
3401
|
+
if (!bestUnconsumedMatch || entry.timestamp < bestUnconsumedMatch.timestamp) {
|
|
3402
|
+
bestUnconsumedMatch = entry;
|
|
3403
|
+
}
|
|
3703
3404
|
}
|
|
3704
3405
|
}
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3406
|
+
}
|
|
3407
|
+
if (bestUnconsumedMatch) return bestUnconsumedMatch;
|
|
3408
|
+
let bestFallbackMatch;
|
|
3409
|
+
for (const entry of Array.from(self.cachedWinningBids.values())) {
|
|
3410
|
+
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
3411
|
+
const isEntryVideo = entry.bidTrace.mediaType === filter.mediaType || entry.rawBid?.mediaType === filter.mediaType || filter.mediaType === "video" && entry.rawBid?.mediaTypes?.video !== void 0;
|
|
3412
|
+
if (isEntryVideo) {
|
|
3413
|
+
if (!bestFallbackMatch || entry.timestamp > bestFallbackMatch.timestamp) {
|
|
3414
|
+
bestFallbackMatch = entry;
|
|
3709
3415
|
}
|
|
3710
3416
|
}
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
if (!value) continue;
|
|
3720
|
-
const ts = parseInt(key.slice(ownPrefix.length), 10);
|
|
3721
|
-
if (!Number.isFinite(ts) || Date.now() - ts > PENDING_BATCH_MAX_AGE_MS) continue;
|
|
3722
|
-
let encoded;
|
|
3723
|
-
try {
|
|
3724
|
-
encoded = base64ToBytes(value);
|
|
3725
|
-
} catch {
|
|
3726
|
-
continue;
|
|
3727
|
-
}
|
|
3728
|
-
if (encoded.byteLength === 0) continue;
|
|
3729
|
-
this.log("DEBUG", `Resending persisted exit batch (${encoded.byteLength} bytes)`);
|
|
3730
|
-
this.sendFetch(
|
|
3731
|
-
{
|
|
3732
|
-
url: `${this.config.endpoint}/${this.config.propertyId}`,
|
|
3733
|
-
encoded,
|
|
3734
|
-
events: []
|
|
3735
|
-
},
|
|
3736
|
-
false,
|
|
3737
|
-
void 0,
|
|
3738
|
-
true
|
|
3739
|
-
);
|
|
3417
|
+
}
|
|
3418
|
+
if (bestFallbackMatch) return bestFallbackMatch;
|
|
3419
|
+
}
|
|
3420
|
+
for (const entry of Array.from(self.cachedWinningBids.values())) {
|
|
3421
|
+
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
3422
|
+
if (!isConsumed(entry)) {
|
|
3423
|
+
if (!bestUnconsumedMatch || entry.timestamp > bestUnconsumedMatch.timestamp) {
|
|
3424
|
+
bestUnconsumedMatch = entry;
|
|
3740
3425
|
}
|
|
3741
|
-
} catch {
|
|
3742
3426
|
}
|
|
3743
3427
|
}
|
|
3744
|
-
|
|
3745
|
-
|
|
3428
|
+
if (bestUnconsumedMatch) return bestUnconsumedMatch;
|
|
3429
|
+
let bestRecent;
|
|
3430
|
+
for (const entry of Array.from(self.cachedWinningBids.values())) {
|
|
3431
|
+
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
3432
|
+
if (!bestRecent || entry.timestamp > bestRecent.timestamp) {
|
|
3433
|
+
bestRecent = entry;
|
|
3434
|
+
}
|
|
3746
3435
|
}
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
const matched = config.find((item) => item?.provider === "bidkernel") ?? (allowProviderless ? config.find(
|
|
3756
|
-
(item) => item && typeof item === "object" && item.provider === void 0
|
|
3757
|
-
) : void 0);
|
|
3758
|
-
return matched ? extractAnalyticsOptions(matched, allowProviderless) : null;
|
|
3436
|
+
return bestRecent;
|
|
3437
|
+
}
|
|
3438
|
+
function attachImaAdsManager(analytics, adsManager, options) {
|
|
3439
|
+
const self = internals(analytics);
|
|
3440
|
+
if (!adsManager || typeof adsManager.addEventListener !== "function") {
|
|
3441
|
+
self.log("WARN", "Invalid AdsManager passed to attachImaAdsManager");
|
|
3442
|
+
return () => {
|
|
3443
|
+
};
|
|
3759
3444
|
}
|
|
3760
|
-
if (
|
|
3761
|
-
|
|
3762
|
-
|
|
3445
|
+
if (!adsManager.__bidkernelAttachedInstances) {
|
|
3446
|
+
try {
|
|
3447
|
+
Object.defineProperty(adsManager, "__bidkernelAttachedInstances", {
|
|
3448
|
+
value: /* @__PURE__ */ new Map(),
|
|
3449
|
+
configurable: true,
|
|
3450
|
+
writable: true
|
|
3451
|
+
});
|
|
3452
|
+
} catch {
|
|
3453
|
+
adsManager.__bidkernelAttachedInstances = /* @__PURE__ */ new Map();
|
|
3763
3454
|
}
|
|
3764
|
-
|
|
3765
|
-
|
|
3455
|
+
}
|
|
3456
|
+
const attached = adsManager.__bidkernelAttachedInstances;
|
|
3457
|
+
const previous = attached.get(analytics);
|
|
3458
|
+
if (previous) {
|
|
3459
|
+
if (!options) return () => {
|
|
3460
|
+
};
|
|
3461
|
+
previous();
|
|
3462
|
+
}
|
|
3463
|
+
const slotId = options?.slotId || options?.adUnitCode || "video";
|
|
3464
|
+
const adUnitCode = options?.adUnitCode || slotId;
|
|
3465
|
+
const auctionId = options?.auctionId || "";
|
|
3466
|
+
const transactionId = options?.transactionId || "";
|
|
3467
|
+
const getWinningBid = (adData) => {
|
|
3468
|
+
let cached = (auctionId ? self.getCachedBid(`${auctionId}:${adUnitCode}`) : void 0) || (auctionId && slotId !== adUnitCode ? self.getCachedBid(`${auctionId}:${slotId}`) : void 0) || (adUnitCode !== "video" ? self.getCachedBid(adUnitCode) : void 0) || (slotId !== adUnitCode && slotId !== "video" ? self.getCachedBid(slotId) : void 0);
|
|
3469
|
+
if (!cached) {
|
|
3470
|
+
cached = findCachedWinningBid(analytics, {
|
|
3471
|
+
creativeId: adData?.creativeId,
|
|
3472
|
+
adId: adData?.adId,
|
|
3473
|
+
mediaType: "video"
|
|
3474
|
+
});
|
|
3766
3475
|
}
|
|
3767
|
-
|
|
3768
|
-
|
|
3476
|
+
return cached;
|
|
3477
|
+
};
|
|
3478
|
+
let adStarted = false;
|
|
3479
|
+
const onAdStartedOrImpression = (event) => {
|
|
3480
|
+
adStarted = true;
|
|
3481
|
+
self.log("DEBUG", "IMA AdEvent.STARTED / IMPRESSION received", event);
|
|
3482
|
+
const ad = typeof event?.getAd === "function" ? event.getAd() : event?.ad;
|
|
3483
|
+
const adData = {};
|
|
3484
|
+
if (ad) {
|
|
3485
|
+
adData.creativeId = (typeof ad.getCreativeId === "function" ? ad.getCreativeId() : ad.creativeId) || "";
|
|
3486
|
+
adData.adId = (typeof ad.getAdId === "function" ? ad.getAdId() : ad.id) || "";
|
|
3487
|
+
adData.title = (typeof ad.getTitle === "function" ? ad.getTitle() : ad.title) || "";
|
|
3488
|
+
adData.duration = typeof ad.getDuration === "function" ? ad.getDuration() : ad.duration;
|
|
3489
|
+
adData.advertiserName = typeof ad.getAdvertiserName === "function" ? ad.getAdvertiserName() : "";
|
|
3490
|
+
}
|
|
3491
|
+
const cached = getWinningBid(adData);
|
|
3492
|
+
const resolvedSlotId = options?.slotId && options.slotId !== "video" ? options.slotId : options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || options?.slotId || options?.adUnitCode || "video";
|
|
3493
|
+
const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || resolvedSlotId;
|
|
3494
|
+
const resolvedAuctionId = auctionId || cached?.auctionId || "";
|
|
3495
|
+
const resolvedTransactionId = transactionId || cached?.transactionId || "";
|
|
3496
|
+
const mergedBid = {
|
|
3497
|
+
...cached?.rawBid || options?.bid,
|
|
3498
|
+
mediaType: "video",
|
|
3499
|
+
creativeId: adData.creativeId || cached?.bidTrace?.creativeId || options?.bid?.creativeId || ""
|
|
3500
|
+
};
|
|
3501
|
+
const emitted = self.recordImpression(resolvedSlotId, {
|
|
3502
|
+
adUnitCode: resolvedAdUnitCode,
|
|
3503
|
+
auctionId: resolvedAuctionId,
|
|
3504
|
+
transactionId: resolvedTransactionId,
|
|
3505
|
+
mediaType: "video",
|
|
3506
|
+
bid: mergedBid,
|
|
3507
|
+
metadata: {
|
|
3508
|
+
...options?.metadata,
|
|
3509
|
+
media_type: "video",
|
|
3510
|
+
...adData.title ? { ad_title: adData.title } : {},
|
|
3511
|
+
...Number.isFinite(adData.duration) ? { ad_duration_s: String(adData.duration) } : {}
|
|
3512
|
+
}
|
|
3513
|
+
});
|
|
3514
|
+
if (emitted && options?.onImpression) {
|
|
3515
|
+
try {
|
|
3516
|
+
options.onImpression(resolvedSlotId, { ad, bid: mergedBid });
|
|
3517
|
+
} catch (e) {
|
|
3518
|
+
self.log("ERROR", "Error in onImpression callback", e);
|
|
3519
|
+
}
|
|
3769
3520
|
}
|
|
3770
|
-
}
|
|
3771
|
-
|
|
3772
|
-
}
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
if (win.bidkernelPrebidAnalytics) {
|
|
3780
|
-
const bpa = win.bidkernelPrebidAnalytics;
|
|
3781
|
-
if (bpa.options) {
|
|
3782
|
-
const extracted = extractAnalyticsOptions(bpa.options);
|
|
3783
|
-
if (extracted) return extracted;
|
|
3521
|
+
};
|
|
3522
|
+
const onMilestone = (milestone) => {
|
|
3523
|
+
self.log("DEBUG", `IMA AdEvent milestone: ${milestone}`);
|
|
3524
|
+
if (options?.onMilestone) {
|
|
3525
|
+
try {
|
|
3526
|
+
options.onMilestone(milestone, slotId);
|
|
3527
|
+
} catch (e) {
|
|
3528
|
+
self.log("ERROR", "Error in onMilestone callback", e);
|
|
3529
|
+
}
|
|
3784
3530
|
}
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3531
|
+
};
|
|
3532
|
+
const onAdClick = (event) => {
|
|
3533
|
+
self.log("DEBUG", "IMA AdEvent.CLICK received", event);
|
|
3534
|
+
const cached = getWinningBid();
|
|
3535
|
+
const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || slotId;
|
|
3536
|
+
self.enqueue(TraceEventType.CLICK, "click", {
|
|
3537
|
+
auctionId: auctionId || cached?.auctionId,
|
|
3538
|
+
transactionId: transactionId || cached?.transactionId,
|
|
3539
|
+
adUnitCode: resolvedAdUnitCode,
|
|
3540
|
+
bid: cached?.bidTrace || options?.bid,
|
|
3541
|
+
metadata: {
|
|
3542
|
+
...options?.metadata,
|
|
3543
|
+
media_type: "video"
|
|
3544
|
+
}
|
|
3545
|
+
});
|
|
3546
|
+
};
|
|
3547
|
+
const onAdError = (event) => {
|
|
3548
|
+
const cached = getWinningBid();
|
|
3549
|
+
const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || slotId;
|
|
3550
|
+
const err = recordImaAdError(analytics, event, {
|
|
3551
|
+
adStarted,
|
|
3552
|
+
adUnitCode: resolvedAdUnitCode,
|
|
3553
|
+
auctionId: auctionId || cached?.auctionId,
|
|
3554
|
+
transactionId: transactionId || cached?.transactionId,
|
|
3555
|
+
bid: cached?.bidTrace || options?.bid,
|
|
3556
|
+
metadata: options?.metadata
|
|
3557
|
+
});
|
|
3558
|
+
if (options?.onError) {
|
|
3559
|
+
try {
|
|
3560
|
+
options.onError(err);
|
|
3561
|
+
} catch (e) {
|
|
3562
|
+
self.log("ERROR", "Error in onError callback", e);
|
|
3563
|
+
}
|
|
3788
3564
|
}
|
|
3789
|
-
|
|
3790
|
-
|
|
3565
|
+
};
|
|
3566
|
+
const googleIma = typeof window !== "undefined" ? window.google?.ima : void 0;
|
|
3567
|
+
const adEventType = googleIma?.AdEvent?.Type || {};
|
|
3568
|
+
const adErrorEventType = googleIma?.AdErrorEvent?.Type || {};
|
|
3569
|
+
const rawListeners = [
|
|
3570
|
+
{ type: adEventType.STARTED || "started", handler: onAdStartedOrImpression },
|
|
3571
|
+
{ type: adEventType.IMPRESSION || "impression", handler: onAdStartedOrImpression },
|
|
3572
|
+
{
|
|
3573
|
+
type: adEventType.FIRST_QUARTILE || "firstQuartile",
|
|
3574
|
+
handler: () => onMilestone("firstQuartile")
|
|
3575
|
+
},
|
|
3576
|
+
{ type: adEventType.MIDPOINT || "midpoint", handler: () => onMilestone("midpoint") },
|
|
3577
|
+
{
|
|
3578
|
+
type: adEventType.THIRD_QUARTILE || "thirdQuartile",
|
|
3579
|
+
handler: () => onMilestone("thirdQuartile")
|
|
3580
|
+
},
|
|
3581
|
+
{ type: adEventType.COMPLETE || "complete", handler: () => onMilestone("complete") },
|
|
3582
|
+
{ type: adEventType.CLICK || "click", handler: onAdClick },
|
|
3583
|
+
{ type: adErrorEventType.AD_ERROR || "adError", handler: onAdError }
|
|
3584
|
+
];
|
|
3585
|
+
const seenListenerTypes = /* @__PURE__ */ new Set();
|
|
3586
|
+
const listeners = [];
|
|
3587
|
+
for (const l of rawListeners) {
|
|
3588
|
+
if (!seenListenerTypes.has(l.type)) {
|
|
3589
|
+
seenListenerTypes.add(l.type);
|
|
3590
|
+
listeners.push(l);
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
for (const { type, handler } of listeners) {
|
|
3594
|
+
try {
|
|
3595
|
+
adsManager.addEventListener(type, handler);
|
|
3596
|
+
} catch {
|
|
3791
3597
|
}
|
|
3792
3598
|
}
|
|
3793
|
-
|
|
3599
|
+
const cleanup = () => {
|
|
3600
|
+
attached.delete(analytics);
|
|
3601
|
+
for (const { type, handler } of listeners) {
|
|
3602
|
+
try {
|
|
3603
|
+
adsManager.removeEventListener(type, handler);
|
|
3604
|
+
} catch {
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
self.videoDetachCleanups.delete(cleanup);
|
|
3608
|
+
};
|
|
3609
|
+
attached.set(analytics, cleanup);
|
|
3610
|
+
self.videoDetachCleanups.add(cleanup);
|
|
3611
|
+
return cleanup;
|
|
3794
3612
|
}
|
|
3795
|
-
function
|
|
3796
|
-
const
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
const previous = win._bidkernelPrebidAnalytics;
|
|
3801
|
-
const pinnedEndpoint = previous?.getEndpoint?.() || "";
|
|
3802
|
-
const merged = {
|
|
3803
|
-
pbjsGlobalName,
|
|
3804
|
-
...defaults,
|
|
3805
|
-
...options
|
|
3613
|
+
function attachVideoPlayer(analytics, target, options) {
|
|
3614
|
+
const self = internals(analytics);
|
|
3615
|
+
if (!target) {
|
|
3616
|
+
self.log("WARN", "attachVideoPlayer called with null/undefined target");
|
|
3617
|
+
return () => {
|
|
3806
3618
|
};
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3619
|
+
}
|
|
3620
|
+
let el = null;
|
|
3621
|
+
if (typeof target === "string") {
|
|
3622
|
+
if (typeof document !== "undefined") {
|
|
3623
|
+
try {
|
|
3624
|
+
el = document.querySelector(target) || document.getElementById(target);
|
|
3625
|
+
} catch {
|
|
3626
|
+
el = document.getElementById(target);
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
} else if (typeof HTMLElement !== "undefined" && target instanceof HTMLElement) {
|
|
3630
|
+
el = target;
|
|
3631
|
+
} else if (target && typeof target === "object" && target.nodeType === 1) {
|
|
3632
|
+
el = target;
|
|
3633
|
+
} else if (
|
|
3634
|
+
// Check if target is an IMA AdsManager
|
|
3635
|
+
typeof target.addEventListener === "function" && (typeof target.init === "function" || typeof target.start === "function" || typeof target.getCuePoints === "function" || typeof target.getVolume === "function" || target.__imaAdsManager)
|
|
3636
|
+
) {
|
|
3637
|
+
return attachImaAdsManager(analytics, target, options);
|
|
3638
|
+
} else if (target && typeof target.addEventListener === "function") {
|
|
3639
|
+
el = target;
|
|
3640
|
+
}
|
|
3641
|
+
const slotId = options?.slotId || options?.adUnitCode || (el ? el.id : "") || "video";
|
|
3642
|
+
const adUnitCode = options?.adUnitCode || slotId;
|
|
3643
|
+
const auctionId = options?.auctionId || "";
|
|
3644
|
+
const transactionId = options?.transactionId || "";
|
|
3645
|
+
const getWinningBid = () => {
|
|
3646
|
+
return (auctionId ? self.getCachedBid(`${auctionId}:${adUnitCode}`) : void 0) || (auctionId && slotId !== adUnitCode ? self.getCachedBid(`${auctionId}:${slotId}`) : void 0) || self.getCachedBid(adUnitCode) || (slotId !== adUnitCode ? self.getCachedBid(slotId) : void 0);
|
|
3647
|
+
};
|
|
3648
|
+
let videoEl = null;
|
|
3649
|
+
if (el) {
|
|
3650
|
+
if (el.tagName && el.tagName.toLowerCase() === "video") {
|
|
3651
|
+
videoEl = el;
|
|
3652
|
+
} else {
|
|
3653
|
+
videoEl = el.querySelector("video");
|
|
3812
3654
|
}
|
|
3813
|
-
|
|
3655
|
+
}
|
|
3656
|
+
const quartilesFired = {
|
|
3657
|
+
q1: false,
|
|
3658
|
+
q2: false,
|
|
3659
|
+
q3: false,
|
|
3660
|
+
q4: false
|
|
3661
|
+
};
|
|
3662
|
+
const triggerPlaybackImpression = (activeVideo) => {
|
|
3663
|
+
const vEl = activeVideo || videoEl || (el?.querySelector ? el.querySelector("video") : null);
|
|
3664
|
+
const cached = getWinningBid();
|
|
3665
|
+
const mergedBid = {
|
|
3666
|
+
...cached?.rawBid || options?.bid,
|
|
3667
|
+
mediaType: "video"
|
|
3668
|
+
};
|
|
3669
|
+
const emitted = self.recordImpression(slotId, {
|
|
3670
|
+
adUnitCode,
|
|
3671
|
+
auctionId: auctionId || cached?.auctionId,
|
|
3672
|
+
transactionId: transactionId || cached?.transactionId,
|
|
3673
|
+
mediaType: "video",
|
|
3674
|
+
bid: mergedBid,
|
|
3675
|
+
metadata: {
|
|
3676
|
+
...options?.metadata,
|
|
3677
|
+
media_type: "video",
|
|
3678
|
+
...vEl && Number.isFinite(vEl.duration) ? { video_duration_s: String(Math.round(vEl.duration)) } : {}
|
|
3679
|
+
}
|
|
3680
|
+
});
|
|
3681
|
+
if (emitted && options?.onImpression) {
|
|
3814
3682
|
try {
|
|
3815
|
-
|
|
3683
|
+
options.onImpression(slotId, { element: vEl || el, bid: mergedBid });
|
|
3816
3684
|
} catch (e) {
|
|
3817
|
-
|
|
3685
|
+
self.log("ERROR", "Error in onImpression callback", e);
|
|
3818
3686
|
}
|
|
3819
3687
|
}
|
|
3820
|
-
const analytics = new BidkernelPrebidAnalytics(merged);
|
|
3821
|
-
analytics.enable();
|
|
3822
|
-
win._bidkernelPrebidAnalytics = analytics;
|
|
3823
|
-
return analytics;
|
|
3824
3688
|
};
|
|
3825
|
-
const
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
}
|
|
3689
|
+
const handlePlaying = (e) => {
|
|
3690
|
+
self.log("DEBUG", "HTML5 video playing event received");
|
|
3691
|
+
const targetVideo = e?.target instanceof HTMLVideoElement ? e.target : videoEl;
|
|
3692
|
+
triggerPlaybackImpression(targetVideo);
|
|
3693
|
+
};
|
|
3694
|
+
const handleTimeUpdate = (e) => {
|
|
3695
|
+
const targetVideo = e?.target instanceof HTMLVideoElement ? e.target : videoEl;
|
|
3696
|
+
if (targetVideo && targetVideo.currentTime > 0) {
|
|
3697
|
+
triggerPlaybackImpression(targetVideo);
|
|
3698
|
+
if (Number.isFinite(targetVideo.duration) && targetVideo.duration > 0) {
|
|
3699
|
+
const progress = targetVideo.currentTime / targetVideo.duration;
|
|
3700
|
+
if (progress >= 0.25 && !quartilesFired.q1) {
|
|
3701
|
+
quartilesFired.q1 = true;
|
|
3702
|
+
options?.onMilestone?.("firstQuartile", slotId);
|
|
3703
|
+
}
|
|
3704
|
+
if (progress >= 0.5 && !quartilesFired.q2) {
|
|
3705
|
+
quartilesFired.q2 = true;
|
|
3706
|
+
options?.onMilestone?.("midpoint", slotId);
|
|
3707
|
+
}
|
|
3708
|
+
if (progress >= 0.75 && !quartilesFired.q3) {
|
|
3709
|
+
quartilesFired.q3 = true;
|
|
3710
|
+
options?.onMilestone?.("thirdQuartile", slotId);
|
|
3711
|
+
}
|
|
3844
3712
|
}
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3713
|
+
}
|
|
3714
|
+
};
|
|
3715
|
+
const handleEnded = () => {
|
|
3716
|
+
if (!quartilesFired.q4) {
|
|
3717
|
+
quartilesFired.q4 = true;
|
|
3718
|
+
options?.onMilestone?.("complete", slotId);
|
|
3719
|
+
}
|
|
3720
|
+
};
|
|
3721
|
+
const handleError = (e) => {
|
|
3722
|
+
const targetVideo = e?.target instanceof HTMLVideoElement ? e.target : videoEl;
|
|
3723
|
+
const err = targetVideo?.error || videoEl?.error;
|
|
3724
|
+
const cached = getWinningBid();
|
|
3725
|
+
const msg = err ? `HTML5 video error code ${err.code}: ${err.message}` : "Video playback error";
|
|
3726
|
+
self.log("WARN", msg);
|
|
3727
|
+
self.enqueue(TraceEventType.AD_RENDER_FAILED, "adRenderFailed", {
|
|
3728
|
+
auctionId: auctionId || cached?.auctionId,
|
|
3729
|
+
transactionId: transactionId || cached?.transactionId,
|
|
3730
|
+
adUnitCode,
|
|
3731
|
+
bid: cached?.bidTrace || options?.bid,
|
|
3732
|
+
metadata: {
|
|
3733
|
+
...options?.metadata,
|
|
3734
|
+
reason: "html5_video_error",
|
|
3735
|
+
message: msg,
|
|
3736
|
+
...err?.code ? { error_code: String(err.code) } : {}
|
|
3737
|
+
},
|
|
3738
|
+
error: err ? new Error(msg) : void 0
|
|
3739
|
+
});
|
|
3740
|
+
if (options?.onError) {
|
|
3741
|
+
try {
|
|
3742
|
+
options.onError(err);
|
|
3743
|
+
} catch (e2) {
|
|
3744
|
+
self.log("ERROR", "Error in onError callback", e2);
|
|
3848
3745
|
}
|
|
3849
|
-
} catch (e) {
|
|
3850
|
-
defaultLogger.warn("Failed to register standard Prebid analytics adapter:", e);
|
|
3851
3746
|
}
|
|
3852
3747
|
};
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3748
|
+
const attachListenersToVideo = (targetVideo) => {
|
|
3749
|
+
targetVideo.addEventListener("playing", handlePlaying);
|
|
3750
|
+
targetVideo.addEventListener("play", handlePlaying);
|
|
3751
|
+
targetVideo.addEventListener("timeupdate", handleTimeUpdate);
|
|
3752
|
+
targetVideo.addEventListener("ended", handleEnded);
|
|
3753
|
+
targetVideo.addEventListener("error", handleError);
|
|
3754
|
+
};
|
|
3755
|
+
const removeListenersFromVideo = (targetVideo) => {
|
|
3756
|
+
targetVideo.removeEventListener("playing", handlePlaying);
|
|
3757
|
+
targetVideo.removeEventListener("play", handlePlaying);
|
|
3758
|
+
targetVideo.removeEventListener("timeupdate", handleTimeUpdate);
|
|
3759
|
+
targetVideo.removeEventListener("ended", handleEnded);
|
|
3760
|
+
targetVideo.removeEventListener("error", handleError);
|
|
3761
|
+
};
|
|
3762
|
+
let mutationObserver = null;
|
|
3763
|
+
if (el && el !== videoEl) {
|
|
3764
|
+
el.addEventListener("playing", handlePlaying, true);
|
|
3765
|
+
el.addEventListener("play", handlePlaying, true);
|
|
3766
|
+
el.addEventListener("timeupdate", handleTimeUpdate, true);
|
|
3767
|
+
el.addEventListener("ended", handleEnded, true);
|
|
3768
|
+
el.addEventListener("error", handleError, true);
|
|
3769
|
+
}
|
|
3770
|
+
if (videoEl) {
|
|
3771
|
+
attachListenersToVideo(videoEl);
|
|
3772
|
+
}
|
|
3773
|
+
if (el && el.tagName?.toLowerCase() !== "video" && typeof MutationObserver !== "undefined") {
|
|
3774
|
+
mutationObserver = new MutationObserver(() => {
|
|
3775
|
+
const found = el.querySelector("video");
|
|
3776
|
+
if (found && found !== videoEl) {
|
|
3777
|
+
if (videoEl) removeListenersFromVideo(videoEl);
|
|
3778
|
+
videoEl = found;
|
|
3779
|
+
if (videoEl) attachListenersToVideo(videoEl);
|
|
3780
|
+
}
|
|
3781
|
+
});
|
|
3782
|
+
mutationObserver.observe(el, { childList: true, subtree: true });
|
|
3858
3783
|
}
|
|
3784
|
+
const cachedInitial = getWinningBid();
|
|
3785
|
+
if (options?.trackViewability !== false && el) {
|
|
3786
|
+
self.observeSlot(el, slotId, {
|
|
3787
|
+
adUnitCode,
|
|
3788
|
+
auctionId: auctionId || cachedInitial?.auctionId,
|
|
3789
|
+
transactionId: transactionId || cachedInitial?.transactionId,
|
|
3790
|
+
mediaType: "video",
|
|
3791
|
+
bid: cachedInitial?.bidTrace || options?.bid,
|
|
3792
|
+
metadata: options?.metadata,
|
|
3793
|
+
emitRefreshEvent: false
|
|
3794
|
+
});
|
|
3795
|
+
}
|
|
3796
|
+
const cleanup = () => {
|
|
3797
|
+
if (mutationObserver) {
|
|
3798
|
+
mutationObserver.disconnect();
|
|
3799
|
+
mutationObserver = null;
|
|
3800
|
+
}
|
|
3801
|
+
if (el && el !== videoEl) {
|
|
3802
|
+
el.removeEventListener("playing", handlePlaying, true);
|
|
3803
|
+
el.removeEventListener("play", handlePlaying, true);
|
|
3804
|
+
el.removeEventListener("timeupdate", handleTimeUpdate, true);
|
|
3805
|
+
el.removeEventListener("ended", handleEnded, true);
|
|
3806
|
+
el.removeEventListener("error", handleError, true);
|
|
3807
|
+
}
|
|
3808
|
+
if (videoEl) {
|
|
3809
|
+
removeListenersFromVideo(videoEl);
|
|
3810
|
+
}
|
|
3811
|
+
self.videoDetachCleanups.delete(cleanup);
|
|
3812
|
+
};
|
|
3813
|
+
self.videoDetachCleanups.add(cleanup);
|
|
3814
|
+
return cleanup;
|
|
3859
3815
|
}
|
|
3816
|
+
BidkernelPrebidAnalytics.prototype.attachImaAdsManager = function(adsManager, options) {
|
|
3817
|
+
return attachImaAdsManager(this, adsManager, options);
|
|
3818
|
+
};
|
|
3819
|
+
BidkernelPrebidAnalytics.prototype.attachVideoPlayer = function(target, options) {
|
|
3820
|
+
return attachVideoPlayer(this, target, options);
|
|
3821
|
+
};
|
|
3822
|
+
var coreEnable = BidkernelPrebidAnalytics.prototype.enable;
|
|
3823
|
+
BidkernelPrebidAnalytics.prototype.enable = function() {
|
|
3824
|
+
coreEnable.call(this);
|
|
3825
|
+
if (typeof window !== "undefined") {
|
|
3826
|
+
initImaInterception();
|
|
3827
|
+
if (window.google?.ima) {
|
|
3828
|
+
hookImaPrototype(window.google.ima);
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
};
|
|
3860
3832
|
|
|
3861
3833
|
// src/index.ts
|
|
3862
3834
|
function getbidkernel(alias = "bidkernel") {
|
|
@@ -3891,7 +3863,10 @@ function getbidkernel(alias = "bidkernel") {
|
|
|
3891
3863
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3892
3864
|
0 && (module.exports = {
|
|
3893
3865
|
BidkernelPrebidAnalytics,
|
|
3866
|
+
CACHED_BID_TTL_MS,
|
|
3894
3867
|
PrebidEventDeduper,
|
|
3868
|
+
attachImaAdsManager,
|
|
3869
|
+
attachVideoPlayer,
|
|
3895
3870
|
getOrCreateSessionId,
|
|
3896
3871
|
getPrebidEventKey,
|
|
3897
3872
|
getbidkernel,
|