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