@bidkernel/analytics 0.12.0 → 0.16.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 +66 -1
- package/dist/index.d.ts +66 -1
- package/dist/index.js +221 -70
- package/dist/index.mjs +220 -70
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -54,6 +54,13 @@ interface AnalyticsConfig {
|
|
|
54
54
|
errorsEnabled?: boolean;
|
|
55
55
|
/** Whether to enable client-side IAB viewability and time-in-view measurement. Defaults to true. */
|
|
56
56
|
viewabilityEnabled?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Whether identifiers may be written to localStorage (session id and its
|
|
59
|
+
* timestamp, exit batches). Defaults to true. Set false until storage
|
|
60
|
+
* consent is known, then flip with setStorageAllowed(true); events are
|
|
61
|
+
* always collected and sent regardless.
|
|
62
|
+
*/
|
|
63
|
+
storageAllowed?: boolean;
|
|
57
64
|
logLevel?: "DEBUG" | "INFO" | "WARN" | "ERROR";
|
|
58
65
|
/** Window global holding the Prebid instance to attach to. Defaults to "pbjs". */
|
|
59
66
|
pbjsGlobalName?: string;
|
|
@@ -123,6 +130,15 @@ interface SlotViewabilityState {
|
|
|
123
130
|
* registerPrebidAnalytics.
|
|
124
131
|
*/
|
|
125
132
|
declare function isTrustedEndpoint(raw: string): boolean;
|
|
133
|
+
/**
|
|
134
|
+
* The visitor's session id: a 30-minute rolling session persisted in
|
|
135
|
+
* localStorage when storage is allowed, kept in memory otherwise. A session
|
|
136
|
+
* begun in memory (before storage consent) is adopted and persisted the
|
|
137
|
+
* first time this runs with storage allowed, so consent never splits a
|
|
138
|
+
* session. Exported so an integration can report the same id the adapter
|
|
139
|
+
* stamps on its batches.
|
|
140
|
+
*/
|
|
141
|
+
declare function getOrCreateSessionId(): string;
|
|
126
142
|
/**
|
|
127
143
|
* Canonical duplicate-detection key for a Prebid event. Shared by the adapter
|
|
128
144
|
* and the diagnostic extension so the two can never drift apart. Covers the
|
|
@@ -149,6 +165,13 @@ declare class BidkernelPrebidAnalytics {
|
|
|
149
165
|
private static activeInstances;
|
|
150
166
|
static getActiveInstances(): Set<BidkernelPrebidAnalytics>;
|
|
151
167
|
static initImaInterception(): void;
|
|
168
|
+
/**
|
|
169
|
+
* Allows or forbids localStorage writes for every instance on the page.
|
|
170
|
+
* Collection and delivery are unaffected: only the persisted session id,
|
|
171
|
+
* its timestamp and exit batches are gated.
|
|
172
|
+
*/
|
|
173
|
+
static setStorageAllowed(allowed: boolean): void;
|
|
174
|
+
static isStorageAllowed(): boolean;
|
|
152
175
|
private config;
|
|
153
176
|
private queue;
|
|
154
177
|
private errorCount;
|
|
@@ -171,6 +194,10 @@ declare class BidkernelPrebidAnalytics {
|
|
|
171
194
|
private slotRefreshIndices;
|
|
172
195
|
private slotLastAuctionIds;
|
|
173
196
|
private pendingThresholdListeners;
|
|
197
|
+
private pendingViewableListeners;
|
|
198
|
+
private slotElements;
|
|
199
|
+
private clickDetachCleanups;
|
|
200
|
+
private lastClickAt;
|
|
174
201
|
private slotEmittedImpressionKeys;
|
|
175
202
|
private cachedWinningBids;
|
|
176
203
|
private videoDetachCleanups;
|
|
@@ -178,10 +205,48 @@ declare class BidkernelPrebidAnalytics {
|
|
|
178
205
|
private logger;
|
|
179
206
|
constructor(config: AnalyticsConfig);
|
|
180
207
|
enable(): void;
|
|
208
|
+
/**
|
|
209
|
+
* Binds the Prebid event handlers on the configured global and replays
|
|
210
|
+
* pbjs.getEvents() history. enable() calls this when attachPbjsListeners is
|
|
211
|
+
* true. An integration that loads Prebid lazily constructs the instance with
|
|
212
|
+
* attachPbjsListeners=false and calls this once pbjs exists; disable() (and
|
|
213
|
+
* therefore navigate()) drops the handlers, so call it again after either.
|
|
214
|
+
* Idempotent. Returns true when handlers are bound after the call, false
|
|
215
|
+
* when the instance is disabled or pbjs.onEvent is not available yet.
|
|
216
|
+
*/
|
|
217
|
+
attachPbjs(): boolean;
|
|
181
218
|
disable(): void;
|
|
182
219
|
private handleVisibilityChange;
|
|
183
220
|
private handleIntersection;
|
|
184
221
|
private handleDwellComplete;
|
|
222
|
+
private notifyViewable;
|
|
223
|
+
/**
|
|
224
|
+
* Calls back once per render cycle when the slot has met the IAB viewable
|
|
225
|
+
* standard this adapter measures (immediately if it already has). Works
|
|
226
|
+
* before the slot is observed: the listener waits for observeSlot. Returns
|
|
227
|
+
* an unsubscribe function.
|
|
228
|
+
*/
|
|
229
|
+
/**
|
|
230
|
+
* Names the element to measure for a slot when the creative renders
|
|
231
|
+
* somewhere other than the slot element (a sticky bar, an interstitial).
|
|
232
|
+
* Applies to the current cycle at once and to every later render.
|
|
233
|
+
*/
|
|
234
|
+
setSlotElement(slotId: string, element: HTMLElement): void;
|
|
235
|
+
/**
|
|
236
|
+
* Records a click on the slot's current creative, attributed to the cached
|
|
237
|
+
* winning bid. Repeats within one second are one click. Returns whether a
|
|
238
|
+
* CLICK was recorded.
|
|
239
|
+
*/
|
|
240
|
+
recordClick(slotId: string, options?: {
|
|
241
|
+
metadata?: Record<string, string>;
|
|
242
|
+
}): boolean;
|
|
243
|
+
/**
|
|
244
|
+
* Detects clicks into a cross-origin creative frame: a click inside an
|
|
245
|
+
* iframe moves focus into it and blurs the window. Returns a detach
|
|
246
|
+
* function; every tracker is detached on disable().
|
|
247
|
+
*/
|
|
248
|
+
attachClickTracker(element: HTMLElement, slotId: string): () => void;
|
|
249
|
+
onViewable(slotId: string, callback: (slotId: string) => void): () => void;
|
|
185
250
|
private scheduleThresholdTimers;
|
|
186
251
|
private checkThresholdListeners;
|
|
187
252
|
private getCurrentTimeInView;
|
|
@@ -337,4 +402,4 @@ interface AdsGlobal {
|
|
|
337
402
|
*/
|
|
338
403
|
declare function getbidkernel(alias?: string): AdsGlobal;
|
|
339
404
|
|
|
340
|
-
export { type AdsGlobal, type AnalyticsConfig, BidkernelPrebidAnalytics, type CachedWinningBid, PrebidEventDeduper, type SizeMapping, type SlotConfig, type SlotObserveOptions, type SlotSize, type SlotSizes, type SlotViewabilityState, type VideoPlayerOptions, getPrebidEventKey, getbidkernel, hookImaPrototype, initImaInterception, isTrustedEndpoint, registerPrebidAnalytics };
|
|
405
|
+
export { type AdsGlobal, type AnalyticsConfig, BidkernelPrebidAnalytics, type CachedWinningBid, PrebidEventDeduper, type SizeMapping, type SlotConfig, type SlotObserveOptions, type SlotSize, type SlotSizes, type SlotViewabilityState, type VideoPlayerOptions, getOrCreateSessionId, getPrebidEventKey, getbidkernel, hookImaPrototype, initImaInterception, isTrustedEndpoint, registerPrebidAnalytics };
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,7 @@ var src_exports = {};
|
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
BidkernelPrebidAnalytics: () => BidkernelPrebidAnalytics,
|
|
24
24
|
PrebidEventDeduper: () => PrebidEventDeduper,
|
|
25
|
+
getOrCreateSessionId: () => getOrCreateSessionId,
|
|
25
26
|
getPrebidEventKey: () => getPrebidEventKey,
|
|
26
27
|
getbidkernel: () => getbidkernel,
|
|
27
28
|
hookImaPrototype: () => hookImaPrototype,
|
|
@@ -1085,11 +1086,13 @@ var CACHED_BID_TTL_MS = 30 * 60 * 1e3;
|
|
|
1085
1086
|
var MAX_CACHED_BID_KEYS = 200;
|
|
1086
1087
|
var MAX_IMPRESSION_KEYS = 1e3;
|
|
1087
1088
|
var MAX_TRACKED_AUCTIONS = 50;
|
|
1089
|
+
var CLICK_DEDUP_MS = 1e3;
|
|
1088
1090
|
var SESSION_KEY = "_bidkernel_session";
|
|
1089
1091
|
var SESSION_TS_KEY = "_bidkernel_session_ts";
|
|
1090
1092
|
var THIRTY_MINUTES_MS = 30 * 60 * 1e3;
|
|
1091
1093
|
var inMemorySessionId = null;
|
|
1092
1094
|
var inMemorySessionTs = 0;
|
|
1095
|
+
var storageAllowed = true;
|
|
1093
1096
|
function generateUUID() {
|
|
1094
1097
|
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
1095
1098
|
return crypto.randomUUID();
|
|
@@ -1159,6 +1162,7 @@ function sameEndpointOrigin(a, b) {
|
|
|
1159
1162
|
function extendSession() {
|
|
1160
1163
|
const now = Date.now();
|
|
1161
1164
|
inMemorySessionTs = now;
|
|
1165
|
+
if (!storageAllowed) return;
|
|
1162
1166
|
try {
|
|
1163
1167
|
if (typeof localStorage !== "undefined") {
|
|
1164
1168
|
localStorage.setItem(SESSION_TS_KEY, now.toString());
|
|
@@ -1168,18 +1172,23 @@ function extendSession() {
|
|
|
1168
1172
|
}
|
|
1169
1173
|
function getOrCreateSessionId() {
|
|
1170
1174
|
const now = Date.now();
|
|
1175
|
+
const memoryFresh = !!inMemorySessionId && !!inMemorySessionTs && now - inMemorySessionTs <= THIRTY_MINUTES_MS;
|
|
1171
1176
|
try {
|
|
1172
|
-
if (typeof localStorage !== "undefined") {
|
|
1177
|
+
if (storageAllowed && typeof localStorage !== "undefined") {
|
|
1173
1178
|
const storedId = localStorage.getItem(SESSION_KEY);
|
|
1174
1179
|
const tsStr = localStorage.getItem(SESSION_TS_KEY);
|
|
1175
1180
|
const storedTs = tsStr ? parseInt(tsStr, 10) || 0 : 0;
|
|
1176
1181
|
if (storedId && storedTs && now - storedTs <= THIRTY_MINUTES_MS) {
|
|
1177
1182
|
localStorage.setItem(SESSION_TS_KEY, now.toString());
|
|
1183
|
+
inMemorySessionId = storedId;
|
|
1184
|
+
inMemorySessionTs = now;
|
|
1178
1185
|
return storedId;
|
|
1179
1186
|
}
|
|
1180
|
-
const newId = generateUUID();
|
|
1187
|
+
const newId = memoryFresh ? inMemorySessionId : generateUUID();
|
|
1181
1188
|
localStorage.setItem(SESSION_KEY, newId);
|
|
1182
1189
|
localStorage.setItem(SESSION_TS_KEY, now.toString());
|
|
1190
|
+
inMemorySessionId = newId;
|
|
1191
|
+
inMemorySessionTs = now;
|
|
1183
1192
|
return newId;
|
|
1184
1193
|
}
|
|
1185
1194
|
} catch {
|
|
@@ -1545,6 +1554,17 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1545
1554
|
static initImaInterception() {
|
|
1546
1555
|
initImaInterception();
|
|
1547
1556
|
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Allows or forbids localStorage writes for every instance on the page.
|
|
1559
|
+
* Collection and delivery are unaffected: only the persisted session id,
|
|
1560
|
+
* its timestamp and exit batches are gated.
|
|
1561
|
+
*/
|
|
1562
|
+
static setStorageAllowed(allowed) {
|
|
1563
|
+
storageAllowed = allowed;
|
|
1564
|
+
}
|
|
1565
|
+
static isStorageAllowed() {
|
|
1566
|
+
return storageAllowed;
|
|
1567
|
+
}
|
|
1548
1568
|
config;
|
|
1549
1569
|
queue = [];
|
|
1550
1570
|
errorCount = 0;
|
|
@@ -1573,6 +1593,12 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1573
1593
|
slotRefreshIndices = /* @__PURE__ */ new Map();
|
|
1574
1594
|
slotLastAuctionIds = /* @__PURE__ */ new Map();
|
|
1575
1595
|
pendingThresholdListeners = /* @__PURE__ */ new Map();
|
|
1596
|
+
pendingViewableListeners = /* @__PURE__ */ new Map();
|
|
1597
|
+
// Element a subscriber named for a slot (a sticky bar or interstitial that
|
|
1598
|
+
// renders outside the slot element); wins over document.getElementById.
|
|
1599
|
+
slotElements = /* @__PURE__ */ new Map();
|
|
1600
|
+
clickDetachCleanups = /* @__PURE__ */ new Set();
|
|
1601
|
+
lastClickAt = /* @__PURE__ */ new Map();
|
|
1576
1602
|
// Impression deduplication per slot and refresh cycle (strictly 1 impression per cycle)
|
|
1577
1603
|
slotEmittedImpressionKeys = /* @__PURE__ */ new Set();
|
|
1578
1604
|
// Winning bid cache from bidWon to marry with subsequent render triggers (adRenderSucceeded, video, etc.)
|
|
@@ -1583,6 +1609,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1583
1609
|
auctionBidderOutcomes = /* @__PURE__ */ new Map();
|
|
1584
1610
|
logger;
|
|
1585
1611
|
constructor(config) {
|
|
1612
|
+
if (config.storageAllowed !== void 0) {
|
|
1613
|
+
_BidkernelPrebidAnalytics.setStorageAllowed(config.storageAllowed);
|
|
1614
|
+
}
|
|
1586
1615
|
const requestedEndpoint = config.endpoint || "";
|
|
1587
1616
|
const endpoint = !requestedEndpoint || isTrustedEndpoint(requestedEndpoint) ? requestedEndpoint : "";
|
|
1588
1617
|
this.config = {
|
|
@@ -1598,6 +1627,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1598
1627
|
warningsEnabled: config.warningsEnabled ?? true,
|
|
1599
1628
|
errorsEnabled: config.errorsEnabled ?? true,
|
|
1600
1629
|
viewabilityEnabled: config.viewabilityEnabled ?? true,
|
|
1630
|
+
storageAllowed: config.storageAllowed ?? true,
|
|
1601
1631
|
logLevel: config.logLevel || "INFO",
|
|
1602
1632
|
pbjsGlobalName: config.pbjsGlobalName || "pbjs",
|
|
1603
1633
|
attachPbjsListeners: config.attachPbjsListeners ?? true,
|
|
@@ -1640,65 +1670,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1640
1670
|
}
|
|
1641
1671
|
}
|
|
1642
1672
|
if (this.config.attachPbjsListeners) {
|
|
1643
|
-
|
|
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
|
-
}
|
|
1673
|
+
this.attachPbjs();
|
|
1702
1674
|
}
|
|
1703
1675
|
if (typeof window !== "undefined") {
|
|
1704
1676
|
if (!this.pageUrl) {
|
|
@@ -1721,6 +1693,68 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1721
1693
|
}
|
|
1722
1694
|
}
|
|
1723
1695
|
}
|
|
1696
|
+
/**
|
|
1697
|
+
* Binds the Prebid event handlers on the configured global and replays
|
|
1698
|
+
* pbjs.getEvents() history. enable() calls this when attachPbjsListeners is
|
|
1699
|
+
* true. An integration that loads Prebid lazily constructs the instance with
|
|
1700
|
+
* attachPbjsListeners=false and calls this once pbjs exists; disable() (and
|
|
1701
|
+
* therefore navigate()) drops the handlers, so call it again after either.
|
|
1702
|
+
* Idempotent. Returns true when handlers are bound after the call, false
|
|
1703
|
+
* when the instance is disabled or pbjs.onEvent is not available yet.
|
|
1704
|
+
*/
|
|
1705
|
+
attachPbjs() {
|
|
1706
|
+
if (!this.isEnabled) return false;
|
|
1707
|
+
if (this.boundPbjsHandlers.length > 0) return true;
|
|
1708
|
+
const win = typeof window !== "undefined" ? window : {};
|
|
1709
|
+
const pbjs = win[this.config.pbjsGlobalName] || {};
|
|
1710
|
+
if (typeof pbjs.onEvent !== "function") {
|
|
1711
|
+
this.log("WARN", "pbjs.onEvent is not defined. Prebid analytics will not function.");
|
|
1712
|
+
return false;
|
|
1713
|
+
}
|
|
1714
|
+
this.log("DEBUG", "Attaching event listeners to pbjs");
|
|
1715
|
+
const handlerMap = {
|
|
1716
|
+
auctionInit: this.handleAuctionInit.bind(this),
|
|
1717
|
+
auctionEnd: this.handleAuctionEnd.bind(this),
|
|
1718
|
+
bidRequested: this.handleBidRequested.bind(this),
|
|
1719
|
+
bidResponse: this.handleBidResponse.bind(this),
|
|
1720
|
+
bidTimeout: this.handleBidTimeout.bind(this),
|
|
1721
|
+
bidWon: this.handleBidWon.bind(this),
|
|
1722
|
+
noBid: this.handleNoBid.bind(this),
|
|
1723
|
+
adRenderFailed: this.handleAdRenderFailed.bind(this),
|
|
1724
|
+
adRenderSucceeded: this.handleAdRenderSucceeded.bind(this)
|
|
1725
|
+
};
|
|
1726
|
+
for (const [name, fn] of Object.entries(handlerMap)) {
|
|
1727
|
+
pbjs.onEvent(name, fn);
|
|
1728
|
+
this.boundPbjsHandlers.push({ event: name, handler: fn });
|
|
1729
|
+
}
|
|
1730
|
+
if (typeof pbjs.getEvents === "function") {
|
|
1731
|
+
try {
|
|
1732
|
+
const pastEvents = pbjs.getEvents();
|
|
1733
|
+
if (Array.isArray(pastEvents)) {
|
|
1734
|
+
const newPastEvents = pastEvents.slice(this.replayedEventCount);
|
|
1735
|
+
this.replayedEventCount = pastEvents.length;
|
|
1736
|
+
if (newPastEvents.length > 0) {
|
|
1737
|
+
this.log(
|
|
1738
|
+
"DEBUG",
|
|
1739
|
+
`Replaying ${newPastEvents.length} historical events from pbjs.getEvents()`
|
|
1740
|
+
);
|
|
1741
|
+
for (const ev of newPastEvents) {
|
|
1742
|
+
if (!ev) continue;
|
|
1743
|
+
const eventType = ev.eventType || ev.event || ev.name;
|
|
1744
|
+
const args = ev.args !== void 0 ? ev.args : ev.data !== void 0 ? ev.data : ev;
|
|
1745
|
+
const handler = handlerMap[eventType];
|
|
1746
|
+
if (handler) {
|
|
1747
|
+
handler(args);
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
} catch (e) {
|
|
1753
|
+
this.log("WARN", "Failed to replay historical events from pbjs.getEvents()", e);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
return true;
|
|
1757
|
+
}
|
|
1724
1758
|
disable() {
|
|
1725
1759
|
if (!this.isEnabled) return;
|
|
1726
1760
|
this.flushAllSlotsTimeInView();
|
|
@@ -1752,6 +1786,14 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1752
1786
|
}
|
|
1753
1787
|
}
|
|
1754
1788
|
this.videoDetachCleanups.clear();
|
|
1789
|
+
for (const cleanup of Array.from(this.clickDetachCleanups)) {
|
|
1790
|
+
try {
|
|
1791
|
+
cleanup();
|
|
1792
|
+
} catch {
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
this.clickDetachCleanups.clear();
|
|
1796
|
+
this.slotElements.clear();
|
|
1755
1797
|
this.cachedWinningBids.clear();
|
|
1756
1798
|
this.slotEmittedImpressionKeys.clear();
|
|
1757
1799
|
this.slotLastAuctionIds.clear();
|
|
@@ -1913,6 +1955,105 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
1913
1955
|
}
|
|
1914
1956
|
});
|
|
1915
1957
|
}
|
|
1958
|
+
this.notifyViewable(record);
|
|
1959
|
+
}
|
|
1960
|
+
notifyViewable(record) {
|
|
1961
|
+
if (!record.viewableFired) return;
|
|
1962
|
+
for (const listener of record.viewableListeners) {
|
|
1963
|
+
if (listener.firedForIndex === record.refreshIndex) continue;
|
|
1964
|
+
listener.firedForIndex = record.refreshIndex;
|
|
1965
|
+
try {
|
|
1966
|
+
listener.callback(record.slotId);
|
|
1967
|
+
} catch (e) {
|
|
1968
|
+
this.log("ERROR", "Error in viewable listener callback", e);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
/**
|
|
1973
|
+
* Calls back once per render cycle when the slot has met the IAB viewable
|
|
1974
|
+
* standard this adapter measures (immediately if it already has). Works
|
|
1975
|
+
* before the slot is observed: the listener waits for observeSlot. Returns
|
|
1976
|
+
* an unsubscribe function.
|
|
1977
|
+
*/
|
|
1978
|
+
/**
|
|
1979
|
+
* Names the element to measure for a slot when the creative renders
|
|
1980
|
+
* somewhere other than the slot element (a sticky bar, an interstitial).
|
|
1981
|
+
* Applies to the current cycle at once and to every later render.
|
|
1982
|
+
*/
|
|
1983
|
+
setSlotElement(slotId, element) {
|
|
1984
|
+
this.slotElements.set(slotId, element);
|
|
1985
|
+
const record = this.slotViewabilityRecords.get(slotId);
|
|
1986
|
+
if (record && record.element !== element) {
|
|
1987
|
+
this.observeSlot(element, slotId, {
|
|
1988
|
+
refreshIndex: record.refreshIndex,
|
|
1989
|
+
emitRefreshEvent: false
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
/**
|
|
1994
|
+
* Records a click on the slot's current creative, attributed to the cached
|
|
1995
|
+
* winning bid. Repeats within one second are one click. Returns whether a
|
|
1996
|
+
* CLICK was recorded.
|
|
1997
|
+
*/
|
|
1998
|
+
recordClick(slotId, options) {
|
|
1999
|
+
if (!this.isEnabled || !slotId) return false;
|
|
2000
|
+
const now = Date.now();
|
|
2001
|
+
if (now - (this.lastClickAt.get(slotId) || 0) < CLICK_DEDUP_MS) return false;
|
|
2002
|
+
this.lastClickAt.set(slotId, now);
|
|
2003
|
+
const cached = this.getCachedBid(slotId);
|
|
2004
|
+
this.enqueue(TraceEventType.CLICK, "click", {
|
|
2005
|
+
auctionId: cached?.auctionId || "",
|
|
2006
|
+
transactionId: cached?.transactionId || "",
|
|
2007
|
+
adUnitCode: slotId,
|
|
2008
|
+
bid: cached?.bidTrace,
|
|
2009
|
+
metadata: {
|
|
2010
|
+
...options?.metadata,
|
|
2011
|
+
refresh_index: String(this.getRefreshIndex(slotId))
|
|
2012
|
+
}
|
|
2013
|
+
});
|
|
2014
|
+
return true;
|
|
2015
|
+
}
|
|
2016
|
+
/**
|
|
2017
|
+
* Detects clicks into a cross-origin creative frame: a click inside an
|
|
2018
|
+
* iframe moves focus into it and blurs the window. Returns a detach
|
|
2019
|
+
* function; every tracker is detached on disable().
|
|
2020
|
+
*/
|
|
2021
|
+
attachClickTracker(element, slotId) {
|
|
2022
|
+
if (typeof window === "undefined") return () => {
|
|
2023
|
+
};
|
|
2024
|
+
const onBlur = () => {
|
|
2025
|
+
setTimeout(() => {
|
|
2026
|
+
const active = document.activeElement;
|
|
2027
|
+
if (active && (active === element || element.contains(active))) {
|
|
2028
|
+
this.recordClick(slotId, { metadata: { source: "blur" } });
|
|
2029
|
+
window.focus();
|
|
2030
|
+
}
|
|
2031
|
+
}, 0);
|
|
2032
|
+
};
|
|
2033
|
+
window.addEventListener("blur", onBlur);
|
|
2034
|
+
const cleanup = () => {
|
|
2035
|
+
window.removeEventListener("blur", onBlur);
|
|
2036
|
+
this.clickDetachCleanups.delete(cleanup);
|
|
2037
|
+
};
|
|
2038
|
+
this.clickDetachCleanups.add(cleanup);
|
|
2039
|
+
return cleanup;
|
|
2040
|
+
}
|
|
2041
|
+
onViewable(slotId, callback) {
|
|
2042
|
+
const listener = { callback, firedForIndex: -1 };
|
|
2043
|
+
const record = this.slotViewabilityRecords.get(slotId);
|
|
2044
|
+
if (record) {
|
|
2045
|
+
record.viewableListeners.add(listener);
|
|
2046
|
+
this.notifyViewable(record);
|
|
2047
|
+
} else {
|
|
2048
|
+
if (!this.pendingViewableListeners.has(slotId)) {
|
|
2049
|
+
this.pendingViewableListeners.set(slotId, /* @__PURE__ */ new Set());
|
|
2050
|
+
}
|
|
2051
|
+
this.pendingViewableListeners.get(slotId).add(listener);
|
|
2052
|
+
}
|
|
2053
|
+
return () => {
|
|
2054
|
+
this.slotViewabilityRecords.get(slotId)?.viewableListeners.delete(listener);
|
|
2055
|
+
this.pendingViewableListeners.get(slotId)?.delete(listener);
|
|
2056
|
+
};
|
|
1916
2057
|
}
|
|
1917
2058
|
scheduleThresholdTimers(record) {
|
|
1918
2059
|
if (!record.inView || typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
@@ -2102,6 +2243,8 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2102
2243
|
}
|
|
2103
2244
|
const listeners = this.pendingThresholdListeners.get(resolvedSlotId) || /* @__PURE__ */ new Set();
|
|
2104
2245
|
this.pendingThresholdListeners.delete(resolvedSlotId);
|
|
2246
|
+
const viewableListeners = this.pendingViewableListeners.get(resolvedSlotId) || /* @__PURE__ */ new Set();
|
|
2247
|
+
this.pendingViewableListeners.delete(resolvedSlotId);
|
|
2105
2248
|
const record = {
|
|
2106
2249
|
slotId: resolvedSlotId,
|
|
2107
2250
|
element: el,
|
|
@@ -2118,7 +2261,8 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2118
2261
|
viewableFired: false,
|
|
2119
2262
|
dwellTimer: null,
|
|
2120
2263
|
dwellStartedAt: null,
|
|
2121
|
-
thresholdListeners: listeners
|
|
2264
|
+
thresholdListeners: listeners,
|
|
2265
|
+
viewableListeners
|
|
2122
2266
|
};
|
|
2123
2267
|
this.slotViewabilityRecords.set(resolvedSlotId, record);
|
|
2124
2268
|
if (el) {
|
|
@@ -2143,6 +2287,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2143
2287
|
}
|
|
2144
2288
|
}
|
|
2145
2289
|
record.thresholdListeners = /* @__PURE__ */ new Set();
|
|
2290
|
+
record.viewableListeners = /* @__PURE__ */ new Set();
|
|
2146
2291
|
if (record.element && this.intersectionObserver) {
|
|
2147
2292
|
this.intersectionObserver.unobserve(record.element);
|
|
2148
2293
|
this.elementToSlotId.delete(record.element);
|
|
@@ -2157,6 +2302,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2157
2302
|
this.slotRefreshIndices.delete(slotId);
|
|
2158
2303
|
this.slotLastAuctionIds.delete(slotId);
|
|
2159
2304
|
this.pendingThresholdListeners.delete(slotId);
|
|
2305
|
+
this.pendingViewableListeners.delete(slotId);
|
|
2160
2306
|
const prefix = `${escapeKeyPart(slotId)}:`;
|
|
2161
2307
|
for (const key of Array.from(this.slotEmittedImpressionKeys)) {
|
|
2162
2308
|
if (key === slotId || key.startsWith(prefix)) {
|
|
@@ -2431,19 +2577,21 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2431
2577
|
if (!adsManager.__bidkernelAttachedInstances) {
|
|
2432
2578
|
try {
|
|
2433
2579
|
Object.defineProperty(adsManager, "__bidkernelAttachedInstances", {
|
|
2434
|
-
value: /* @__PURE__ */ new
|
|
2580
|
+
value: /* @__PURE__ */ new Map(),
|
|
2435
2581
|
configurable: true,
|
|
2436
2582
|
writable: true
|
|
2437
2583
|
});
|
|
2438
2584
|
} catch {
|
|
2439
|
-
adsManager.__bidkernelAttachedInstances = /* @__PURE__ */ new
|
|
2585
|
+
adsManager.__bidkernelAttachedInstances = /* @__PURE__ */ new Map();
|
|
2440
2586
|
}
|
|
2441
2587
|
}
|
|
2442
|
-
|
|
2443
|
-
|
|
2588
|
+
const attached = adsManager.__bidkernelAttachedInstances;
|
|
2589
|
+
const previous = attached.get(this);
|
|
2590
|
+
if (previous) {
|
|
2591
|
+
if (!options) return () => {
|
|
2444
2592
|
};
|
|
2593
|
+
previous();
|
|
2445
2594
|
}
|
|
2446
|
-
adsManager.__bidkernelAttachedInstances.add(this);
|
|
2447
2595
|
const slotId = options?.slotId || options?.adUnitCode || "video";
|
|
2448
2596
|
const adUnitCode = options?.adUnitCode || slotId;
|
|
2449
2597
|
const auctionId = options?.auctionId || "";
|
|
@@ -2591,7 +2739,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2591
2739
|
}
|
|
2592
2740
|
}
|
|
2593
2741
|
const cleanup = () => {
|
|
2594
|
-
|
|
2742
|
+
attached.delete(this);
|
|
2595
2743
|
for (const { type, handler } of listeners) {
|
|
2596
2744
|
try {
|
|
2597
2745
|
adsManager.removeEventListener(type, handler);
|
|
@@ -2600,6 +2748,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
2600
2748
|
}
|
|
2601
2749
|
this.videoDetachCleanups.delete(cleanup);
|
|
2602
2750
|
};
|
|
2751
|
+
attached.set(this, cleanup);
|
|
2603
2752
|
this.videoDetachCleanups.add(cleanup);
|
|
2604
2753
|
return cleanup;
|
|
2605
2754
|
}
|
|
@@ -3227,9 +3376,9 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
3227
3376
|
);
|
|
3228
3377
|
}
|
|
3229
3378
|
if (this.config.viewabilityEnabled && typeof document !== "undefined") {
|
|
3230
|
-
let el = null;
|
|
3379
|
+
let el = adUnitCode && this.slotElements.get(adUnitCode) || null;
|
|
3231
3380
|
const targetId = adUnitCode || data.adId || bid.adId;
|
|
3232
|
-
if (targetId) {
|
|
3381
|
+
if (!el && targetId) {
|
|
3233
3382
|
el = document.getElementById(targetId);
|
|
3234
3383
|
}
|
|
3235
3384
|
if (!el && typeof window !== "undefined" && window.googletag?.pubads) {
|
|
@@ -3490,6 +3639,7 @@ var BidkernelPrebidAnalytics = class _BidkernelPrebidAnalytics {
|
|
|
3490
3639
|
return `${PENDING_BATCH_KEY_PREFIX}${encodeURIComponent(this.config.propertyId)}_`;
|
|
3491
3640
|
}
|
|
3492
3641
|
persistBatch(encoded) {
|
|
3642
|
+
if (!storageAllowed) return null;
|
|
3493
3643
|
try {
|
|
3494
3644
|
if (typeof localStorage === "undefined") return null;
|
|
3495
3645
|
if (!this.config.propertyId) return null;
|
|
@@ -3742,6 +3892,7 @@ function getbidkernel(alias = "bidkernel") {
|
|
|
3742
3892
|
0 && (module.exports = {
|
|
3743
3893
|
BidkernelPrebidAnalytics,
|
|
3744
3894
|
PrebidEventDeduper,
|
|
3895
|
+
getOrCreateSessionId,
|
|
3745
3896
|
getPrebidEventKey,
|
|
3746
3897
|
getbidkernel,
|
|
3747
3898
|
hookImaPrototype,
|