@highfivve/ad-tag 5.5.4
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/LICENSE +201 -0
- package/README.md +149 -0
- package/lib/ads/a9.js +190 -0
- package/lib/ads/adPipeline.js +159 -0
- package/lib/ads/adService.js +251 -0
- package/lib/ads/adUnitPath.js +37 -0
- package/lib/ads/auctions/adRequestThrottling.js +22 -0
- package/lib/ads/auctions/biddersDisabling.js +90 -0
- package/lib/ads/auctions/frequencyCapping.js +189 -0
- package/lib/ads/auctions/interstitialContext.js +92 -0
- package/lib/ads/auctions/previousBidCpms.js +33 -0
- package/lib/ads/auctions/resume.js +3 -0
- package/lib/ads/bridge/bridge.js +62 -0
- package/lib/ads/consent.js +63 -0
- package/lib/ads/eventService.js +44 -0
- package/lib/ads/globalAuctionContext.js +98 -0
- package/lib/ads/googleAdManager.js +380 -0
- package/lib/ads/keyValues.js +1 -0
- package/lib/ads/labelConfigService.js +42 -0
- package/lib/ads/modules/ad-reload/adVisibilityService.js +158 -0
- package/lib/ads/modules/ad-reload/index.js +163 -0
- package/lib/ads/modules/ad-reload/userActivityService.js +70 -0
- package/lib/ads/modules/adex/adex-mapping.js +77 -0
- package/lib/ads/modules/adex/adexUtiq.js +15 -0
- package/lib/ads/modules/adex/index.js +142 -0
- package/lib/ads/modules/adex/sendAdvertisingId.js +20 -0
- package/lib/ads/modules/blocklist-url/index.js +118 -0
- package/lib/ads/modules/cleanup/index.js +93 -0
- package/lib/ads/modules/confiant/index.js +47 -0
- package/lib/ads/modules/emetriq/index.js +154 -0
- package/lib/ads/modules/emetriq/trackInApp.js +34 -0
- package/lib/ads/modules/emetriq/trackLoginEvent.js +59 -0
- package/lib/ads/modules/generic-skin/index.js +150 -0
- package/lib/ads/modules/identitylink/index.js +58 -0
- package/lib/ads/modules/interstitial/index.js +38 -0
- package/lib/ads/modules/interstitial/interstitialAd.js +111 -0
- package/lib/ads/modules/lazy-load/index.js +191 -0
- package/lib/ads/modules/lazy-load/selectInfiniteSlot.js +11 -0
- package/lib/ads/modules/prebid-first-party-data/index.js +115 -0
- package/lib/ads/modules/pubstack/index.js +59 -0
- package/lib/ads/modules/sticky-footer-ad/desktopFloorAd.js +63 -0
- package/lib/ads/modules/sticky-footer-ad/index.js +43 -0
- package/lib/ads/modules/sticky-footer-ad/mobileSticky.js +93 -0
- package/lib/ads/modules/sticky-footer-ad-v2/footerStickyAd.js +118 -0
- package/lib/ads/modules/sticky-footer-ad-v2/index.js +43 -0
- package/lib/ads/modules/sticky-header-ad/fadeOutCallback.js +25 -0
- package/lib/ads/modules/sticky-header-ad/index.js +103 -0
- package/lib/ads/modules/sticky-header-ad/renderResult.js +24 -0
- package/lib/ads/modules/utiq/index.js +79 -0
- package/lib/ads/modules/yield-optimization/dynamicFloorPrice.js +86 -0
- package/lib/ads/modules/yield-optimization/index.js +57 -0
- package/lib/ads/modules/yield-optimization/isYieldOptimizationConfigDynamic.js +6 -0
- package/lib/ads/modules/yield-optimization/yieldOptimizationService.js +169 -0
- package/lib/ads/modules/zeotap/index.js +111 -0
- package/lib/ads/moli.js +645 -0
- package/lib/ads/moliGlobal.js +11 -0
- package/lib/ads/prebid-outstream.js +13 -0
- package/lib/ads/prebid.js +406 -0
- package/lib/ads/sizeConfigService.js +49 -0
- package/lib/ads/spa.js +32 -0
- package/lib/bundle/adReload.js +2 -0
- package/lib/bundle/adex.js +2 -0
- package/lib/bundle/blocklistUrls.js +2 -0
- package/lib/bundle/cleanup.js +2 -0
- package/lib/bundle/confiant.js +2 -0
- package/lib/bundle/configureFromEndpoint.js +40 -0
- package/lib/bundle/emetriq.js +2 -0
- package/lib/bundle/identityLink.js +2 -0
- package/lib/bundle/init.js +2 -0
- package/lib/bundle/interstitialModule.js +2 -0
- package/lib/bundle/lazyLoad.js +2 -0
- package/lib/bundle/prebidFirstPartyData.js +2 -0
- package/lib/bundle/pubstack.js +2 -0
- package/lib/bundle/skin.js +2 -0
- package/lib/bundle/stickyFooterAd.js +2 -0
- package/lib/bundle/stickyFooterAds2.js +2 -0
- package/lib/bundle/stickyHeaderAd.js +2 -0
- package/lib/bundle/utiq.js +2 -0
- package/lib/bundle/yieldOptimization.js +2 -0
- package/lib/bundle/zeotap.js +2 -0
- package/lib/console/components/adSlotConfig.js +225 -0
- package/lib/console/components/consentConfig.js +138 -0
- package/lib/console/components/globalConfig.js +634 -0
- package/lib/console/components/labelConfigDebug.js +19 -0
- package/lib/console/components/sizeConfigDebug.js +34 -0
- package/lib/console/components/tag.js +4 -0
- package/lib/console/debug.js +38 -0
- package/lib/console/util/array.js +1 -0
- package/lib/console/util/calculateAdDensity.js +36 -0
- package/lib/console/util/debounce.js +12 -0
- package/lib/console/util/debugLogger.js +6 -0
- package/lib/console/util/extractPositionFromPath.js +8 -0
- package/lib/console/util/prebid.js +3 -0
- package/lib/console/util/stringUtils.js +23 -0
- package/lib/console/util/themingService.js +31 -0
- package/lib/console/util/windowResizeService.js +22 -0
- package/lib/console/validations/adReloadValidations.js +47 -0
- package/lib/console/validations/bucketValidations.js +78 -0
- package/lib/console/validations/sizesConfigValidations.js +97 -0
- package/lib/gen/packageJson.js +3 -0
- package/lib/index.js +3 -0
- package/lib/types/apstag.js +1 -0
- package/lib/types/dom.js +1 -0
- package/lib/types/emetriq.js +1 -0
- package/lib/types/googletag.js +1 -0
- package/lib/types/identitylink.js +1 -0
- package/lib/types/module.js +1 -0
- package/lib/types/moliConfig.js +1 -0
- package/lib/types/moliRuntime.js +1 -0
- package/lib/types/prebidjs.js +45 -0
- package/lib/types/supplyChainObject.js +1 -0
- package/lib/types/tcfapi.js +48 -0
- package/lib/util/addNewInfiniteSlotToConfig.js +11 -0
- package/lib/util/arrayUtils.js +9 -0
- package/lib/util/assetLoaderService.js +91 -0
- package/lib/util/browserStorageKeys.js +7 -0
- package/lib/util/debugDelay.js +12 -0
- package/lib/util/domready.js +15 -0
- package/lib/util/environmentOverride.js +16 -0
- package/lib/util/extractAdTagVersion.js +16 -0
- package/lib/util/extractTopPrivateDomainFromHostname.js +17 -0
- package/lib/util/localStorage.js +26 -0
- package/lib/util/logging.js +106 -0
- package/lib/util/objectUtils.js +29 -0
- package/lib/util/query.js +40 -0
- package/lib/util/queryParameters.js +5 -0
- package/lib/util/resolveOverrides.js +21 -0
- package/lib/util/resolveStoredRequestIdInOrtb2Object.js +12 -0
- package/lib/util/sizes.js +3 -0
- package/lib/util/test-slots.js +150 -0
- package/lib/util/uuid.js +10 -0
- package/package.json +127 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { remainingTime } from './resume';
|
|
2
|
+
import { resolveAdUnitPath } from '../adUnitPath';
|
|
3
|
+
import { formatKey } from 'ad-tag/ads/keyValues';
|
|
4
|
+
import { isGamInterstitial } from 'ad-tag/ads/auctions/interstitialContext';
|
|
5
|
+
const sessionStorageKey = 'h5v-fc';
|
|
6
|
+
const hasPacingInterval = (config) => !!config.conditions && !!config.conditions.pacingInterval;
|
|
7
|
+
export const createFrequencyCapping = (config, _window, now, logger) => {
|
|
8
|
+
const positionImpSchedules = new Map();
|
|
9
|
+
const positionLastImpressionNumberOfAdRequests = new Map();
|
|
10
|
+
const bidderImpSchedules = new Map();
|
|
11
|
+
const positionAdRequests = new Map();
|
|
12
|
+
let numAdRequests = 0;
|
|
13
|
+
const pacingIntervalConfigs = config.bidders?.filter(hasPacingInterval) ?? [];
|
|
14
|
+
const bidWonConfigs = pacingIntervalConfigs.filter(config => !config.conditions.pacingInterval?.events ||
|
|
15
|
+
config.conditions.pacingInterval?.events?.includes('bidWon')) ?? [];
|
|
16
|
+
const bidRequestedConfigs = pacingIntervalConfigs.filter(config => config.conditions.pacingInterval?.events?.includes('bidRequested')) ?? [];
|
|
17
|
+
let resolvedAdUnitPathPositionConfigs = config.positions || [];
|
|
18
|
+
const persist = () => {
|
|
19
|
+
if (config.persistent === true) {
|
|
20
|
+
const data = {
|
|
21
|
+
bCaps: Array.from(bidderImpSchedules.entries()).reduce((acc, [key, schedules]) => ({ ...acc, [key]: schedules }), {}),
|
|
22
|
+
pCaps: Array.from(positionImpSchedules.entries()).reduce((acc, [adUnitPath, schedules]) => ({ ...acc, [adUnitPath]: schedules }), {}),
|
|
23
|
+
pLastImpAdRequests: Array.from(positionLastImpressionNumberOfAdRequests.entries()).reduce((acc, [adUnitPath, numAdRequests]) => ({ ...acc, [adUnitPath]: numAdRequests }), {}),
|
|
24
|
+
requestAds: numAdRequests
|
|
25
|
+
};
|
|
26
|
+
_window.sessionStorage.setItem(sessionStorageKey, JSON.stringify(data));
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const cap = (startTimestamp, config, intervalInMs, bid) => {
|
|
30
|
+
const bidders = config.bidders;
|
|
31
|
+
if ((!bidders || bidders.includes(bid.bidder)) && config.domId === bid.adUnitCode) {
|
|
32
|
+
const key = `${bid.adUnitCode}:${bid.bidder}`;
|
|
33
|
+
const currentSchedules = bidderImpSchedules.get(key) ?? [];
|
|
34
|
+
currentSchedules.push({
|
|
35
|
+
ts: startTimestamp,
|
|
36
|
+
wait: intervalInMs
|
|
37
|
+
});
|
|
38
|
+
bidderImpSchedules.set(key, currentSchedules);
|
|
39
|
+
_window.setTimeout(() => {
|
|
40
|
+
logger.debug('fc', `removing ${key} at ${startTimestamp}`);
|
|
41
|
+
const schedules = bidderImpSchedules.get(key);
|
|
42
|
+
if (schedules) {
|
|
43
|
+
bidderImpSchedules.set(key, schedules.filter(s => s.ts !== startTimestamp));
|
|
44
|
+
}
|
|
45
|
+
}, intervalInMs);
|
|
46
|
+
persist();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const capPosition = (startTimestamp, adUnitPath, pacingInterval) => {
|
|
50
|
+
const currentSchedules = positionImpSchedules.get(adUnitPath) ?? [];
|
|
51
|
+
currentSchedules.push({ ts: startTimestamp, wait: pacingInterval.intervalInMs });
|
|
52
|
+
positionImpSchedules.set(adUnitPath, currentSchedules);
|
|
53
|
+
_window.setTimeout(() => {
|
|
54
|
+
const schedules = positionImpSchedules.get(adUnitPath);
|
|
55
|
+
if (schedules) {
|
|
56
|
+
positionImpSchedules.set(adUnitPath, schedules.filter(s => s.ts !== startTimestamp));
|
|
57
|
+
}
|
|
58
|
+
}, pacingInterval.intervalInMs);
|
|
59
|
+
persist();
|
|
60
|
+
};
|
|
61
|
+
const onSlotRenderEndedOrImpressionViewable = (adUnitPath) => {
|
|
62
|
+
resolvedAdUnitPathPositionConfigs
|
|
63
|
+
.filter(config => config.adUnitPath === adUnitPath)
|
|
64
|
+
.forEach(config => {
|
|
65
|
+
if (config.conditions.pacingInterval) {
|
|
66
|
+
capPosition(now(), adUnitPath, config.conditions.pacingInterval);
|
|
67
|
+
}
|
|
68
|
+
if (config.conditions.pacingRequestAds) {
|
|
69
|
+
positionLastImpressionNumberOfAdRequests.set(adUnitPath, numAdRequests);
|
|
70
|
+
persist();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
if (config.persistent === true) {
|
|
75
|
+
const storedData = _window.sessionStorage.getItem(sessionStorageKey);
|
|
76
|
+
if (storedData) {
|
|
77
|
+
try {
|
|
78
|
+
const parsedData = JSON.parse(storedData);
|
|
79
|
+
numAdRequests = parsedData.requestAds;
|
|
80
|
+
Object.entries(parsedData.bCaps).forEach(([adUnitBidderKey, schedules]) => {
|
|
81
|
+
schedules?.forEach(schedule => {
|
|
82
|
+
const [adUnitCode, bidder] = adUnitBidderKey.split(':');
|
|
83
|
+
const blockedForMs = remainingTime(schedule, now());
|
|
84
|
+
cap(now(), { domId: adUnitCode, bidders: [bidder] }, blockedForMs, {
|
|
85
|
+
bidder,
|
|
86
|
+
adUnitCode
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
Object.entries(parsedData.pCaps).forEach(([adUnitPath, schedules]) => {
|
|
91
|
+
schedules?.forEach(schedule => {
|
|
92
|
+
const blockedForMs = remainingTime(schedule, now());
|
|
93
|
+
capPosition(schedule.ts, adUnitPath, { intervalInMs: blockedForMs });
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
logger.error('fc', 'failed to parse fc state', e);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
onSlotRequested(event) {
|
|
104
|
+
resolvedAdUnitPathPositionConfigs
|
|
105
|
+
.filter(config => config.adUnitPath === event.slot.getAdUnitPath())
|
|
106
|
+
.forEach(config => {
|
|
107
|
+
if (config.conditions.adRequestLimit) {
|
|
108
|
+
const adUnitPath = config.adUnitPath;
|
|
109
|
+
const currentAdRequests = positionAdRequests.get(adUnitPath) ?? 0;
|
|
110
|
+
positionAdRequests.set(adUnitPath, currentAdRequests + 1);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
onAuctionEnd(auction) {
|
|
115
|
+
bidRequestedConfigs.forEach(config => {
|
|
116
|
+
auction.bidderRequests?.forEach(bidderRequests => {
|
|
117
|
+
bidderRequests?.bids?.forEach(bid => {
|
|
118
|
+
cap(now(), config, config.conditions.pacingInterval.intervalInMs, bid);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
persist();
|
|
123
|
+
},
|
|
124
|
+
onBidWon(bid) {
|
|
125
|
+
bidWonConfigs.forEach(config => cap(now(), config, config.conditions.pacingInterval.intervalInMs, bid));
|
|
126
|
+
persist();
|
|
127
|
+
},
|
|
128
|
+
onSlotRenderEnded(event) {
|
|
129
|
+
const [format] = event.slot.getTargeting(formatKey);
|
|
130
|
+
if (!event.isEmpty &&
|
|
131
|
+
format !== 5..toString()) {
|
|
132
|
+
onSlotRenderEndedOrImpressionViewable(event.slot.getAdUnitPath());
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
onImpressionViewable(event) {
|
|
136
|
+
const [format] = event.slot.getTargeting(formatKey);
|
|
137
|
+
if (format === 5..toString()) {
|
|
138
|
+
onSlotRenderEndedOrImpressionViewable(event.slot.getAdUnitPath());
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
beforeRequestAds() {
|
|
142
|
+
positionAdRequests.clear();
|
|
143
|
+
},
|
|
144
|
+
afterRequestAds() {
|
|
145
|
+
numAdRequests++;
|
|
146
|
+
persist();
|
|
147
|
+
},
|
|
148
|
+
updateAdUnitPaths(adUnitPathVariables) {
|
|
149
|
+
resolvedAdUnitPathPositionConfigs = (config.positions ?? []).map(config => {
|
|
150
|
+
return { ...config, adUnitPath: resolveAdUnitPath(config.adUnitPath, adUnitPathVariables) };
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
isAdUnitCapped(slot) {
|
|
154
|
+
const adUnitPath = slot.getAdUnitPath();
|
|
155
|
+
const isGamInst = isGamInterstitial(slot, _window);
|
|
156
|
+
return resolvedAdUnitPathPositionConfigs
|
|
157
|
+
.filter(config => config.adUnitPath === adUnitPath)
|
|
158
|
+
.some(positionConfig => {
|
|
159
|
+
return ((positionConfig.adUnitPath === adUnitPath &&
|
|
160
|
+
positionConfig.conditions.delay &&
|
|
161
|
+
numAdRequests <
|
|
162
|
+
positionConfig.conditions.delay.minRequestAds - (isGamInst ? 1 : 0)) ||
|
|
163
|
+
(positionConfig.conditions.pacingRequestAds &&
|
|
164
|
+
positionLastImpressionNumberOfAdRequests.has(adUnitPath) &&
|
|
165
|
+
numAdRequests - (positionLastImpressionNumberOfAdRequests.get(adUnitPath) ?? 0) <
|
|
166
|
+
positionConfig.conditions.pacingRequestAds.requestAds) ||
|
|
167
|
+
(positionConfig.conditions.pacingInterval &&
|
|
168
|
+
(positionImpSchedules.get(adUnitPath) ?? []).length >=
|
|
169
|
+
positionConfig.conditions.pacingInterval.maxImpressions) ||
|
|
170
|
+
(positionConfig.conditions.adRequestLimit &&
|
|
171
|
+
(positionAdRequests.get(adUnitPath) ?? 0) >=
|
|
172
|
+
positionConfig.conditions.adRequestLimit.maxAdRequests));
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
isBidderCapped(slotId, bidder) {
|
|
176
|
+
if (!config.bidders) {
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
return config.bidders
|
|
180
|
+
.filter(c => c.domId === slotId && (!c.bidders || c.bidders.includes(bidder)))
|
|
181
|
+
.some(({ conditions: { pacingInterval, delay } }) => {
|
|
182
|
+
return ((pacingInterval &&
|
|
183
|
+
(bidderImpSchedules.get(`${slotId}:${bidder}`) ?? []).length >=
|
|
184
|
+
pacingInterval.maxImpressions) ||
|
|
185
|
+
(delay && numAdRequests < delay.minRequestAds));
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { resolveAdUnitPath } from 'ad-tag/ads/adUnitPath';
|
|
2
|
+
import { formatKey } from 'ad-tag/ads/keyValues';
|
|
3
|
+
export const isGamInterstitial = (slot, window) => {
|
|
4
|
+
const [value] = slot.getTargeting(formatKey);
|
|
5
|
+
return !!value && value === window.googletag.enums.OutOfPageFormat.INTERSTITIAL.toString();
|
|
6
|
+
};
|
|
7
|
+
export const createInterstitialContext = (config, window__, now, logger) => {
|
|
8
|
+
const sessionStorageKey = 'h5v_intstl';
|
|
9
|
+
const sessionStorageTimeToLive = config.ttlStorage ?? 30 * 60 * 1000;
|
|
10
|
+
const currentTime = now();
|
|
11
|
+
let interstitialAdUnitPath = config.adUnitPath;
|
|
12
|
+
let currentInterstitialState = {
|
|
13
|
+
priority: config.priority,
|
|
14
|
+
updatedAt: currentTime
|
|
15
|
+
};
|
|
16
|
+
try {
|
|
17
|
+
const sessionState = window__.sessionStorage.getItem(sessionStorageKey);
|
|
18
|
+
if (sessionState) {
|
|
19
|
+
const parsedState = JSON.parse(sessionState);
|
|
20
|
+
if (currentTime - parsedState.updatedAt < sessionStorageTimeToLive) {
|
|
21
|
+
currentInterstitialState = parsedState;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
currentInterstitialState.updatedAt = currentTime;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
logger.error('interstitial', 'failed to load interstitial state from session storage', e);
|
|
30
|
+
}
|
|
31
|
+
if (config.priority.length === 0) {
|
|
32
|
+
logger.error('interstitial', 'no interstitial priority configured');
|
|
33
|
+
}
|
|
34
|
+
const persistInterstitialState = () => {
|
|
35
|
+
currentInterstitialState.updatedAt = now();
|
|
36
|
+
try {
|
|
37
|
+
window.sessionStorage.setItem(sessionStorageKey, JSON.stringify(currentInterstitialState));
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
logger.error('interstitial', 'failed to persist interstitial state to session storage', e);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const shiftDemandPriority = (arr) => {
|
|
44
|
+
if (arr.length === 0) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
return [...arr.slice(1), arr[0]];
|
|
48
|
+
};
|
|
49
|
+
const onSlotRenderEnded = (event) => {
|
|
50
|
+
if (event.slot.getAdUnitPath() !== interstitialAdUnitPath) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (event.isEmpty) {
|
|
54
|
+
currentInterstitialState.priority = shiftDemandPriority(currentInterstitialState.priority);
|
|
55
|
+
persistInterstitialState();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const onImpressionViewable = (event) => {
|
|
59
|
+
if (event.slot.getAdUnitPath() !== interstitialAdUnitPath) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (isGamInterstitial(event.slot, window__)) {
|
|
63
|
+
currentInterstitialState.priority = shiftDemandPriority(currentInterstitialState.priority);
|
|
64
|
+
persistInterstitialState();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const onAuctionEnd = (event) => {
|
|
68
|
+
if (!event.adUnitCodes.includes(config.domId)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const interstitialBids = event.bidsReceived?.filter(bid => bid.adUnitCode === config.domId);
|
|
72
|
+
if ((interstitialBids?.length ?? 0) === 0) {
|
|
73
|
+
currentInterstitialState.priority = shiftDemandPriority(currentInterstitialState.priority);
|
|
74
|
+
persistInterstitialState();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const interstitialChannel = () => {
|
|
78
|
+
return currentInterstitialState.priority[0];
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
interstitialChannel,
|
|
82
|
+
onSlotRenderEnded,
|
|
83
|
+
onImpressionViewable,
|
|
84
|
+
onAuctionEnd,
|
|
85
|
+
interstitialState: () => {
|
|
86
|
+
return currentInterstitialState;
|
|
87
|
+
},
|
|
88
|
+
updateAdUnitPaths: (variables) => {
|
|
89
|
+
interstitialAdUnitPath = resolveAdUnitPath(config.adUnitPath, variables);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const createPreviousBidCpms = () => {
|
|
2
|
+
const lastBidCpms = new Map();
|
|
3
|
+
const groupReceivedBidsByAdUnitCode = (bidsReceived) => {
|
|
4
|
+
return bidsReceived.reduce((result, current) => {
|
|
5
|
+
const { adUnitCode, cpm } = current;
|
|
6
|
+
if (!result[adUnitCode]) {
|
|
7
|
+
result[adUnitCode] = [];
|
|
8
|
+
}
|
|
9
|
+
result[adUnitCode].push(cpm);
|
|
10
|
+
return result;
|
|
11
|
+
}, {});
|
|
12
|
+
};
|
|
13
|
+
const onAuctionEnd = (bidsReceived) => {
|
|
14
|
+
const bidsOfLastAuction = groupReceivedBidsByAdUnitCode(bidsReceived);
|
|
15
|
+
lastBidCpms.forEach((_, key) => {
|
|
16
|
+
if (!bidsOfLastAuction[key]) {
|
|
17
|
+
lastBidCpms.delete(key);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
Object.entries(bidsOfLastAuction).forEach(([adUnitCode, cpmsOfLastAuction]) => {
|
|
21
|
+
const earlierCpmsOnPosition = getLastBidCpms(adUnitCode) ?? [];
|
|
22
|
+
lastBidCpms.set(adUnitCode, Array.from(new Set([...cpmsOfLastAuction, ...earlierCpmsOnPosition])));
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
const getLastBidCpms = (adUnitCode) => {
|
|
26
|
+
return lastBidCpms.get(adUnitCode) ?? null;
|
|
27
|
+
};
|
|
28
|
+
return {
|
|
29
|
+
groupReceivedBidsByAdUnitCode,
|
|
30
|
+
onAuctionEnd,
|
|
31
|
+
getLastBidCpms
|
|
32
|
+
};
|
|
33
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { mkInitStep } from 'ad-tag/ads/adPipeline';
|
|
2
|
+
import { isPlainObject } from 'ad-tag/util/objectUtils';
|
|
3
|
+
const findAdSlot = (message, googletag) => googletag
|
|
4
|
+
.pubads()
|
|
5
|
+
.getSlots()
|
|
6
|
+
.find(slot => slot.getSlotElementId() === message.domId || slot.getAdUnitPath() === message.adUnitPath);
|
|
7
|
+
const parseMessageData = (data) => {
|
|
8
|
+
try {
|
|
9
|
+
const message = typeof data === 'string' ? JSON.parse(data) : isPlainObject(data) ? data : null;
|
|
10
|
+
switch (message?.event) {
|
|
11
|
+
case 'h5.adunit.passback':
|
|
12
|
+
return message.passbackOrigin && (message.domId || message.adUnitPath) ? message : null;
|
|
13
|
+
case 'h5.adunit.refresh':
|
|
14
|
+
return message;
|
|
15
|
+
default:
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const handleRefresh = (message, context) => {
|
|
24
|
+
const backfillMoliSlot = context.config__.slots.find(slot => slot.domId === message.domId && slot.behaviour.loaded === 'backfill');
|
|
25
|
+
const adSlot = findAdSlot(message, context.window__.googletag);
|
|
26
|
+
if (backfillMoliSlot && adSlot) {
|
|
27
|
+
context.logger__.debug('bridge', `Refresh ad slot ${message.domId}`);
|
|
28
|
+
context.window__.googletag.destroySlots([adSlot]);
|
|
29
|
+
context.window__.moli
|
|
30
|
+
.refreshAdSlot(message.domId, { loaded: 'backfill' })
|
|
31
|
+
.catch(e => context.logger__.error('bridge', `failed to refresh ${message.domId}`, e));
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const handlePassback = (message, context) => {
|
|
35
|
+
const adSlot = findAdSlot(message, context.window__.googletag);
|
|
36
|
+
const passbackKey = 'passback';
|
|
37
|
+
if (adSlot && adSlot.getTargeting(passbackKey).length === 0) {
|
|
38
|
+
context.logger__.debug('passback', `Process passback for ad slot ${adSlot.getAdUnitPath()}`);
|
|
39
|
+
adSlot.setTargeting(passbackKey, 'true');
|
|
40
|
+
adSlot.setTargeting('passbackOrigin', message.passbackOrigin);
|
|
41
|
+
context.window__.googletag.pubads().refresh([adSlot]);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
export const bridgeInitStep = () => mkInitStep('bridge', (context) => {
|
|
45
|
+
if (context.config__.bridge?.enabled) {
|
|
46
|
+
context.logger__.debug('bridge', 'add message listener');
|
|
47
|
+
context.window__.addEventListener('message', (event) => {
|
|
48
|
+
const message = parseMessageData(event.data);
|
|
49
|
+
if (message) {
|
|
50
|
+
switch (message.event) {
|
|
51
|
+
case 'h5.adunit.passback':
|
|
52
|
+
handlePassback(message, context);
|
|
53
|
+
break;
|
|
54
|
+
case 'h5.adunit.refresh':
|
|
55
|
+
handleRefresh(message, context);
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return Promise.resolve();
|
|
62
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { tcfapi } from '../types/tcfapi';
|
|
2
|
+
var EventStatus = tcfapi.status.EventStatus;
|
|
3
|
+
var CmpStatus = tcfapi.status.CmpStatus;
|
|
4
|
+
const allPurposes = [
|
|
5
|
+
tcfapi.responses.TCPurpose.STORE_INFORMATION_ON_DEVICE,
|
|
6
|
+
tcfapi.responses.TCPurpose.SELECT_BASIC_ADS,
|
|
7
|
+
tcfapi.responses.TCPurpose.CREATE_PERSONALISED_ADS_PROFILE,
|
|
8
|
+
tcfapi.responses.TCPurpose.SELECT_PERSONALISED_ADS,
|
|
9
|
+
tcfapi.responses.TCPurpose.CREATE_PERSONALISED_CONTENT_PROFILE,
|
|
10
|
+
tcfapi.responses.TCPurpose.SELECT_PERSONALISED_CONTENT,
|
|
11
|
+
tcfapi.responses.TCPurpose.MEASURE_AD_PERFORMANCE,
|
|
12
|
+
tcfapi.responses.TCPurpose.MEASURE_CONTENT_PERFORMANCE,
|
|
13
|
+
tcfapi.responses.TCPurpose.APPLY_MARKET_RESEARCH,
|
|
14
|
+
tcfapi.responses.TCPurpose.DEVELOP_IMPROVE_PRODUCTS
|
|
15
|
+
];
|
|
16
|
+
export const missingPurposeConsent = (tcData) => {
|
|
17
|
+
return (!!tcData.gdprApplies &&
|
|
18
|
+
allPurposes.some(p => !tcData.purpose.consents[p]));
|
|
19
|
+
};
|
|
20
|
+
export const consentReady = (consentConfig, window, log, env) => {
|
|
21
|
+
if (env === 'test') {
|
|
22
|
+
log.info('gdprApplies is set to false in test mode!');
|
|
23
|
+
}
|
|
24
|
+
if (env === 'test' || consentConfig.enabled === false) {
|
|
25
|
+
return Promise.resolve({
|
|
26
|
+
gdprApplies: false,
|
|
27
|
+
eventStatus: EventStatus.TC_LOADED,
|
|
28
|
+
cmpStatus: CmpStatus.LOADED,
|
|
29
|
+
cmpId: 0,
|
|
30
|
+
cmpVersion: 0,
|
|
31
|
+
tcfPolicyVersion: undefined,
|
|
32
|
+
listenerId: null
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
if (window.__tcfapi) {
|
|
37
|
+
const listener = (tcData) => {
|
|
38
|
+
if (tcData.cmpStatus === 'error') {
|
|
39
|
+
reject(tcData);
|
|
40
|
+
}
|
|
41
|
+
else if (tcData.eventStatus === 'useractioncomplete' ||
|
|
42
|
+
tcData.eventStatus === 'tcloaded') {
|
|
43
|
+
log.debug('Consent', 'consent ready', tcData);
|
|
44
|
+
if (consentConfig.disableLegitimateInterest && missingPurposeConsent(tcData)) {
|
|
45
|
+
reject('user consent is missing for some purposes');
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
resolve(tcData);
|
|
49
|
+
}
|
|
50
|
+
if (tcData.listenerId) {
|
|
51
|
+
window.__tcfapi('removeEventListener', 2, () => {
|
|
52
|
+
return;
|
|
53
|
+
}, tcData.listenerId);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
window.__tcfapi('addEventListener', 2, listener);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
reject('window.__tcfapi is not defined. Make sure that the stub code is inlined in the head tag');
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const createEventService = () => {
|
|
2
|
+
const eventListeners = new Map();
|
|
3
|
+
const oneTimeListeners = new Map();
|
|
4
|
+
return {
|
|
5
|
+
addEventListener(event, listener, options = {}) {
|
|
6
|
+
const targetMap = options.once ? oneTimeListeners : eventListeners;
|
|
7
|
+
if (!targetMap.has(event)) {
|
|
8
|
+
targetMap.set(event, new Set());
|
|
9
|
+
}
|
|
10
|
+
targetMap.get(event)?.add(listener);
|
|
11
|
+
},
|
|
12
|
+
removeEventListener(event, listener) {
|
|
13
|
+
eventListeners.get(event)?.delete(listener);
|
|
14
|
+
oneTimeListeners.get(event)?.delete(listener);
|
|
15
|
+
},
|
|
16
|
+
emit(event, data) {
|
|
17
|
+
eventListeners.get(event)?.forEach(listener => {
|
|
18
|
+
try {
|
|
19
|
+
listener(data);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
console.error(`Error in event listener for ${String(event)}:`, error);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const oneTimeListenersForEvent = oneTimeListeners.get(event);
|
|
26
|
+
if (oneTimeListenersForEvent) {
|
|
27
|
+
oneTimeListenersForEvent.forEach(listener => {
|
|
28
|
+
try {
|
|
29
|
+
listener(data);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error(`Error in one-time event listener for ${String(event)}:`, error);
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
oneTimeListenersForEvent.delete(listener);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
if (oneTimeListenersForEvent.size === 0) {
|
|
39
|
+
oneTimeListeners.delete(event);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createBiddersDisabling } from './auctions/biddersDisabling';
|
|
2
|
+
import { createAdRequestThrottling } from './auctions/adRequestThrottling';
|
|
3
|
+
import { createFrequencyCapping } from './auctions/frequencyCapping';
|
|
4
|
+
import { createPreviousBidCpms } from './auctions/previousBidCpms';
|
|
5
|
+
import { mkConfigureStep } from './adPipeline';
|
|
6
|
+
import { createInterstitialContext } from 'ad-tag/ads/auctions/interstitialContext';
|
|
7
|
+
export const createGlobalAuctionContext = (window, logger, eventService, config = {}) => {
|
|
8
|
+
const biddersDisabling = config.biddersDisabling?.enabled
|
|
9
|
+
? createBiddersDisabling(config.biddersDisabling, window)
|
|
10
|
+
: undefined;
|
|
11
|
+
const adRequestThrottling = config.adRequestThrottling?.enabled
|
|
12
|
+
? createAdRequestThrottling(config.adRequestThrottling, window)
|
|
13
|
+
: undefined;
|
|
14
|
+
const frequencyCapping = config.frequencyCap?.enabled
|
|
15
|
+
? createFrequencyCapping(config.frequencyCap, window, window.Date.now, logger)
|
|
16
|
+
: undefined;
|
|
17
|
+
const previousBidCpms = config.previousBidCpms?.enabled ? createPreviousBidCpms() : undefined;
|
|
18
|
+
const interstitial = config.interstitial?.enabled
|
|
19
|
+
? createInterstitialContext(config.interstitial, window, window.Date.now, logger)
|
|
20
|
+
: undefined;
|
|
21
|
+
window.pbjs = window.pbjs || { que: [] };
|
|
22
|
+
window.googletag = window.googletag || {};
|
|
23
|
+
window.googletag.cmd = window.googletag.cmd || [];
|
|
24
|
+
if (config.biddersDisabling?.enabled ||
|
|
25
|
+
config.previousBidCpms?.enabled ||
|
|
26
|
+
config.frequencyCap?.enabled ||
|
|
27
|
+
config.interstitial?.enabled) {
|
|
28
|
+
window.pbjs.que.push(() => {
|
|
29
|
+
window.pbjs.onEvent('auctionEnd', auction => {
|
|
30
|
+
biddersDisabling?.onAuctionEnd(auction);
|
|
31
|
+
interstitial?.onAuctionEnd(auction);
|
|
32
|
+
if (config.previousBidCpms?.enabled && auction.bidsReceived) {
|
|
33
|
+
previousBidCpms?.onAuctionEnd(auction.bidsReceived);
|
|
34
|
+
}
|
|
35
|
+
frequencyCapping?.onAuctionEnd(auction);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
if (config.adRequestThrottling?.enabled || config.frequencyCap?.enabled) {
|
|
40
|
+
window.googletag.cmd.push(() => {
|
|
41
|
+
window.googletag.pubads().addEventListener('slotRequested', event => {
|
|
42
|
+
adRequestThrottling?.onSlotRequested(event);
|
|
43
|
+
frequencyCapping?.onSlotRequested(event);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
if (config.frequencyCap?.enabled) {
|
|
48
|
+
window.pbjs.que.push(() => {
|
|
49
|
+
window.pbjs.onEvent('bidWon', bid => {
|
|
50
|
+
if (config.frequencyCap) {
|
|
51
|
+
frequencyCapping?.onBidWon(bid);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
eventService.addEventListener('beforeRequestAds', () => {
|
|
56
|
+
frequencyCapping?.beforeRequestAds();
|
|
57
|
+
});
|
|
58
|
+
eventService.addEventListener('afterRequestAds', () => {
|
|
59
|
+
frequencyCapping?.afterRequestAds();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (config.frequencyCap?.enabled || config.interstitial?.enabled) {
|
|
63
|
+
window.googletag.cmd.push(() => {
|
|
64
|
+
window.googletag.pubads().addEventListener('slotRenderEnded', event => {
|
|
65
|
+
frequencyCapping?.onSlotRenderEnded(event);
|
|
66
|
+
interstitial?.onSlotRenderEnded(event);
|
|
67
|
+
});
|
|
68
|
+
window.googletag.pubads().addEventListener('impressionViewable', event => {
|
|
69
|
+
frequencyCapping?.onImpressionViewable(event);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
const configureStep = mkConfigureStep('GlobalAuctionContext', context => {
|
|
74
|
+
frequencyCapping?.updateAdUnitPaths(context.adUnitPathVariables__);
|
|
75
|
+
return Promise.resolve();
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
isSlotThrottled(slot) {
|
|
79
|
+
return !!(adRequestThrottling?.isThrottled(slot.getSlotElementId()) ||
|
|
80
|
+
frequencyCapping?.isAdUnitCapped(slot));
|
|
81
|
+
},
|
|
82
|
+
isBidderFrequencyCappedOnSlot(slotId, bidder) {
|
|
83
|
+
return frequencyCapping?.isBidderCapped(slotId, bidder) ?? false;
|
|
84
|
+
},
|
|
85
|
+
getLastBidCpmsOfAdUnit(slotId) {
|
|
86
|
+
return previousBidCpms?.getLastBidCpms(slotId) ?? [];
|
|
87
|
+
},
|
|
88
|
+
isBidderDisabled(domId, bidder) {
|
|
89
|
+
return biddersDisabling?.isBidderDisabled(domId, bidder) ?? false;
|
|
90
|
+
},
|
|
91
|
+
interstitialChannel: () => {
|
|
92
|
+
return interstitial?.interstitialChannel();
|
|
93
|
+
},
|
|
94
|
+
configureStep() {
|
|
95
|
+
return configureStep;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
};
|