@bidkernel/analytics 0.12.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 +130 -56
- package/dist/index.d.ts +130 -56
- package/dist/index.js +1412 -1333
- package/dist/index.mjs +1408 -1333
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21,7 +21,11 @@ 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,
|
|
28
|
+
getOrCreateSessionId: () => getOrCreateSessionId,
|
|
25
29
|
getPrebidEventKey: () => getPrebidEventKey,
|
|
26
30
|
getbidkernel: () => getbidkernel,
|
|
27
31
|
hookImaPrototype: () => hookImaPrototype,
|
|
@@ -544,28 +548,6 @@ var TraceEventBatch = {
|
|
|
544
548
|
reader.skip(tag & 7);
|
|
545
549
|
}
|
|
546
550
|
return message;
|
|
547
|
-
},
|
|
548
|
-
create(base) {
|
|
549
|
-
return TraceEventBatch.fromPartial(base ?? {});
|
|
550
|
-
},
|
|
551
|
-
fromPartial(object) {
|
|
552
|
-
const message = createBaseTraceEventBatch();
|
|
553
|
-
message.propertyId = object.propertyId ?? "";
|
|
554
|
-
message.pageviewId = object.pageviewId ?? "";
|
|
555
|
-
message.sessionId = object.sessionId ?? "";
|
|
556
|
-
message.pageUrl = object.pageUrl ?? "";
|
|
557
|
-
message.country = object.country ?? "";
|
|
558
|
-
message.region = object.region ?? "";
|
|
559
|
-
message.deviceType = object.deviceType ?? "";
|
|
560
|
-
message.userId = object.userId ?? "";
|
|
561
|
-
message.domain = object.domain ?? "";
|
|
562
|
-
message.events = object.events?.map((e) => TraceEvent.fromPartial(e)) || [];
|
|
563
|
-
message.browser = object.browser ?? "";
|
|
564
|
-
message.deviceManufacturer = object.deviceManufacturer ?? "";
|
|
565
|
-
message.deviceModel = object.deviceModel ?? "";
|
|
566
|
-
message.city = object.city ?? "";
|
|
567
|
-
message.postalCode = object.postalCode ?? "";
|
|
568
|
-
return message;
|
|
569
551
|
}
|
|
570
552
|
};
|
|
571
553
|
function createBaseTraceEvent() {
|
|
@@ -730,33 +712,6 @@ var TraceEvent = {
|
|
|
730
712
|
reader.skip(tag & 7);
|
|
731
713
|
}
|
|
732
714
|
return message;
|
|
733
|
-
},
|
|
734
|
-
create(base) {
|
|
735
|
-
return TraceEvent.fromPartial(base ?? {});
|
|
736
|
-
},
|
|
737
|
-
fromPartial(object) {
|
|
738
|
-
const message = createBaseTraceEvent();
|
|
739
|
-
message.timestampMs = object.timestampMs ?? 0;
|
|
740
|
-
message.type = object.type ?? 0;
|
|
741
|
-
message.eventName = object.eventName ?? "";
|
|
742
|
-
message.auctionId = object.auctionId ?? "";
|
|
743
|
-
message.transactionId = object.transactionId ?? "";
|
|
744
|
-
message.adUnitCode = object.adUnitCode ?? "";
|
|
745
|
-
message.bid = object.bid !== void 0 && object.bid !== null ? BidTrace.fromPartial(object.bid) : void 0;
|
|
746
|
-
message.namespace = object.namespace ?? "";
|
|
747
|
-
message.eventId = object.eventId ?? "";
|
|
748
|
-
message.metadata = globalThis.Object.entries(object.metadata ?? {}).reduce(
|
|
749
|
-
(acc, [key, value]) => {
|
|
750
|
-
if (value !== void 0) {
|
|
751
|
-
acc[key] = globalThis.String(value);
|
|
752
|
-
}
|
|
753
|
-
return acc;
|
|
754
|
-
},
|
|
755
|
-
{}
|
|
756
|
-
);
|
|
757
|
-
message.viewableDurationMs = object.viewableDurationMs ?? void 0;
|
|
758
|
-
message.bidderOutcomes = object.bidderOutcomes?.map((e) => BidderOutcomeEntry.fromPartial(e)) || [];
|
|
759
|
-
return message;
|
|
760
715
|
}
|
|
761
716
|
};
|
|
762
717
|
function createBaseTraceEvent_MetadataEntry() {
|
|
@@ -800,15 +755,6 @@ var TraceEvent_MetadataEntry = {
|
|
|
800
755
|
reader.skip(tag & 7);
|
|
801
756
|
}
|
|
802
757
|
return message;
|
|
803
|
-
},
|
|
804
|
-
create(base) {
|
|
805
|
-
return TraceEvent_MetadataEntry.fromPartial(base ?? {});
|
|
806
|
-
},
|
|
807
|
-
fromPartial(object) {
|
|
808
|
-
const message = createBaseTraceEvent_MetadataEntry();
|
|
809
|
-
message.key = object.key ?? "";
|
|
810
|
-
message.value = object.value ?? "";
|
|
811
|
-
return message;
|
|
812
758
|
}
|
|
813
759
|
};
|
|
814
760
|
function createBaseBidderOutcomeEntry() {
|
|
@@ -882,18 +828,6 @@ var BidderOutcomeEntry = {
|
|
|
882
828
|
reader.skip(tag & 7);
|
|
883
829
|
}
|
|
884
830
|
return message;
|
|
885
|
-
},
|
|
886
|
-
create(base) {
|
|
887
|
-
return BidderOutcomeEntry.fromPartial(base ?? {});
|
|
888
|
-
},
|
|
889
|
-
fromPartial(object) {
|
|
890
|
-
const message = createBaseBidderOutcomeEntry();
|
|
891
|
-
message.bidder = object.bidder ?? "";
|
|
892
|
-
message.adUnitCode = object.adUnitCode ?? "";
|
|
893
|
-
message.mediaType = object.mediaType ?? "";
|
|
894
|
-
message.outcome = object.outcome ?? 0;
|
|
895
|
-
message.latencyMs = object.latencyMs ?? 0;
|
|
896
|
-
return message;
|
|
897
831
|
}
|
|
898
832
|
};
|
|
899
833
|
function createBaseBidTrace() {
|
|
@@ -1028,23 +962,6 @@ var BidTrace = {
|
|
|
1028
962
|
reader.skip(tag & 7);
|
|
1029
963
|
}
|
|
1030
964
|
return message;
|
|
1031
|
-
},
|
|
1032
|
-
create(base) {
|
|
1033
|
-
return BidTrace.fromPartial(base ?? {});
|
|
1034
|
-
},
|
|
1035
|
-
fromPartial(object) {
|
|
1036
|
-
const message = createBaseBidTrace();
|
|
1037
|
-
message.bidder = object.bidder ?? "";
|
|
1038
|
-
message.cpm = object.cpm ?? 0;
|
|
1039
|
-
message.currency = object.currency ?? "";
|
|
1040
|
-
message.width = object.width ?? 0;
|
|
1041
|
-
message.height = object.height ?? 0;
|
|
1042
|
-
message.dealId = object.dealId ?? "";
|
|
1043
|
-
message.mediaType = object.mediaType ?? "";
|
|
1044
|
-
message.latencyMs = object.latencyMs ?? 0;
|
|
1045
|
-
message.advertiserDomain = object.advertiserDomain ?? "";
|
|
1046
|
-
message.creativeId = object.creativeId ?? "";
|
|
1047
|
-
return message;
|
|
1048
965
|
}
|
|
1049
966
|
};
|
|
1050
967
|
|
|
@@ -1085,11 +1002,13 @@ var CACHED_BID_TTL_MS = 30 * 60 * 1e3;
|
|
|
1085
1002
|
var MAX_CACHED_BID_KEYS = 200;
|
|
1086
1003
|
var MAX_IMPRESSION_KEYS = 1e3;
|
|
1087
1004
|
var MAX_TRACKED_AUCTIONS = 50;
|
|
1005
|
+
var CLICK_DEDUP_MS = 1e3;
|
|
1088
1006
|
var SESSION_KEY = "_bidkernel_session";
|
|
1089
1007
|
var SESSION_TS_KEY = "_bidkernel_session_ts";
|
|
1090
1008
|
var THIRTY_MINUTES_MS = 30 * 60 * 1e3;
|
|
1091
1009
|
var inMemorySessionId = null;
|
|
1092
1010
|
var inMemorySessionTs = 0;
|
|
1011
|
+
var storageAllowed = true;
|
|
1093
1012
|
function generateUUID() {
|
|
1094
1013
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
1095
1014
|
return crypto.randomUUID();
|
|
@@ -1156,30 +1075,43 @@ function sameEndpointOrigin(a, b) {
|
|
|
1156
1075
|
return false;
|
|
1157
1076
|
}
|
|
1158
1077
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1078
|
+
var SESSION_PERSIST_INTERVAL_MS = 60 * 1e3;
|
|
1079
|
+
var persistedSessionTs = 0;
|
|
1080
|
+
function persistSessionTs(now) {
|
|
1162
1081
|
try {
|
|
1163
1082
|
if (typeof localStorage !== "undefined") {
|
|
1164
1083
|
localStorage.setItem(SESSION_TS_KEY, now.toString());
|
|
1084
|
+
persistedSessionTs = now;
|
|
1165
1085
|
}
|
|
1166
1086
|
} catch {
|
|
1167
1087
|
}
|
|
1168
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
|
+
}
|
|
1169
1096
|
function getOrCreateSessionId() {
|
|
1170
1097
|
const now = Date.now();
|
|
1098
|
+
const memoryFresh = !!inMemorySessionId && !!inMemorySessionTs && now - inMemorySessionTs <= THIRTY_MINUTES_MS;
|
|
1171
1099
|
try {
|
|
1172
|
-
if (typeof localStorage !== "undefined") {
|
|
1100
|
+
if (storageAllowed && typeof localStorage !== "undefined") {
|
|
1173
1101
|
const storedId = localStorage.getItem(SESSION_KEY);
|
|
1174
1102
|
const tsStr = localStorage.getItem(SESSION_TS_KEY);
|
|
1175
1103
|
const storedTs = tsStr ? parseInt(tsStr, 10) || 0 : 0;
|
|
1176
1104
|
if (storedId && storedTs && now - storedTs <= THIRTY_MINUTES_MS) {
|
|
1177
|
-
|
|
1105
|
+
persistSessionTs(now);
|
|
1106
|
+
inMemorySessionId = storedId;
|
|
1107
|
+
inMemorySessionTs = now;
|
|
1178
1108
|
return storedId;
|
|
1179
1109
|
}
|
|
1180
|
-
const newId = generateUUID();
|
|
1110
|
+
const newId = memoryFresh ? inMemorySessionId : generateUUID();
|
|
1181
1111
|
localStorage.setItem(SESSION_KEY, newId);
|
|
1182
|
-
|
|
1112
|
+
persistSessionTs(now);
|
|
1113
|
+
inMemorySessionId = newId;
|
|
1114
|
+
inMemorySessionTs = now;
|
|
1183
1115
|
return newId;
|
|
1184
1116
|
}
|
|
1185
1117
|
} catch {
|
|
@@ -1287,557 +1219,334 @@ function parseBidDimensions(bid) {
|
|
|
1287
1219
|
height: Number.isFinite(bid?.height) ? bid.height : 0
|
|
1288
1220
|
};
|
|
1289
1221
|
}
|
|
1290
|
-
var
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1222
|
+
var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
1223
|
+
static activeInstances = /* @__PURE__ */ new Set();
|
|
1224
|
+
static getActiveInstances() {
|
|
1225
|
+
return _BidkernelPrebidAnalytics.activeInstances;
|
|
1226
|
+
}
|
|
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);
|
|
1311
1285
|
}
|
|
1312
|
-
|
|
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
|
+
);
|
|
1316
|
+
}
|
|
1317
|
+
this.boundFlushBeacon = () => {
|
|
1318
|
+
this.flushAllSlotsTimeInView();
|
|
1319
|
+
this.flushBeacon();
|
|
1320
|
+
};
|
|
1321
|
+
this.boundVisibilityChange = () => this.handleVisibilityChange();
|
|
1313
1322
|
}
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
if (
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
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);
|
|
1326
1339
|
}
|
|
1327
1340
|
}
|
|
1328
|
-
|
|
1329
|
-
};
|
|
1330
|
-
hookedInit.__bidkernelHooked = true;
|
|
1331
|
-
proto.init = hookedInit;
|
|
1341
|
+
}
|
|
1332
1342
|
}
|
|
1333
|
-
if (
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1343
|
+
if (this.config.attachPbjsListeners) {
|
|
1344
|
+
this.attachPbjs();
|
|
1345
|
+
}
|
|
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;
|
|
1341
1353
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1354
|
+
}
|
|
1355
|
+
window.addEventListener("pagehide", this.boundFlushBeacon);
|
|
1356
|
+
if (typeof document !== "undefined") {
|
|
1357
|
+
document.addEventListener("visibilitychange", this.boundVisibilityChange);
|
|
1358
|
+
}
|
|
1359
|
+
this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
|
|
1360
|
+
this.resendPersistedBatches();
|
|
1346
1361
|
}
|
|
1347
|
-
} catch {
|
|
1348
1362
|
}
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
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;
|
|
1380
|
+
}
|
|
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") {
|
|
1398
|
+
try {
|
|
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);
|
|
1377
1415
|
}
|
|
1378
|
-
|
|
1379
|
-
};
|
|
1380
|
-
adsLoaderListenerMap.set(listener, wrappedListener);
|
|
1416
|
+
}
|
|
1381
1417
|
}
|
|
1382
|
-
return origAddEventListener.call(this, type, wrappedListener, ...rest);
|
|
1383
1418
|
}
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
proto.addEventListener = hookedAddEventListener;
|
|
1388
|
-
}
|
|
1389
|
-
if (typeof proto.removeEventListener === "function" && !proto.removeEventListener.__bidkernelHooked) {
|
|
1390
|
-
const origRemoveEventListener = proto.removeEventListener;
|
|
1391
|
-
const hookedRemoveEventListener = function(type, listener, ...rest) {
|
|
1392
|
-
const wrapped = typeof listener === "function" ? adsLoaderListenerMap.get(listener) : void 0;
|
|
1393
|
-
return origRemoveEventListener.call(this, type, wrapped || listener, ...rest);
|
|
1394
|
-
};
|
|
1395
|
-
hookedRemoveEventListener.__bidkernelHooked = true;
|
|
1396
|
-
proto.removeEventListener = hookedRemoveEventListener;
|
|
1419
|
+
} catch (e) {
|
|
1420
|
+
this.log("WARN", "Failed to replay historical events from pbjs.getEvents()", e);
|
|
1421
|
+
}
|
|
1397
1422
|
}
|
|
1398
|
-
|
|
1423
|
+
return true;
|
|
1399
1424
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
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
|
+
}
|
|
1406
1442
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
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) {
|
|
1410
1450
|
try {
|
|
1411
|
-
|
|
1412
|
-
configurable: true,
|
|
1413
|
-
enumerable: true,
|
|
1414
|
-
get() {
|
|
1415
|
-
return _loadedEvent;
|
|
1416
|
-
},
|
|
1417
|
-
set(val) {
|
|
1418
|
-
_loadedEvent = val;
|
|
1419
|
-
hookAdsManagerLoadedEvent(val);
|
|
1420
|
-
}
|
|
1421
|
-
});
|
|
1451
|
+
cleanup();
|
|
1422
1452
|
} catch {
|
|
1423
1453
|
}
|
|
1424
1454
|
}
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
}
|
|
1428
|
-
const loaderDesc = Object.getOwnPropertyDescriptor(ima, "AdsLoader");
|
|
1429
|
-
if (!loaderDesc || loaderDesc.configurable) {
|
|
1430
|
-
let _loader = ima.AdsLoader;
|
|
1455
|
+
this.videoDetachCleanups.clear();
|
|
1456
|
+
for (const cleanup of Array.from(this.clickDetachCleanups)) {
|
|
1431
1457
|
try {
|
|
1432
|
-
|
|
1433
|
-
configurable: true,
|
|
1434
|
-
enumerable: true,
|
|
1435
|
-
get() {
|
|
1436
|
-
return _loader;
|
|
1437
|
-
},
|
|
1438
|
-
set(val) {
|
|
1439
|
-
_loader = val;
|
|
1440
|
-
hookAdsLoader(val);
|
|
1441
|
-
}
|
|
1442
|
-
});
|
|
1458
|
+
cleanup();
|
|
1443
1459
|
} catch {
|
|
1444
1460
|
}
|
|
1445
1461
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
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;
|
|
1448
1471
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
hookAdsManagerPrototype(val);
|
|
1462
|
-
}
|
|
1463
|
-
});
|
|
1464
|
-
} catch {
|
|
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);
|
|
1465
1484
|
}
|
|
1466
1485
|
}
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
hookImaPrototype(win.google.ima);
|
|
1475
|
-
}
|
|
1476
|
-
if (win.google && typeof win.google === "object") {
|
|
1477
|
-
const imaDesc = Object.getOwnPropertyDescriptor(win.google, "ima");
|
|
1478
|
-
if (!imaDesc || imaDesc.configurable) {
|
|
1479
|
-
let _ima = win.google.ima;
|
|
1480
|
-
try {
|
|
1481
|
-
Object.defineProperty(win.google, "ima", {
|
|
1482
|
-
configurable: true,
|
|
1483
|
-
enumerable: true,
|
|
1484
|
-
get() {
|
|
1485
|
-
return _ima;
|
|
1486
|
-
},
|
|
1487
|
-
set(val) {
|
|
1488
|
-
_ima = val;
|
|
1489
|
-
hookImaPrototype(val);
|
|
1490
|
-
}
|
|
1491
|
-
});
|
|
1492
|
-
} catch {
|
|
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);
|
|
1492
|
+
}
|
|
1493
1493
|
}
|
|
1494
|
+
this.boundPbjsHandlers = [];
|
|
1494
1495
|
}
|
|
1496
|
+
this.flush();
|
|
1495
1497
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
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
|
-
}
|
|
1498
|
+
handleVisibilityChange() {
|
|
1499
|
+
if (typeof document === "undefined") return;
|
|
1500
|
+
if (document.visibilityState === "hidden") {
|
|
1501
|
+
const now = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
1502
|
+
for (const record of this.slotViewabilityRecords.values()) {
|
|
1503
|
+
if (record.inView && record.lastEnteredViewAt !== null) {
|
|
1504
|
+
record.accumulatedTimeInViewMs += Math.max(0, now - record.lastEnteredViewAt);
|
|
1505
|
+
record.lastEnteredViewAt = null;
|
|
1506
|
+
this.checkThresholdListeners(record);
|
|
1507
|
+
}
|
|
1508
|
+
if (record.dwellTimer) {
|
|
1509
|
+
clearTimeout(record.dwellTimer);
|
|
1510
|
+
record.dwellTimer = null;
|
|
1511
|
+
record.dwellStartedAt = null;
|
|
1512
|
+
}
|
|
1513
|
+
for (const l of record.thresholdListeners) {
|
|
1514
|
+
if (l.timer) {
|
|
1515
|
+
clearTimeout(l.timer);
|
|
1516
|
+
l.timer = null;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
const durationMs = Math.round(record.accumulatedTimeInViewMs);
|
|
1520
|
+
if (durationMs > 0) {
|
|
1521
|
+
this.enqueue(TraceEventType.TIME_IN_VIEW, "timeInView", {
|
|
1522
|
+
auctionId: record.auctionId,
|
|
1523
|
+
transactionId: record.transactionId,
|
|
1524
|
+
adUnitCode: record.adUnitCode,
|
|
1525
|
+
viewableDurationMs: durationMs,
|
|
1526
|
+
bid: record.bidPayload,
|
|
1527
|
+
metadata: {
|
|
1528
|
+
...record.metadata,
|
|
1529
|
+
refresh_index: String(record.refreshIndex)
|
|
1529
1530
|
}
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
this.flushBeacon();
|
|
1535
|
+
} else if (document.visibilityState === "visible") {
|
|
1536
|
+
const now = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
1537
|
+
for (const record of this.slotViewabilityRecords.values()) {
|
|
1538
|
+
if (record.inView) {
|
|
1539
|
+
record.lastEnteredViewAt = now;
|
|
1540
|
+
if (!record.viewableFired && record.dwellTimer === null) {
|
|
1541
|
+
const dwellTarget = record.mediaType === "video" ? 2e3 : 1e3;
|
|
1542
|
+
record.dwellStartedAt = now;
|
|
1543
|
+
record.dwellTimer = setTimeout(() => {
|
|
1544
|
+
this.handleDwellComplete(record);
|
|
1545
|
+
}, dwellTarget);
|
|
1530
1546
|
}
|
|
1547
|
+
this.scheduleThresholdTimers(record);
|
|
1531
1548
|
}
|
|
1532
|
-
}
|
|
1533
|
-
} catch {
|
|
1534
|
-
}
|
|
1535
|
-
}
|
|
1536
|
-
}
|
|
1537
|
-
if (typeof window !== "undefined") {
|
|
1538
|
-
initImaInterception();
|
|
1539
|
-
}
|
|
1540
|
-
var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
1541
|
-
static activeInstances = /* @__PURE__ */ new Set();
|
|
1542
|
-
static getActiveInstances() {
|
|
1543
|
-
return _BidkernelPrebidAnalytics.activeInstances;
|
|
1544
|
-
}
|
|
1545
|
-
static initImaInterception() {
|
|
1546
|
-
initImaInterception();
|
|
1547
|
-
}
|
|
1548
|
-
config;
|
|
1549
|
-
queue = [];
|
|
1550
|
-
errorCount = 0;
|
|
1551
|
-
flushTimer = null;
|
|
1552
|
-
boundFlushBeacon;
|
|
1553
|
-
boundVisibilityChange;
|
|
1554
|
-
isEnabled = false;
|
|
1555
|
-
boundPbjsHandlers = [];
|
|
1556
|
-
// Captured at enable()/navigate() so events queued before an SPA route
|
|
1557
|
-
// change flush with the page they occurred on, not the new URL.
|
|
1558
|
-
pageUrl = "";
|
|
1559
|
-
deduper = new PrebidEventDeduper();
|
|
1560
|
-
consecutiveSendFailures = 0;
|
|
1561
|
-
nextSendAllowedAt = 0;
|
|
1562
|
-
replayedEventCount = 0;
|
|
1563
|
-
revenueFlushTimer = null;
|
|
1564
|
-
// localStorage keys of exit batches this instance persisted, so a page that
|
|
1565
|
-
// survives its own pagehide/hidden (bfcache restore, tab re-focus) can
|
|
1566
|
-
// remove them instead of leaving them for a duplicate resend.
|
|
1567
|
-
persistedBatchKeys = [];
|
|
1568
|
-
persistedCleanupTimer = null;
|
|
1569
|
-
// Viewability & refresh tracking
|
|
1570
|
-
intersectionObserver = null;
|
|
1571
|
-
slotViewabilityRecords = /* @__PURE__ */ new Map();
|
|
1572
|
-
elementToSlotId = /* @__PURE__ */ new Map();
|
|
1573
|
-
slotRefreshIndices = /* @__PURE__ */ new Map();
|
|
1574
|
-
slotLastAuctionIds = /* @__PURE__ */ new Map();
|
|
1575
|
-
pendingThresholdListeners = /* @__PURE__ */ new Map();
|
|
1576
|
-
// Impression deduplication per slot and refresh cycle (strictly 1 impression per cycle)
|
|
1577
|
-
slotEmittedImpressionKeys = /* @__PURE__ */ new Set();
|
|
1578
|
-
// Winning bid cache from bidWon to marry with subsequent render triggers (adRenderSucceeded, video, etc.)
|
|
1579
|
-
cachedWinningBids = /* @__PURE__ */ new Map();
|
|
1580
|
-
// Active video player attachment cleanup routines
|
|
1581
|
-
videoDetachCleanups = /* @__PURE__ */ new Set();
|
|
1582
|
-
// Compacted bidder participation map per auction: auctionId -> Map<`${bidder}:${adUnitCode}`, BidderOutcomeEntry>
|
|
1583
|
-
auctionBidderOutcomes = /* @__PURE__ */ new Map();
|
|
1584
|
-
logger;
|
|
1585
|
-
constructor(config) {
|
|
1586
|
-
const requestedEndpoint = config.endpoint || "";
|
|
1587
|
-
const endpoint = !requestedEndpoint || isTrustedEndpoint(requestedEndpoint) ? requestedEndpoint : "";
|
|
1588
|
-
this.config = {
|
|
1589
|
-
endpoint,
|
|
1590
|
-
propertyId: config.propertyId || "",
|
|
1591
|
-
pageviewId: config.pageviewId || generateUUID(),
|
|
1592
|
-
sessionId: config.sessionId || getOrCreateSessionId(),
|
|
1593
|
-
userId: config.userId || "",
|
|
1594
|
-
deviceType: config.deviceType || "desktop",
|
|
1595
|
-
country: config.country || "",
|
|
1596
|
-
region: config.region || "",
|
|
1597
|
-
auctionEnabled: config.auctionEnabled ?? true,
|
|
1598
|
-
warningsEnabled: config.warningsEnabled ?? true,
|
|
1599
|
-
errorsEnabled: config.errorsEnabled ?? true,
|
|
1600
|
-
viewabilityEnabled: config.viewabilityEnabled ?? true,
|
|
1601
|
-
logLevel: config.logLevel || "INFO",
|
|
1602
|
-
pbjsGlobalName: config.pbjsGlobalName || "pbjs",
|
|
1603
|
-
attachPbjsListeners: config.attachPbjsListeners ?? true,
|
|
1604
|
-
revenueFlushDelayMs: Number.isFinite(config.revenueFlushDelayMs) && config.revenueFlushDelayMs >= 0 ? config.revenueFlushDelayMs : REVENUE_FLUSH_DELAY_MS
|
|
1605
|
-
};
|
|
1606
|
-
this.logger = createLogger({
|
|
1607
|
-
prefix: "[bidkernel][prebid-analytics]",
|
|
1608
|
-
logLevel: this.config.logLevel
|
|
1609
|
-
});
|
|
1610
|
-
if (requestedEndpoint && !endpoint) {
|
|
1611
|
-
this.log(
|
|
1612
|
-
"ERROR",
|
|
1613
|
-
`Rejected untrusted analytics endpoint ${requestedEndpoint}: must be an absolute https:// URL without embedded credentials. Events will not be transmitted.`
|
|
1614
|
-
);
|
|
1615
|
-
}
|
|
1616
|
-
this.boundFlushBeacon = () => {
|
|
1617
|
-
this.flushAllSlotsTimeInView();
|
|
1618
|
-
this.flushBeacon();
|
|
1619
|
-
};
|
|
1620
|
-
this.boundVisibilityChange = () => this.handleVisibilityChange();
|
|
1621
|
-
}
|
|
1622
|
-
enable() {
|
|
1623
|
-
if (this.isEnabled) return;
|
|
1624
|
-
this.isEnabled = true;
|
|
1625
|
-
_BidkernelPrebidAnalytics.activeInstances.add(this);
|
|
1626
|
-
this.logger.setLevel(this.config.logLevel);
|
|
1627
|
-
if (!this.config.endpoint) {
|
|
1628
|
-
this.log("WARN", "Endpoint is empty. Analytics events will not be transmitted.");
|
|
1629
|
-
}
|
|
1630
|
-
if (this.config.viewabilityEnabled) {
|
|
1631
|
-
if (typeof IntersectionObserver !== "undefined") {
|
|
1632
|
-
this.intersectionObserver = new IntersectionObserver(this.handleIntersection.bind(this), {
|
|
1633
|
-
threshold: [0.5]
|
|
1634
|
-
});
|
|
1635
|
-
for (const record of this.slotViewabilityRecords.values()) {
|
|
1636
|
-
if (record.element) {
|
|
1637
|
-
this.intersectionObserver.observe(record.element);
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
if (this.config.attachPbjsListeners) {
|
|
1643
|
-
const win = typeof window !== "undefined" ? window : {};
|
|
1644
|
-
const pbjs = win[this.config.pbjsGlobalName] || {};
|
|
1645
|
-
if (typeof pbjs.onEvent === "function") {
|
|
1646
|
-
this.log("DEBUG", "Attaching event listeners to pbjs");
|
|
1647
|
-
const events = [
|
|
1648
|
-
["auctionInit", this.handleAuctionInit.bind(this)],
|
|
1649
|
-
["auctionEnd", this.handleAuctionEnd.bind(this)],
|
|
1650
|
-
["bidRequested", this.handleBidRequested.bind(this)],
|
|
1651
|
-
["bidResponse", this.handleBidResponse.bind(this)],
|
|
1652
|
-
["bidTimeout", this.handleBidTimeout.bind(this)],
|
|
1653
|
-
["bidWon", this.handleBidWon.bind(this)],
|
|
1654
|
-
["noBid", this.handleNoBid.bind(this)],
|
|
1655
|
-
["adRenderFailed", this.handleAdRenderFailed.bind(this)],
|
|
1656
|
-
["adRenderSucceeded", this.handleAdRenderSucceeded.bind(this)]
|
|
1657
|
-
];
|
|
1658
|
-
for (const [name, fn] of events) {
|
|
1659
|
-
pbjs.onEvent(name, fn);
|
|
1660
|
-
this.boundPbjsHandlers.push({ event: name, handler: fn });
|
|
1661
|
-
}
|
|
1662
|
-
} else {
|
|
1663
|
-
this.log("WARN", "pbjs.onEvent is not defined. Prebid analytics will not function.");
|
|
1664
|
-
}
|
|
1665
|
-
if (typeof pbjs.getEvents === "function") {
|
|
1666
|
-
try {
|
|
1667
|
-
const pastEvents = pbjs.getEvents();
|
|
1668
|
-
if (Array.isArray(pastEvents)) {
|
|
1669
|
-
const newPastEvents = pastEvents.slice(this.replayedEventCount);
|
|
1670
|
-
this.replayedEventCount = pastEvents.length;
|
|
1671
|
-
if (newPastEvents.length > 0) {
|
|
1672
|
-
this.log(
|
|
1673
|
-
"DEBUG",
|
|
1674
|
-
`Replaying ${newPastEvents.length} historical events from pbjs.getEvents()`
|
|
1675
|
-
);
|
|
1676
|
-
const handlerMap = {
|
|
1677
|
-
auctionInit: this.handleAuctionInit.bind(this),
|
|
1678
|
-
auctionEnd: this.handleAuctionEnd.bind(this),
|
|
1679
|
-
bidRequested: this.handleBidRequested.bind(this),
|
|
1680
|
-
bidResponse: this.handleBidResponse.bind(this),
|
|
1681
|
-
bidTimeout: this.handleBidTimeout.bind(this),
|
|
1682
|
-
bidWon: this.handleBidWon.bind(this),
|
|
1683
|
-
noBid: this.handleNoBid.bind(this),
|
|
1684
|
-
adRenderFailed: this.handleAdRenderFailed.bind(this),
|
|
1685
|
-
adRenderSucceeded: this.handleAdRenderSucceeded.bind(this)
|
|
1686
|
-
};
|
|
1687
|
-
for (const ev of newPastEvents) {
|
|
1688
|
-
if (!ev) continue;
|
|
1689
|
-
const eventType = ev.eventType || ev.event || ev.name;
|
|
1690
|
-
const args = ev.args !== void 0 ? ev.args : ev.data !== void 0 ? ev.data : ev;
|
|
1691
|
-
const handler = handlerMap[eventType];
|
|
1692
|
-
if (handler) {
|
|
1693
|
-
handler(args);
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
} catch (e) {
|
|
1699
|
-
this.log("WARN", "Failed to replay historical events from pbjs.getEvents()", e);
|
|
1700
|
-
}
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
if (typeof window !== "undefined") {
|
|
1704
|
-
if (!this.pageUrl) {
|
|
1705
|
-
try {
|
|
1706
|
-
const u = new URL(window.location.href);
|
|
1707
|
-
this.pageUrl = u.origin + u.pathname;
|
|
1708
|
-
} catch {
|
|
1709
|
-
this.pageUrl = window.location.href;
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
window.addEventListener("pagehide", this.boundFlushBeacon);
|
|
1713
|
-
if (typeof document !== "undefined") {
|
|
1714
|
-
document.addEventListener("visibilitychange", this.boundVisibilityChange);
|
|
1715
|
-
}
|
|
1716
|
-
this.flushTimer = setInterval(() => this.flush(), FLUSH_INTERVAL_MS);
|
|
1717
|
-
this.resendPersistedBatches();
|
|
1718
|
-
initImaInterception();
|
|
1719
|
-
if (window.google?.ima) {
|
|
1720
|
-
hookImaPrototype(window.google.ima);
|
|
1721
|
-
}
|
|
1722
|
-
}
|
|
1723
|
-
}
|
|
1724
|
-
disable() {
|
|
1725
|
-
if (!this.isEnabled) return;
|
|
1726
|
-
this.flushAllSlotsTimeInView();
|
|
1727
|
-
this.isEnabled = false;
|
|
1728
|
-
_BidkernelPrebidAnalytics.activeInstances.delete(this);
|
|
1729
|
-
for (const record of this.slotViewabilityRecords.values()) {
|
|
1730
|
-
if (record.dwellTimer) {
|
|
1731
|
-
clearTimeout(record.dwellTimer);
|
|
1732
|
-
record.dwellTimer = null;
|
|
1733
|
-
record.dwellStartedAt = null;
|
|
1734
|
-
}
|
|
1735
|
-
for (const l of record.thresholdListeners) {
|
|
1736
|
-
if (l.timer) {
|
|
1737
|
-
clearTimeout(l.timer);
|
|
1738
|
-
l.timer = null;
|
|
1739
|
-
}
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1742
|
-
if (this.intersectionObserver) {
|
|
1743
|
-
this.intersectionObserver.disconnect();
|
|
1744
|
-
this.intersectionObserver = null;
|
|
1745
|
-
}
|
|
1746
|
-
this.slotViewabilityRecords.clear();
|
|
1747
|
-
this.elementToSlotId.clear();
|
|
1748
|
-
for (const cleanup of this.videoDetachCleanups) {
|
|
1749
|
-
try {
|
|
1750
|
-
cleanup();
|
|
1751
|
-
} catch {
|
|
1752
|
-
}
|
|
1753
|
-
}
|
|
1754
|
-
this.videoDetachCleanups.clear();
|
|
1755
|
-
this.cachedWinningBids.clear();
|
|
1756
|
-
this.slotEmittedImpressionKeys.clear();
|
|
1757
|
-
this.slotLastAuctionIds.clear();
|
|
1758
|
-
this.auctionBidderOutcomes.clear();
|
|
1759
|
-
if (this.flushTimer) {
|
|
1760
|
-
clearInterval(this.flushTimer);
|
|
1761
|
-
this.flushTimer = null;
|
|
1762
|
-
}
|
|
1763
|
-
if (this.revenueFlushTimer) {
|
|
1764
|
-
clearTimeout(this.revenueFlushTimer);
|
|
1765
|
-
this.revenueFlushTimer = null;
|
|
1766
|
-
}
|
|
1767
|
-
if (this.persistedCleanupTimer) {
|
|
1768
|
-
clearTimeout(this.persistedCleanupTimer);
|
|
1769
|
-
this.persistedCleanupTimer = null;
|
|
1770
|
-
}
|
|
1771
|
-
if (typeof window !== "undefined") {
|
|
1772
|
-
window.removeEventListener("pagehide", this.boundFlushBeacon);
|
|
1773
|
-
if (typeof document !== "undefined") {
|
|
1774
|
-
document.removeEventListener("visibilitychange", this.boundVisibilityChange);
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
if (this.boundPbjsHandlers.length > 0) {
|
|
1778
|
-
const win = typeof window !== "undefined" ? window : {};
|
|
1779
|
-
const pbjs = win[this.config.pbjsGlobalName] || {};
|
|
1780
|
-
if (typeof pbjs.offEvent === "function") {
|
|
1781
|
-
for (const { event, handler } of this.boundPbjsHandlers) {
|
|
1782
|
-
pbjs.offEvent(event, handler);
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
this.boundPbjsHandlers = [];
|
|
1786
|
-
}
|
|
1787
|
-
this.flush();
|
|
1788
|
-
}
|
|
1789
|
-
handleVisibilityChange() {
|
|
1790
|
-
if (typeof document === "undefined") return;
|
|
1791
|
-
if (document.visibilityState === "hidden") {
|
|
1792
|
-
const now = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
1793
|
-
for (const record of this.slotViewabilityRecords.values()) {
|
|
1794
|
-
if (record.inView && record.lastEnteredViewAt !== null) {
|
|
1795
|
-
record.accumulatedTimeInViewMs += Math.max(0, now - record.lastEnteredViewAt);
|
|
1796
|
-
record.lastEnteredViewAt = null;
|
|
1797
|
-
this.checkThresholdListeners(record);
|
|
1798
|
-
}
|
|
1799
|
-
if (record.dwellTimer) {
|
|
1800
|
-
clearTimeout(record.dwellTimer);
|
|
1801
|
-
record.dwellTimer = null;
|
|
1802
|
-
record.dwellStartedAt = null;
|
|
1803
|
-
}
|
|
1804
|
-
for (const l of record.thresholdListeners) {
|
|
1805
|
-
if (l.timer) {
|
|
1806
|
-
clearTimeout(l.timer);
|
|
1807
|
-
l.timer = null;
|
|
1808
|
-
}
|
|
1809
|
-
}
|
|
1810
|
-
const durationMs = Math.round(record.accumulatedTimeInViewMs);
|
|
1811
|
-
if (durationMs > 0) {
|
|
1812
|
-
this.enqueue(TraceEventType.TIME_IN_VIEW, "timeInView", {
|
|
1813
|
-
auctionId: record.auctionId,
|
|
1814
|
-
transactionId: record.transactionId,
|
|
1815
|
-
adUnitCode: record.adUnitCode,
|
|
1816
|
-
viewableDurationMs: durationMs,
|
|
1817
|
-
bid: record.bidPayload,
|
|
1818
|
-
metadata: {
|
|
1819
|
-
...record.metadata,
|
|
1820
|
-
refresh_index: String(record.refreshIndex)
|
|
1821
|
-
}
|
|
1822
|
-
});
|
|
1823
|
-
}
|
|
1824
|
-
}
|
|
1825
|
-
this.flushBeacon();
|
|
1826
|
-
} else if (document.visibilityState === "visible") {
|
|
1827
|
-
const now = typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
1828
|
-
for (const record of this.slotViewabilityRecords.values()) {
|
|
1829
|
-
if (record.inView) {
|
|
1830
|
-
record.lastEnteredViewAt = now;
|
|
1831
|
-
if (!record.viewableFired && record.dwellTimer === null) {
|
|
1832
|
-
const dwellTarget = record.mediaType === "video" ? 2e3 : 1e3;
|
|
1833
|
-
record.dwellStartedAt = now;
|
|
1834
|
-
record.dwellTimer = setTimeout(() => {
|
|
1835
|
-
this.handleDwellComplete(record);
|
|
1836
|
-
}, dwellTarget);
|
|
1837
|
-
}
|
|
1838
|
-
this.scheduleThresholdTimers(record);
|
|
1839
|
-
}
|
|
1840
|
-
}
|
|
1549
|
+
}
|
|
1841
1550
|
}
|
|
1842
1551
|
}
|
|
1843
1552
|
handleIntersection(entries) {
|
|
@@ -1913,6 +1622,105 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1913
1622
|
}
|
|
1914
1623
|
});
|
|
1915
1624
|
}
|
|
1625
|
+
this.notifyViewable(record);
|
|
1626
|
+
}
|
|
1627
|
+
notifyViewable(record) {
|
|
1628
|
+
if (!record.viewableFired) return;
|
|
1629
|
+
for (const listener of record.viewableListeners) {
|
|
1630
|
+
if (listener.firedForIndex === record.refreshIndex) continue;
|
|
1631
|
+
listener.firedForIndex = record.refreshIndex;
|
|
1632
|
+
try {
|
|
1633
|
+
listener.callback(record.slotId);
|
|
1634
|
+
} catch (e) {
|
|
1635
|
+
this.log("ERROR", "Error in viewable listener callback", e);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
/**
|
|
1640
|
+
* Calls back once per render cycle when the slot has met the IAB viewable
|
|
1641
|
+
* standard this adapter measures (immediately if it already has). Works
|
|
1642
|
+
* before the slot is observed: the listener waits for observeSlot. Returns
|
|
1643
|
+
* an unsubscribe function.
|
|
1644
|
+
*/
|
|
1645
|
+
/**
|
|
1646
|
+
* Names the element to measure for a slot when the creative renders
|
|
1647
|
+
* somewhere other than the slot element (a sticky bar, an interstitial).
|
|
1648
|
+
* Applies to the current cycle at once and to every later render.
|
|
1649
|
+
*/
|
|
1650
|
+
setSlotElement(slotId, element) {
|
|
1651
|
+
this.slotElements.set(slotId, element);
|
|
1652
|
+
const record = this.slotViewabilityRecords.get(slotId);
|
|
1653
|
+
if (record && record.element !== element) {
|
|
1654
|
+
this.observeSlot(element, slotId, {
|
|
1655
|
+
refreshIndex: record.refreshIndex,
|
|
1656
|
+
emitRefreshEvent: false
|
|
1657
|
+
});
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
/**
|
|
1661
|
+
* Records a click on the slot's current creative, attributed to the cached
|
|
1662
|
+
* winning bid. Repeats within one second are one click. Returns whether a
|
|
1663
|
+
* CLICK was recorded.
|
|
1664
|
+
*/
|
|
1665
|
+
recordClick(slotId, options) {
|
|
1666
|
+
if (!this.isEnabled || !slotId) return false;
|
|
1667
|
+
const now = Date.now();
|
|
1668
|
+
if (now - (this.lastClickAt.get(slotId) || 0) < CLICK_DEDUP_MS) return false;
|
|
1669
|
+
this.lastClickAt.set(slotId, now);
|
|
1670
|
+
const cached = this.getCachedBid(slotId);
|
|
1671
|
+
this.enqueue(TraceEventType.CLICK, "click", {
|
|
1672
|
+
auctionId: cached?.auctionId || "",
|
|
1673
|
+
transactionId: cached?.transactionId || "",
|
|
1674
|
+
adUnitCode: slotId,
|
|
1675
|
+
bid: cached?.bidTrace,
|
|
1676
|
+
metadata: {
|
|
1677
|
+
...options?.metadata,
|
|
1678
|
+
refresh_index: String(this.getRefreshIndex(slotId))
|
|
1679
|
+
}
|
|
1680
|
+
});
|
|
1681
|
+
return true;
|
|
1682
|
+
}
|
|
1683
|
+
/**
|
|
1684
|
+
* Detects clicks into a cross-origin creative frame: a click inside an
|
|
1685
|
+
* iframe moves focus into it and blurs the window. Returns a detach
|
|
1686
|
+
* function; every tracker is detached on disable().
|
|
1687
|
+
*/
|
|
1688
|
+
attachClickTracker(element, slotId) {
|
|
1689
|
+
if (typeof window === "undefined") return () => {
|
|
1690
|
+
};
|
|
1691
|
+
const onBlur = () => {
|
|
1692
|
+
setTimeout(() => {
|
|
1693
|
+
const active = document.activeElement;
|
|
1694
|
+
if (active && (active === element || element.contains(active))) {
|
|
1695
|
+
this.recordClick(slotId, { metadata: { source: "blur" } });
|
|
1696
|
+
window.focus();
|
|
1697
|
+
}
|
|
1698
|
+
}, 0);
|
|
1699
|
+
};
|
|
1700
|
+
window.addEventListener("blur", onBlur);
|
|
1701
|
+
const cleanup = () => {
|
|
1702
|
+
window.removeEventListener("blur", onBlur);
|
|
1703
|
+
this.clickDetachCleanups.delete(cleanup);
|
|
1704
|
+
};
|
|
1705
|
+
this.clickDetachCleanups.add(cleanup);
|
|
1706
|
+
return cleanup;
|
|
1707
|
+
}
|
|
1708
|
+
onViewable(slotId, callback) {
|
|
1709
|
+
const listener = { callback, firedForIndex: -1 };
|
|
1710
|
+
const record = this.slotViewabilityRecords.get(slotId);
|
|
1711
|
+
if (record) {
|
|
1712
|
+
record.viewableListeners.add(listener);
|
|
1713
|
+
this.notifyViewable(record);
|
|
1714
|
+
} else {
|
|
1715
|
+
if (!this.pendingViewableListeners.has(slotId)) {
|
|
1716
|
+
this.pendingViewableListeners.set(slotId, /* @__PURE__ */ new Set());
|
|
1717
|
+
}
|
|
1718
|
+
this.pendingViewableListeners.get(slotId).add(listener);
|
|
1719
|
+
}
|
|
1720
|
+
return () => {
|
|
1721
|
+
this.slotViewabilityRecords.get(slotId)?.viewableListeners.delete(listener);
|
|
1722
|
+
this.pendingViewableListeners.get(slotId)?.delete(listener);
|
|
1723
|
+
};
|
|
1916
1724
|
}
|
|
1917
1725
|
scheduleThresholdTimers(record) {
|
|
1918
1726
|
if (!record.inView || typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
@@ -2102,6 +1910,8 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2102
1910
|
}
|
|
2103
1911
|
const listeners = this.pendingThresholdListeners.get(resolvedSlotId) || /* @__PURE__ */ new Set();
|
|
2104
1912
|
this.pendingThresholdListeners.delete(resolvedSlotId);
|
|
1913
|
+
const viewableListeners = this.pendingViewableListeners.get(resolvedSlotId) || /* @__PURE__ */ new Set();
|
|
1914
|
+
this.pendingViewableListeners.delete(resolvedSlotId);
|
|
2105
1915
|
const record = {
|
|
2106
1916
|
slotId: resolvedSlotId,
|
|
2107
1917
|
element: el,
|
|
@@ -2118,7 +1928,8 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2118
1928
|
viewableFired: false,
|
|
2119
1929
|
dwellTimer: null,
|
|
2120
1930
|
dwellStartedAt: null,
|
|
2121
|
-
thresholdListeners: listeners
|
|
1931
|
+
thresholdListeners: listeners,
|
|
1932
|
+
viewableListeners
|
|
2122
1933
|
};
|
|
2123
1934
|
this.slotViewabilityRecords.set(resolvedSlotId, record);
|
|
2124
1935
|
if (el) {
|
|
@@ -2143,6 +1954,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2143
1954
|
}
|
|
2144
1955
|
}
|
|
2145
1956
|
record.thresholdListeners = /* @__PURE__ */ new Set();
|
|
1957
|
+
record.viewableListeners = /* @__PURE__ */ new Set();
|
|
2146
1958
|
if (record.element && this.intersectionObserver) {
|
|
2147
1959
|
this.intersectionObserver.unobserve(record.element);
|
|
2148
1960
|
this.elementToSlotId.delete(record.element);
|
|
@@ -2157,6 +1969,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2157
1969
|
this.slotRefreshIndices.delete(slotId);
|
|
2158
1970
|
this.slotLastAuctionIds.delete(slotId);
|
|
2159
1971
|
this.pendingThresholdListeners.delete(slotId);
|
|
1972
|
+
this.pendingViewableListeners.delete(slotId);
|
|
2160
1973
|
const prefix = `${escapeKeyPart(slotId)}:`;
|
|
2161
1974
|
for (const key of Array.from(this.slotEmittedImpressionKeys)) {
|
|
2162
1975
|
if (key === slotId || key.startsWith(prefix)) {
|
|
@@ -2203,72 +2016,6 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2203
2016
|
this.cachedWinningBids.delete(oldest.value);
|
|
2204
2017
|
}
|
|
2205
2018
|
}
|
|
2206
|
-
/**
|
|
2207
|
-
* Searches cached winning bids for an entry matching the given filters (creativeId,
|
|
2208
|
-
* adId, or mediaType), falling back to the most recent winning video bid.
|
|
2209
|
-
*/
|
|
2210
|
-
findCachedWinningBid(filter) {
|
|
2211
|
-
const now = Date.now();
|
|
2212
|
-
const isConsumed = (entry) => {
|
|
2213
|
-
return this.hasImpressionEmitted(
|
|
2214
|
-
entry.adUnitCode,
|
|
2215
|
-
void 0,
|
|
2216
|
-
entry.auctionId,
|
|
2217
|
-
entry.bidTrace.creativeId
|
|
2218
|
-
);
|
|
2219
|
-
};
|
|
2220
|
-
if (filter?.creativeId || filter?.adId) {
|
|
2221
|
-
for (const entry of Array.from(this.cachedWinningBids.values())) {
|
|
2222
|
-
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
2223
|
-
if (filter.creativeId && (entry.bidTrace.creativeId === filter.creativeId || entry.rawBid?.creativeId === filter.creativeId) || filter.adId && (entry.rawBid?.adId === filter.adId || entry.adUnitCode === filter.adId)) {
|
|
2224
|
-
return entry;
|
|
2225
|
-
}
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
let bestUnconsumedMatch;
|
|
2229
|
-
if (filter?.mediaType) {
|
|
2230
|
-
for (const entry of Array.from(this.cachedWinningBids.values())) {
|
|
2231
|
-
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
2232
|
-
const isEntryVideo = entry.bidTrace.mediaType === filter.mediaType || entry.rawBid?.mediaType === filter.mediaType || filter.mediaType === "video" && entry.rawBid?.mediaTypes?.video !== void 0;
|
|
2233
|
-
if (isEntryVideo) {
|
|
2234
|
-
if (!isConsumed(entry)) {
|
|
2235
|
-
if (!bestUnconsumedMatch || entry.timestamp < bestUnconsumedMatch.timestamp) {
|
|
2236
|
-
bestUnconsumedMatch = entry;
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2240
|
-
}
|
|
2241
|
-
if (bestUnconsumedMatch) return bestUnconsumedMatch;
|
|
2242
|
-
let bestFallbackMatch;
|
|
2243
|
-
for (const entry of Array.from(this.cachedWinningBids.values())) {
|
|
2244
|
-
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
2245
|
-
const isEntryVideo = entry.bidTrace.mediaType === filter.mediaType || entry.rawBid?.mediaType === filter.mediaType || filter.mediaType === "video" && entry.rawBid?.mediaTypes?.video !== void 0;
|
|
2246
|
-
if (isEntryVideo) {
|
|
2247
|
-
if (!bestFallbackMatch || entry.timestamp > bestFallbackMatch.timestamp) {
|
|
2248
|
-
bestFallbackMatch = entry;
|
|
2249
|
-
}
|
|
2250
|
-
}
|
|
2251
|
-
}
|
|
2252
|
-
if (bestFallbackMatch) return bestFallbackMatch;
|
|
2253
|
-
}
|
|
2254
|
-
for (const entry of Array.from(this.cachedWinningBids.values())) {
|
|
2255
|
-
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
2256
|
-
if (!isConsumed(entry)) {
|
|
2257
|
-
if (!bestUnconsumedMatch || entry.timestamp > bestUnconsumedMatch.timestamp) {
|
|
2258
|
-
bestUnconsumedMatch = entry;
|
|
2259
|
-
}
|
|
2260
|
-
}
|
|
2261
|
-
}
|
|
2262
|
-
if (bestUnconsumedMatch) return bestUnconsumedMatch;
|
|
2263
|
-
let bestRecent;
|
|
2264
|
-
for (const entry of Array.from(this.cachedWinningBids.values())) {
|
|
2265
|
-
if (now - entry.timestamp > CACHED_BID_TTL_MS) continue;
|
|
2266
|
-
if (!bestRecent || entry.timestamp > bestRecent.timestamp) {
|
|
2267
|
-
bestRecent = entry;
|
|
2268
|
-
}
|
|
2269
|
-
}
|
|
2270
|
-
return bestRecent;
|
|
2271
|
-
}
|
|
2272
2019
|
/** Records a dedup key, evicting oldest-first at the cap. */
|
|
2273
2020
|
addImpressionKey(key) {
|
|
2274
2021
|
if (this.slotEmittedImpressionKeys.has(key)) return;
|
|
@@ -2416,399 +2163,6 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2416
2163
|
}
|
|
2417
2164
|
return true;
|
|
2418
2165
|
}
|
|
2419
|
-
/**
|
|
2420
|
-
* Bridges Google IMA SDK AdsManager events to Bidkernel analytics.
|
|
2421
|
-
* Defers IMPRESSION emission until AdEvent.STARTED (or IMPRESSION),
|
|
2422
|
-
* tracks milestones (FIRST_QUARTILE, MIDPOINT, THIRD_QUARTILE, COMPLETE),
|
|
2423
|
-
* and handles AD_ERROR.
|
|
2424
|
-
*/
|
|
2425
|
-
attachImaAdsManager(adsManager, options) {
|
|
2426
|
-
if (!adsManager || typeof adsManager.addEventListener !== "function") {
|
|
2427
|
-
this.log("WARN", "Invalid AdsManager passed to attachImaAdsManager");
|
|
2428
|
-
return () => {
|
|
2429
|
-
};
|
|
2430
|
-
}
|
|
2431
|
-
if (!adsManager.__bidkernelAttachedInstances) {
|
|
2432
|
-
try {
|
|
2433
|
-
Object.defineProperty(adsManager, "__bidkernelAttachedInstances", {
|
|
2434
|
-
value: /* @__PURE__ */ new Set(),
|
|
2435
|
-
configurable: true,
|
|
2436
|
-
writable: true
|
|
2437
|
-
});
|
|
2438
|
-
} catch {
|
|
2439
|
-
adsManager.__bidkernelAttachedInstances = /* @__PURE__ */ new Set();
|
|
2440
|
-
}
|
|
2441
|
-
}
|
|
2442
|
-
if (adsManager.__bidkernelAttachedInstances.has(this)) {
|
|
2443
|
-
return () => {
|
|
2444
|
-
};
|
|
2445
|
-
}
|
|
2446
|
-
adsManager.__bidkernelAttachedInstances.add(this);
|
|
2447
|
-
const slotId = options?.slotId || options?.adUnitCode || "video";
|
|
2448
|
-
const adUnitCode = options?.adUnitCode || slotId;
|
|
2449
|
-
const auctionId = options?.auctionId || "";
|
|
2450
|
-
const transactionId = options?.transactionId || "";
|
|
2451
|
-
const getWinningBid = (adData) => {
|
|
2452
|
-
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);
|
|
2453
|
-
if (!cached) {
|
|
2454
|
-
cached = this.findCachedWinningBid({
|
|
2455
|
-
creativeId: adData?.creativeId,
|
|
2456
|
-
adId: adData?.adId,
|
|
2457
|
-
mediaType: "video"
|
|
2458
|
-
});
|
|
2459
|
-
}
|
|
2460
|
-
return cached;
|
|
2461
|
-
};
|
|
2462
|
-
let adStarted = false;
|
|
2463
|
-
const onAdStartedOrImpression = (event) => {
|
|
2464
|
-
adStarted = true;
|
|
2465
|
-
this.log("DEBUG", "IMA AdEvent.STARTED / IMPRESSION received", event);
|
|
2466
|
-
const ad = typeof event?.getAd === "function" ? event.getAd() : event?.ad;
|
|
2467
|
-
const adData = {};
|
|
2468
|
-
if (ad) {
|
|
2469
|
-
adData.creativeId = (typeof ad.getCreativeId === "function" ? ad.getCreativeId() : ad.creativeId) || "";
|
|
2470
|
-
adData.adId = (typeof ad.getAdId === "function" ? ad.getAdId() : ad.id) || "";
|
|
2471
|
-
adData.title = (typeof ad.getTitle === "function" ? ad.getTitle() : ad.title) || "";
|
|
2472
|
-
adData.duration = typeof ad.getDuration === "function" ? ad.getDuration() : ad.duration;
|
|
2473
|
-
adData.advertiserName = typeof ad.getAdvertiserName === "function" ? ad.getAdvertiserName() : "";
|
|
2474
|
-
}
|
|
2475
|
-
const cached = getWinningBid(adData);
|
|
2476
|
-
const resolvedSlotId = options?.slotId && options.slotId !== "video" ? options.slotId : options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || options?.slotId || options?.adUnitCode || "video";
|
|
2477
|
-
const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || resolvedSlotId;
|
|
2478
|
-
const resolvedAuctionId = auctionId || cached?.auctionId || "";
|
|
2479
|
-
const resolvedTransactionId = transactionId || cached?.transactionId || "";
|
|
2480
|
-
const mergedBid = {
|
|
2481
|
-
...cached?.rawBid || options?.bid,
|
|
2482
|
-
mediaType: "video",
|
|
2483
|
-
creativeId: adData.creativeId || cached?.bidTrace?.creativeId || options?.bid?.creativeId || ""
|
|
2484
|
-
};
|
|
2485
|
-
const emitted = this.recordImpression(resolvedSlotId, {
|
|
2486
|
-
adUnitCode: resolvedAdUnitCode,
|
|
2487
|
-
auctionId: resolvedAuctionId,
|
|
2488
|
-
transactionId: resolvedTransactionId,
|
|
2489
|
-
mediaType: "video",
|
|
2490
|
-
bid: mergedBid,
|
|
2491
|
-
metadata: {
|
|
2492
|
-
...options?.metadata,
|
|
2493
|
-
media_type: "video",
|
|
2494
|
-
...adData.title ? { ad_title: adData.title } : {},
|
|
2495
|
-
...Number.isFinite(adData.duration) ? { ad_duration_s: String(adData.duration) } : {}
|
|
2496
|
-
}
|
|
2497
|
-
});
|
|
2498
|
-
if (emitted && options?.onImpression) {
|
|
2499
|
-
try {
|
|
2500
|
-
options.onImpression(resolvedSlotId, { ad, bid: mergedBid });
|
|
2501
|
-
} catch (e) {
|
|
2502
|
-
this.log("ERROR", "Error in onImpression callback", e);
|
|
2503
|
-
}
|
|
2504
|
-
}
|
|
2505
|
-
};
|
|
2506
|
-
const onMilestone = (milestone) => {
|
|
2507
|
-
this.log("DEBUG", `IMA AdEvent milestone: ${milestone}`);
|
|
2508
|
-
if (options?.onMilestone) {
|
|
2509
|
-
try {
|
|
2510
|
-
options.onMilestone(milestone, slotId);
|
|
2511
|
-
} catch (e) {
|
|
2512
|
-
this.log("ERROR", "Error in onMilestone callback", e);
|
|
2513
|
-
}
|
|
2514
|
-
}
|
|
2515
|
-
};
|
|
2516
|
-
const onAdClick = (event) => {
|
|
2517
|
-
this.log("DEBUG", "IMA AdEvent.CLICK received", event);
|
|
2518
|
-
const cached = getWinningBid();
|
|
2519
|
-
const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || slotId;
|
|
2520
|
-
this.enqueue(TraceEventType.CLICK, "click", {
|
|
2521
|
-
auctionId: auctionId || cached?.auctionId,
|
|
2522
|
-
transactionId: transactionId || cached?.transactionId,
|
|
2523
|
-
adUnitCode: resolvedAdUnitCode,
|
|
2524
|
-
bid: cached?.bidTrace || options?.bid,
|
|
2525
|
-
metadata: {
|
|
2526
|
-
...options?.metadata,
|
|
2527
|
-
media_type: "video"
|
|
2528
|
-
}
|
|
2529
|
-
});
|
|
2530
|
-
};
|
|
2531
|
-
const onAdError = (event) => {
|
|
2532
|
-
this.log("WARN", "IMA AdErrorEvent received", event);
|
|
2533
|
-
const err = typeof event?.getError === "function" ? event.getError() : event?.error || event;
|
|
2534
|
-
const msg = (err && typeof err.getMessage === "function" ? err.getMessage() : err?.message) || String(err || "IMA ad error");
|
|
2535
|
-
const code = (err && typeof err.getErrorCode === "function" ? err.getErrorCode() : err?.code) || "";
|
|
2536
|
-
const cached = getWinningBid();
|
|
2537
|
-
const resolvedAdUnitCode = options?.adUnitCode && options.adUnitCode !== "video" ? options.adUnitCode : cached?.adUnitCode || slotId;
|
|
2538
|
-
this.enqueue(TraceEventType.AD_RENDER_FAILED, "adRenderFailed", {
|
|
2539
|
-
auctionId: auctionId || cached?.auctionId,
|
|
2540
|
-
transactionId: transactionId || cached?.transactionId,
|
|
2541
|
-
adUnitCode: resolvedAdUnitCode,
|
|
2542
|
-
bid: cached?.bidTrace || options?.bid,
|
|
2543
|
-
metadata: {
|
|
2544
|
-
...options?.metadata,
|
|
2545
|
-
reason: adStarted ? "ima_playback_error" : "ima_ad_error",
|
|
2546
|
-
playback_phase: adStarted ? "post_start" : "pre_start",
|
|
2547
|
-
message: msg,
|
|
2548
|
-
...code ? { ima_error_code: String(code) } : {}
|
|
2549
|
-
},
|
|
2550
|
-
error: typeof err === "object" ? err : new Error(msg)
|
|
2551
|
-
});
|
|
2552
|
-
if (options?.onError) {
|
|
2553
|
-
try {
|
|
2554
|
-
options.onError(err);
|
|
2555
|
-
} catch (e) {
|
|
2556
|
-
this.log("ERROR", "Error in onError callback", e);
|
|
2557
|
-
}
|
|
2558
|
-
}
|
|
2559
|
-
};
|
|
2560
|
-
const googleIma = typeof window !== "undefined" ? window.google?.ima : void 0;
|
|
2561
|
-
const adEventType = googleIma?.AdEvent?.Type || {};
|
|
2562
|
-
const adErrorEventType = googleIma?.AdErrorEvent?.Type || {};
|
|
2563
|
-
const rawListeners = [
|
|
2564
|
-
{ type: adEventType.STARTED || "started", handler: onAdStartedOrImpression },
|
|
2565
|
-
{ type: adEventType.IMPRESSION || "impression", handler: onAdStartedOrImpression },
|
|
2566
|
-
{
|
|
2567
|
-
type: adEventType.FIRST_QUARTILE || "firstQuartile",
|
|
2568
|
-
handler: () => onMilestone("firstQuartile")
|
|
2569
|
-
},
|
|
2570
|
-
{ type: adEventType.MIDPOINT || "midpoint", handler: () => onMilestone("midpoint") },
|
|
2571
|
-
{
|
|
2572
|
-
type: adEventType.THIRD_QUARTILE || "thirdQuartile",
|
|
2573
|
-
handler: () => onMilestone("thirdQuartile")
|
|
2574
|
-
},
|
|
2575
|
-
{ type: adEventType.COMPLETE || "complete", handler: () => onMilestone("complete") },
|
|
2576
|
-
{ type: adEventType.CLICK || "click", handler: onAdClick },
|
|
2577
|
-
{ type: adErrorEventType.AD_ERROR || "adError", handler: onAdError }
|
|
2578
|
-
];
|
|
2579
|
-
const seenListenerTypes = /* @__PURE__ */ new Set();
|
|
2580
|
-
const listeners = [];
|
|
2581
|
-
for (const l of rawListeners) {
|
|
2582
|
-
if (!seenListenerTypes.has(l.type)) {
|
|
2583
|
-
seenListenerTypes.add(l.type);
|
|
2584
|
-
listeners.push(l);
|
|
2585
|
-
}
|
|
2586
|
-
}
|
|
2587
|
-
for (const { type, handler } of listeners) {
|
|
2588
|
-
try {
|
|
2589
|
-
adsManager.addEventListener(type, handler);
|
|
2590
|
-
} catch {
|
|
2591
|
-
}
|
|
2592
|
-
}
|
|
2593
|
-
const cleanup = () => {
|
|
2594
|
-
adsManager.__bidkernelAttachedInstances?.delete(this);
|
|
2595
|
-
for (const { type, handler } of listeners) {
|
|
2596
|
-
try {
|
|
2597
|
-
adsManager.removeEventListener(type, handler);
|
|
2598
|
-
} catch {
|
|
2599
|
-
}
|
|
2600
|
-
}
|
|
2601
|
-
this.videoDetachCleanups.delete(cleanup);
|
|
2602
|
-
};
|
|
2603
|
-
this.videoDetachCleanups.add(cleanup);
|
|
2604
|
-
return cleanup;
|
|
2605
|
-
}
|
|
2606
|
-
/**
|
|
2607
|
-
* Attaches render hooks and viewability tracking to a video player.
|
|
2608
|
-
* Supports HTML5 <video> elements, container elements, or Google IMA AdsManager.
|
|
2609
|
-
*/
|
|
2610
|
-
attachVideoPlayer(target, options) {
|
|
2611
|
-
if (!target) {
|
|
2612
|
-
this.log("WARN", "attachVideoPlayer called with null/undefined target");
|
|
2613
|
-
return () => {
|
|
2614
|
-
};
|
|
2615
|
-
}
|
|
2616
|
-
let el = null;
|
|
2617
|
-
if (typeof target === "string") {
|
|
2618
|
-
if (typeof document !== "undefined") {
|
|
2619
|
-
try {
|
|
2620
|
-
el = document.querySelector(target) || document.getElementById(target);
|
|
2621
|
-
} catch {
|
|
2622
|
-
el = document.getElementById(target);
|
|
2623
|
-
}
|
|
2624
|
-
}
|
|
2625
|
-
} else if (typeof HTMLElement !== "undefined" && target instanceof HTMLElement) {
|
|
2626
|
-
el = target;
|
|
2627
|
-
} else if (target && typeof target === "object" && target.nodeType === 1) {
|
|
2628
|
-
el = target;
|
|
2629
|
-
} else if (
|
|
2630
|
-
// Check if target is an IMA AdsManager
|
|
2631
|
-
typeof target.addEventListener === "function" && (typeof target.init === "function" || typeof target.start === "function" || typeof target.getCuePoints === "function" || typeof target.getVolume === "function" || target.__imaAdsManager)
|
|
2632
|
-
) {
|
|
2633
|
-
return this.attachImaAdsManager(target, options);
|
|
2634
|
-
} else if (target && typeof target.addEventListener === "function") {
|
|
2635
|
-
el = target;
|
|
2636
|
-
}
|
|
2637
|
-
const slotId = options?.slotId || options?.adUnitCode || (el ? el.id : "") || "video";
|
|
2638
|
-
const adUnitCode = options?.adUnitCode || slotId;
|
|
2639
|
-
const auctionId = options?.auctionId || "";
|
|
2640
|
-
const transactionId = options?.transactionId || "";
|
|
2641
|
-
const getWinningBid = () => {
|
|
2642
|
-
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);
|
|
2643
|
-
};
|
|
2644
|
-
let videoEl = null;
|
|
2645
|
-
if (el) {
|
|
2646
|
-
if (el.tagName && el.tagName.toLowerCase() === "video") {
|
|
2647
|
-
videoEl = el;
|
|
2648
|
-
} else {
|
|
2649
|
-
videoEl = el.querySelector("video");
|
|
2650
|
-
}
|
|
2651
|
-
}
|
|
2652
|
-
const quartilesFired = {
|
|
2653
|
-
q1: false,
|
|
2654
|
-
q2: false,
|
|
2655
|
-
q3: false,
|
|
2656
|
-
q4: false
|
|
2657
|
-
};
|
|
2658
|
-
const triggerPlaybackImpression = (activeVideo) => {
|
|
2659
|
-
const vEl = activeVideo || videoEl || (el?.querySelector ? el.querySelector("video") : null);
|
|
2660
|
-
const cached = getWinningBid();
|
|
2661
|
-
const mergedBid = {
|
|
2662
|
-
...cached?.rawBid || options?.bid,
|
|
2663
|
-
mediaType: "video"
|
|
2664
|
-
};
|
|
2665
|
-
const emitted = this.recordImpression(slotId, {
|
|
2666
|
-
adUnitCode,
|
|
2667
|
-
auctionId: auctionId || cached?.auctionId,
|
|
2668
|
-
transactionId: transactionId || cached?.transactionId,
|
|
2669
|
-
mediaType: "video",
|
|
2670
|
-
bid: mergedBid,
|
|
2671
|
-
metadata: {
|
|
2672
|
-
...options?.metadata,
|
|
2673
|
-
media_type: "video",
|
|
2674
|
-
...vEl && Number.isFinite(vEl.duration) ? { video_duration_s: String(Math.round(vEl.duration)) } : {}
|
|
2675
|
-
}
|
|
2676
|
-
});
|
|
2677
|
-
if (emitted && options?.onImpression) {
|
|
2678
|
-
try {
|
|
2679
|
-
options.onImpression(slotId, { element: vEl || el, bid: mergedBid });
|
|
2680
|
-
} catch (e) {
|
|
2681
|
-
this.log("ERROR", "Error in onImpression callback", e);
|
|
2682
|
-
}
|
|
2683
|
-
}
|
|
2684
|
-
};
|
|
2685
|
-
const handlePlaying = (e) => {
|
|
2686
|
-
this.log("DEBUG", "HTML5 video playing event received");
|
|
2687
|
-
const targetVideo = e?.target instanceof HTMLVideoElement ? e.target : videoEl;
|
|
2688
|
-
triggerPlaybackImpression(targetVideo);
|
|
2689
|
-
};
|
|
2690
|
-
const handleTimeUpdate = (e) => {
|
|
2691
|
-
const targetVideo = e?.target instanceof HTMLVideoElement ? e.target : videoEl;
|
|
2692
|
-
if (targetVideo && targetVideo.currentTime > 0) {
|
|
2693
|
-
triggerPlaybackImpression(targetVideo);
|
|
2694
|
-
if (Number.isFinite(targetVideo.duration) && targetVideo.duration > 0) {
|
|
2695
|
-
const progress = targetVideo.currentTime / targetVideo.duration;
|
|
2696
|
-
if (progress >= 0.25 && !quartilesFired.q1) {
|
|
2697
|
-
quartilesFired.q1 = true;
|
|
2698
|
-
options?.onMilestone?.("firstQuartile", slotId);
|
|
2699
|
-
}
|
|
2700
|
-
if (progress >= 0.5 && !quartilesFired.q2) {
|
|
2701
|
-
quartilesFired.q2 = true;
|
|
2702
|
-
options?.onMilestone?.("midpoint", slotId);
|
|
2703
|
-
}
|
|
2704
|
-
if (progress >= 0.75 && !quartilesFired.q3) {
|
|
2705
|
-
quartilesFired.q3 = true;
|
|
2706
|
-
options?.onMilestone?.("thirdQuartile", slotId);
|
|
2707
|
-
}
|
|
2708
|
-
}
|
|
2709
|
-
}
|
|
2710
|
-
};
|
|
2711
|
-
const handleEnded = () => {
|
|
2712
|
-
if (!quartilesFired.q4) {
|
|
2713
|
-
quartilesFired.q4 = true;
|
|
2714
|
-
options?.onMilestone?.("complete", slotId);
|
|
2715
|
-
}
|
|
2716
|
-
};
|
|
2717
|
-
const handleError = (e) => {
|
|
2718
|
-
const targetVideo = e?.target instanceof HTMLVideoElement ? e.target : videoEl;
|
|
2719
|
-
const err = targetVideo?.error || videoEl?.error;
|
|
2720
|
-
const cached = getWinningBid();
|
|
2721
|
-
const msg = err ? `HTML5 video error code ${err.code}: ${err.message}` : "Video playback error";
|
|
2722
|
-
this.log("WARN", msg);
|
|
2723
|
-
this.enqueue(TraceEventType.AD_RENDER_FAILED, "adRenderFailed", {
|
|
2724
|
-
auctionId: auctionId || cached?.auctionId,
|
|
2725
|
-
transactionId: transactionId || cached?.transactionId,
|
|
2726
|
-
adUnitCode,
|
|
2727
|
-
bid: cached?.bidTrace || options?.bid,
|
|
2728
|
-
metadata: {
|
|
2729
|
-
...options?.metadata,
|
|
2730
|
-
reason: "html5_video_error",
|
|
2731
|
-
message: msg,
|
|
2732
|
-
...err?.code ? { video_error_code: String(err.code) } : {}
|
|
2733
|
-
},
|
|
2734
|
-
error: err ? new Error(msg) : void 0
|
|
2735
|
-
});
|
|
2736
|
-
if (options?.onError) {
|
|
2737
|
-
try {
|
|
2738
|
-
options.onError(err);
|
|
2739
|
-
} catch (e2) {
|
|
2740
|
-
this.log("ERROR", "Error in onError callback", e2);
|
|
2741
|
-
}
|
|
2742
|
-
}
|
|
2743
|
-
};
|
|
2744
|
-
const attachListenersToVideo = (targetVideo) => {
|
|
2745
|
-
targetVideo.addEventListener("playing", handlePlaying);
|
|
2746
|
-
targetVideo.addEventListener("play", handlePlaying);
|
|
2747
|
-
targetVideo.addEventListener("timeupdate", handleTimeUpdate);
|
|
2748
|
-
targetVideo.addEventListener("ended", handleEnded);
|
|
2749
|
-
targetVideo.addEventListener("error", handleError);
|
|
2750
|
-
};
|
|
2751
|
-
const removeListenersFromVideo = (targetVideo) => {
|
|
2752
|
-
targetVideo.removeEventListener("playing", handlePlaying);
|
|
2753
|
-
targetVideo.removeEventListener("play", handlePlaying);
|
|
2754
|
-
targetVideo.removeEventListener("timeupdate", handleTimeUpdate);
|
|
2755
|
-
targetVideo.removeEventListener("ended", handleEnded);
|
|
2756
|
-
targetVideo.removeEventListener("error", handleError);
|
|
2757
|
-
};
|
|
2758
|
-
let mutationObserver = null;
|
|
2759
|
-
if (el && el !== videoEl) {
|
|
2760
|
-
el.addEventListener("playing", handlePlaying, true);
|
|
2761
|
-
el.addEventListener("play", handlePlaying, true);
|
|
2762
|
-
el.addEventListener("timeupdate", handleTimeUpdate, true);
|
|
2763
|
-
el.addEventListener("ended", handleEnded, true);
|
|
2764
|
-
el.addEventListener("error", handleError, true);
|
|
2765
|
-
}
|
|
2766
|
-
if (videoEl) {
|
|
2767
|
-
attachListenersToVideo(videoEl);
|
|
2768
|
-
}
|
|
2769
|
-
if (el && el.tagName?.toLowerCase() !== "video" && typeof MutationObserver !== "undefined") {
|
|
2770
|
-
mutationObserver = new MutationObserver(() => {
|
|
2771
|
-
const found = el.querySelector("video");
|
|
2772
|
-
if (found && found !== videoEl) {
|
|
2773
|
-
if (videoEl) removeListenersFromVideo(videoEl);
|
|
2774
|
-
videoEl = found;
|
|
2775
|
-
if (videoEl) attachListenersToVideo(videoEl);
|
|
2776
|
-
}
|
|
2777
|
-
});
|
|
2778
|
-
mutationObserver.observe(el, { childList: true, subtree: true });
|
|
2779
|
-
}
|
|
2780
|
-
const cachedInitial = getWinningBid();
|
|
2781
|
-
if (options?.trackViewability !== false && el) {
|
|
2782
|
-
this.observeSlot(el, slotId, {
|
|
2783
|
-
adUnitCode,
|
|
2784
|
-
auctionId: auctionId || cachedInitial?.auctionId,
|
|
2785
|
-
transactionId: transactionId || cachedInitial?.transactionId,
|
|
2786
|
-
mediaType: "video",
|
|
2787
|
-
bid: cachedInitial?.bidTrace || options?.bid,
|
|
2788
|
-
metadata: options?.metadata,
|
|
2789
|
-
emitRefreshEvent: false
|
|
2790
|
-
});
|
|
2791
|
-
}
|
|
2792
|
-
const cleanup = () => {
|
|
2793
|
-
if (mutationObserver) {
|
|
2794
|
-
mutationObserver.disconnect();
|
|
2795
|
-
mutationObserver = null;
|
|
2796
|
-
}
|
|
2797
|
-
if (el && el !== videoEl) {
|
|
2798
|
-
el.removeEventListener("playing", handlePlaying, true);
|
|
2799
|
-
el.removeEventListener("play", handlePlaying, true);
|
|
2800
|
-
el.removeEventListener("timeupdate", handleTimeUpdate, true);
|
|
2801
|
-
el.removeEventListener("ended", handleEnded, true);
|
|
2802
|
-
el.removeEventListener("error", handleError, true);
|
|
2803
|
-
}
|
|
2804
|
-
if (videoEl) {
|
|
2805
|
-
removeListenersFromVideo(videoEl);
|
|
2806
|
-
}
|
|
2807
|
-
this.videoDetachCleanups.delete(cleanup);
|
|
2808
|
-
};
|
|
2809
|
-
this.videoDetachCleanups.add(cleanup);
|
|
2810
|
-
return cleanup;
|
|
2811
|
-
}
|
|
2812
2166
|
onTimeInViewThreshold(slotId, thresholdMs, callback) {
|
|
2813
2167
|
const listener = {
|
|
2814
2168
|
thresholdMs,
|
|
@@ -3227,9 +2581,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
3227
2581
|
);
|
|
3228
2582
|
}
|
|
3229
2583
|
if (this.config.viewabilityEnabled && typeof document !== "undefined") {
|
|
3230
|
-
let el = null;
|
|
2584
|
+
let el = adUnitCode && this.slotElements.get(adUnitCode) || null;
|
|
3231
2585
|
const targetId = adUnitCode || data.adId || bid.adId;
|
|
3232
|
-
if (targetId) {
|
|
2586
|
+
if (!el && targetId) {
|
|
3233
2587
|
el = document.getElementById(targetId);
|
|
3234
2588
|
}
|
|
3235
2589
|
if (!el && typeof window !== "undefined" && window.googletag?.pubads) {
|
|
@@ -3400,313 +2754,1034 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
3400
2754
|
};
|
|
3401
2755
|
}
|
|
3402
2756
|
}
|
|
3403
|
-
sendFetch(payload, useKeepalive = false, onDelivered, isReplay = false) {
|
|
3404
|
-
const fetchOpts = {
|
|
3405
|
-
method: "POST",
|
|
3406
|
-
// Safelisted content type: no CORS preflight (see SAFE_CONTENT_TYPE).
|
|
3407
|
-
headers: { "Content-Type": SAFE_CONTENT_TYPE },
|
|
3408
|
-
body: payload.encoded
|
|
3409
|
-
};
|
|
3410
|
-
if (useKeepalive) {
|
|
3411
|
-
fetchOpts.keepalive = true;
|
|
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 {
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
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);
|
|
2803
|
+
} else {
|
|
2804
|
+
this.log(
|
|
2805
|
+
"WARN",
|
|
2806
|
+
`Dropping ${events.length} events after ${this.consecutiveSendFailures} consecutive send failures`
|
|
2807
|
+
);
|
|
2808
|
+
}
|
|
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") {
|
|
2824
|
+
try {
|
|
2825
|
+
const blob = new Blob([payload.encoded], { type: SAFE_CONTENT_TYPE });
|
|
2826
|
+
sent = navigator.sendBeacon(payload.url, blob);
|
|
2827
|
+
} catch {
|
|
2828
|
+
sent = false;
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
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) {
|
|
2911
|
+
try {
|
|
2912
|
+
localStorage.removeItem(key);
|
|
2913
|
+
} catch {
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
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
|
+
);
|
|
2945
|
+
}
|
|
2946
|
+
} catch {
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
log(level, msg, ...args) {
|
|
2950
|
+
this.logger.log(level, msg, ...args);
|
|
2951
|
+
}
|
|
2952
|
+
};
|
|
2953
|
+
var defaultLogger = createLogger({
|
|
2954
|
+
prefix: "[bidkernel]",
|
|
2955
|
+
logLevel: "INFO"
|
|
2956
|
+
});
|
|
2957
|
+
function extractAnalyticsOptions(config, allowProviderless = true) {
|
|
2958
|
+
if (!config) return null;
|
|
2959
|
+
if (Array.isArray(config)) {
|
|
2960
|
+
const matched = config.find((item) => item?.provider === "bidkernel") ?? (allowProviderless ? config.find(
|
|
2961
|
+
(item) => item && typeof item === "object" && item.provider === void 0
|
|
2962
|
+
) : void 0);
|
|
2963
|
+
return matched ? extractAnalyticsOptions(matched, allowProviderless) : null;
|
|
2964
|
+
}
|
|
2965
|
+
if (typeof config === "object") {
|
|
2966
|
+
if (config.options && typeof config.options === "object") {
|
|
2967
|
+
return config.options;
|
|
2968
|
+
}
|
|
2969
|
+
if (config.config && typeof config.config === "object") {
|
|
2970
|
+
return config.config;
|
|
2971
|
+
}
|
|
2972
|
+
if (config.propertyId || config.endpoint) {
|
|
2973
|
+
return config;
|
|
2974
|
+
}
|
|
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;
|
|
2989
|
+
}
|
|
2990
|
+
if (bpa.config) {
|
|
2991
|
+
const extracted = extractAnalyticsOptions(bpa.config);
|
|
2992
|
+
if (extracted) return extracted;
|
|
2993
|
+
}
|
|
2994
|
+
if (bpa.propertyId) {
|
|
2995
|
+
return extractAnalyticsOptions(bpa);
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
return null;
|
|
2999
|
+
}
|
|
3000
|
+
function registerPrebidAnalytics(pbjsGlobalName = "pbjs", defaults) {
|
|
3001
|
+
const win = typeof window !== "undefined" ? window : {};
|
|
3002
|
+
win[pbjsGlobalName] = win[pbjsGlobalName] || {};
|
|
3003
|
+
const pbjs = win[pbjsGlobalName];
|
|
3004
|
+
const enableInstance = (options) => {
|
|
3005
|
+
const previous = win._bidkernelPrebidAnalytics;
|
|
3006
|
+
const pinnedEndpoint = previous?.getEndpoint?.() || "";
|
|
3007
|
+
const merged = {
|
|
3008
|
+
pbjsGlobalName,
|
|
3009
|
+
...defaults,
|
|
3010
|
+
...options
|
|
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;
|
|
3017
|
+
}
|
|
3018
|
+
if (previous) {
|
|
3019
|
+
try {
|
|
3020
|
+
previous.disable();
|
|
3021
|
+
} catch (e) {
|
|
3022
|
+
defaultLogger.warn("Failed to disable previous analytics instance:", e);
|
|
3023
|
+
}
|
|
3024
|
+
}
|
|
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);
|
|
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);
|
|
3063
|
+
}
|
|
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;
|
|
3089
|
+
}
|
|
3090
|
+
} catch {
|
|
3091
|
+
}
|
|
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;
|
|
3124
|
+
}
|
|
3125
|
+
} catch {
|
|
3126
|
+
}
|
|
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);
|
|
3172
|
+
};
|
|
3173
|
+
hookedRemoveEventListener.__bidkernelHooked = true;
|
|
3174
|
+
proto.removeEventListener = hookedRemoveEventListener;
|
|
3175
|
+
}
|
|
3176
|
+
} catch {
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
function hookImaPrototype(ima) {
|
|
3180
|
+
if (!ima || typeof ima !== "object") return;
|
|
3181
|
+
try {
|
|
3182
|
+
if (ima.AdsManagerLoadedEvent) {
|
|
3183
|
+
hookAdsManagerLoadedEvent(ima.AdsManagerLoadedEvent);
|
|
3412
3184
|
}
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
if (res.status >= 400 && res.status < 500 && res.status !== 429 && res.status !== 408) {
|
|
3417
|
-
this.log("WARN", `Dropping batch due to non-retryable client error HTTP ${res.status}`);
|
|
3418
|
-
return;
|
|
3419
|
-
}
|
|
3420
|
-
if (!isReplay) this.handleSendFailure(payload.events);
|
|
3421
|
-
} else {
|
|
3422
|
-
if (!isReplay) {
|
|
3423
|
-
this.consecutiveSendFailures = 0;
|
|
3424
|
-
this.nextSendAllowedAt = 0;
|
|
3425
|
-
}
|
|
3426
|
-
if (onDelivered) onDelivered();
|
|
3427
|
-
}
|
|
3428
|
-
}).catch((err) => {
|
|
3429
|
-
this.log("ERROR", "Failed to send batch", err);
|
|
3430
|
-
if (!isReplay) this.handleSendFailure(payload.events);
|
|
3431
|
-
});
|
|
3432
|
-
const proc = typeof globalThis !== "undefined" ? globalThis.process : void 0;
|
|
3433
|
-
if (proc && typeof proc._tickCallback === "function") {
|
|
3185
|
+
const loadedDesc = Object.getOwnPropertyDescriptor(ima, "AdsManagerLoadedEvent");
|
|
3186
|
+
if (!loadedDesc || loadedDesc.configurable) {
|
|
3187
|
+
let _loadedEvent = ima.AdsManagerLoadedEvent;
|
|
3434
3188
|
try {
|
|
3435
|
-
|
|
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
|
+
});
|
|
3436
3200
|
} catch {
|
|
3437
3201
|
}
|
|
3438
3202
|
}
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
this.consecutiveSendFailures++;
|
|
3442
|
-
const backoff = Math.min(
|
|
3443
|
-
FLUSH_INTERVAL_MS * 2 ** (this.consecutiveSendFailures - 1),
|
|
3444
|
-
MAX_SEND_BACKOFF_MS
|
|
3445
|
-
);
|
|
3446
|
-
this.nextSendAllowedAt = Date.now() + backoff;
|
|
3447
|
-
if (this.consecutiveSendFailures <= MAX_CONSECUTIVE_SEND_FAILURES) {
|
|
3448
|
-
this.requeue(events);
|
|
3449
|
-
} else {
|
|
3450
|
-
this.log(
|
|
3451
|
-
"WARN",
|
|
3452
|
-
`Dropping ${events.length} events after ${this.consecutiveSendFailures} consecutive send failures`
|
|
3453
|
-
);
|
|
3454
|
-
}
|
|
3455
|
-
}
|
|
3456
|
-
flush() {
|
|
3457
|
-
if (Date.now() < this.nextSendAllowedAt) return;
|
|
3458
|
-
let payload = this.drainBatch();
|
|
3459
|
-
while (payload) {
|
|
3460
|
-
this.sendFetch(payload, false);
|
|
3461
|
-
if (Date.now() < this.nextSendAllowedAt) break;
|
|
3462
|
-
payload = this.drainBatch();
|
|
3203
|
+
if (ima.AdsLoader) {
|
|
3204
|
+
hookAdsLoader(ima.AdsLoader);
|
|
3463
3205
|
}
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
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 {
|
|
3476
3222
|
}
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
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 {
|
|
3480
3243
|
}
|
|
3481
|
-
payload = this.drainBatch();
|
|
3482
3244
|
}
|
|
3483
|
-
|
|
3245
|
+
} catch {
|
|
3484
3246
|
}
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3247
|
+
}
|
|
3248
|
+
function initImaInterception() {
|
|
3249
|
+
if (typeof window === "undefined") return;
|
|
3250
|
+
const win = window;
|
|
3251
|
+
if (win.google?.ima) {
|
|
3252
|
+
hookImaPrototype(win.google.ima);
|
|
3491
3253
|
}
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
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 {
|
|
3500
3271
|
}
|
|
3501
|
-
if (pendingCount >= MAX_PENDING_BATCHES) return null;
|
|
3502
|
-
const key = `${this.pendingKeyPrefix()}${Date.now()}_${Math.floor(Math.random() * 1e6)}`;
|
|
3503
|
-
localStorage.setItem(key, bytesToBase64(encoded));
|
|
3504
|
-
this.persistedBatchKeys.push(key);
|
|
3505
|
-
return key;
|
|
3506
|
-
} catch {
|
|
3507
|
-
return null;
|
|
3508
3272
|
}
|
|
3509
3273
|
}
|
|
3510
|
-
|
|
3511
|
-
|
|
3274
|
+
const googleDesc = Object.getOwnPropertyDescriptor(win, "google");
|
|
3275
|
+
if (!googleDesc || googleDesc.configurable) {
|
|
3276
|
+
let _google = win.google;
|
|
3512
3277
|
try {
|
|
3513
|
-
|
|
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
|
+
});
|
|
3514
3311
|
} catch {
|
|
3515
3312
|
}
|
|
3516
|
-
const idx = this.persistedBatchKeys.indexOf(key);
|
|
3517
|
-
if (idx !== -1) this.persistedBatchKeys.splice(idx, 1);
|
|
3518
3313
|
}
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
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;
|
|
3529
3334
|
}
|
|
3530
|
-
}
|
|
3335
|
+
}
|
|
3531
3336
|
}
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
if (!this.config.propertyId) return;
|
|
3543
|
-
const ownPrefix = this.pendingKeyPrefix();
|
|
3544
|
-
const keys = [];
|
|
3545
|
-
const legacyKeys = [];
|
|
3546
|
-
for (let i = 0; i < localStorage.length; i++) {
|
|
3547
|
-
const k = localStorage.key(i);
|
|
3548
|
-
if (!k || !k.startsWith(PENDING_BATCH_KEY_PREFIX)) continue;
|
|
3549
|
-
if (k.startsWith(ownPrefix)) {
|
|
3550
|
-
keys.push(k);
|
|
3551
|
-
} else if (isLegacyPendingKey(k)) {
|
|
3552
|
-
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
|
+
}
|
|
3553
3347
|
}
|
|
3554
3348
|
}
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
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;
|
|
3559
3358
|
}
|
|
3560
3359
|
}
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
if (!value) continue;
|
|
3570
|
-
const ts = parseInt(key.slice(ownPrefix.length), 10);
|
|
3571
|
-
if (!Number.isFinite(ts) || Date.now() - ts > PENDING_BATCH_MAX_AGE_MS) continue;
|
|
3572
|
-
let encoded;
|
|
3573
|
-
try {
|
|
3574
|
-
encoded = base64ToBytes(value);
|
|
3575
|
-
} catch {
|
|
3576
|
-
continue;
|
|
3577
|
-
}
|
|
3578
|
-
if (encoded.byteLength === 0) continue;
|
|
3579
|
-
this.log("DEBUG", `Resending persisted exit batch (${encoded.byteLength} bytes)`);
|
|
3580
|
-
this.sendFetch(
|
|
3581
|
-
{
|
|
3582
|
-
url: `${this.config.endpoint}/${this.config.propertyId}`,
|
|
3583
|
-
encoded,
|
|
3584
|
-
events: []
|
|
3585
|
-
},
|
|
3586
|
-
false,
|
|
3587
|
-
void 0,
|
|
3588
|
-
true
|
|
3589
|
-
);
|
|
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;
|
|
3590
3368
|
}
|
|
3591
|
-
} catch {
|
|
3592
3369
|
}
|
|
3593
3370
|
}
|
|
3594
|
-
|
|
3595
|
-
|
|
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
|
+
}
|
|
3596
3378
|
}
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
const matched = config.find((item) => item?.provider === "bidkernel") ?? (allowProviderless ? config.find(
|
|
3606
|
-
(item) => item && typeof item === "object" && item.provider === void 0
|
|
3607
|
-
) : void 0);
|
|
3608
|
-
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
|
+
};
|
|
3609
3387
|
}
|
|
3610
|
-
if (
|
|
3611
|
-
|
|
3612
|
-
|
|
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();
|
|
3613
3397
|
}
|
|
3614
|
-
|
|
3615
|
-
|
|
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
|
+
});
|
|
3616
3418
|
}
|
|
3617
|
-
|
|
3618
|
-
|
|
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
|
+
}
|
|
3619
3463
|
}
|
|
3620
|
-
}
|
|
3621
|
-
|
|
3622
|
-
}
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
if (win.bidkernelPrebidAnalytics) {
|
|
3630
|
-
const bpa = win.bidkernelPrebidAnalytics;
|
|
3631
|
-
if (bpa.options) {
|
|
3632
|
-
const extracted = extractAnalyticsOptions(bpa.options);
|
|
3633
|
-
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
|
+
}
|
|
3634
3473
|
}
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
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
|
+
}
|
|
3638
3517
|
}
|
|
3639
|
-
|
|
3640
|
-
|
|
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 {
|
|
3641
3550
|
}
|
|
3642
3551
|
}
|
|
3643
|
-
|
|
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;
|
|
3644
3565
|
}
|
|
3645
|
-
function
|
|
3646
|
-
const
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
const previous = win._bidkernelPrebidAnalytics;
|
|
3651
|
-
const pinnedEndpoint = previous?.getEndpoint?.() || "";
|
|
3652
|
-
const merged = {
|
|
3653
|
-
pbjsGlobalName,
|
|
3654
|
-
...defaults,
|
|
3655
|
-
...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 () => {
|
|
3656
3571
|
};
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
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");
|
|
3662
3607
|
}
|
|
3663
|
-
|
|
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) {
|
|
3664
3635
|
try {
|
|
3665
|
-
|
|
3636
|
+
options.onImpression(slotId, { element: vEl || el, bid: mergedBid });
|
|
3666
3637
|
} catch (e) {
|
|
3667
|
-
|
|
3638
|
+
self.log("ERROR", "Error in onImpression callback", e);
|
|
3668
3639
|
}
|
|
3669
3640
|
}
|
|
3670
|
-
const analytics = new BidkernelPrebidAnalytics(merged);
|
|
3671
|
-
analytics.enable();
|
|
3672
|
-
win._bidkernelPrebidAnalytics = analytics;
|
|
3673
|
-
return analytics;
|
|
3674
3641
|
};
|
|
3675
|
-
const
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
}
|
|
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
|
+
}
|
|
3694
3665
|
}
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
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);
|
|
3698
3698
|
}
|
|
3699
|
-
} catch (e) {
|
|
3700
|
-
defaultLogger.warn("Failed to register standard Prebid analytics adapter:", e);
|
|
3701
3699
|
}
|
|
3702
3700
|
};
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
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 });
|
|
3708
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;
|
|
3709
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
|
+
};
|
|
3710
3785
|
|
|
3711
3786
|
// src/index.ts
|
|
3712
3787
|
function getbidkernel(alias = "bidkernel") {
|
|
@@ -3741,7 +3816,11 @@ function getbidkernel(alias = "bidkernel") {
|
|
|
3741
3816
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3742
3817
|
0 && (module.exports = {
|
|
3743
3818
|
BidkernelPrebidAnalytics,
|
|
3819
|
+
CACHED_BID_TTL_MS,
|
|
3744
3820
|
PrebidEventDeduper,
|
|
3821
|
+
attachImaAdsManager,
|
|
3822
|
+
attachVideoPlayer,
|
|
3823
|
+
getOrCreateSessionId,
|
|
3745
3824
|
getPrebidEventKey,
|
|
3746
3825
|
getbidkernel,
|
|
3747
3826
|
hookImaPrototype,
|