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