@highfivve/ad-tag 5.9.2 → 5.10.1
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/lib/ads/adService.js +9 -3
- package/lib/ads/auctions/rewardedAdContext.js +250 -0
- package/lib/ads/globalAuctionContext.js +11 -1
- package/lib/ads/modules/ad-reload/index.js +1 -0
- package/lib/ads/modules/adex/index.js +1 -0
- package/lib/ads/modules/blocklist-url/index.js +2 -0
- package/lib/ads/modules/cleanup/index.js +1 -0
- package/lib/ads/modules/confiant/index.js +1 -0
- package/lib/ads/modules/custom/index.js +1 -0
- package/lib/ads/modules/emetriq/index.js +1 -0
- package/lib/ads/modules/generic-skin/index.js +7 -49
- package/lib/ads/modules/geoedge/index.js +1 -0
- package/lib/ads/modules/identitylink/index.js +1 -0
- package/lib/ads/modules/interstitial/index.js +1 -0
- package/lib/ads/modules/lazy-load/index.js +1 -0
- package/lib/ads/modules/moli-analytics/index.js +1 -0
- package/lib/ads/modules/prebid-first-party-data/index.js +1 -0
- package/lib/ads/modules/pubstack/index.js +1 -0
- package/lib/ads/modules/sticky-footer-ad/index.js +1 -0
- package/lib/ads/modules/sticky-footer-ad-v2/index.js +1 -0
- package/lib/ads/modules/sticky-header-ad/index.js +1 -0
- package/lib/ads/modules/utiq/index.js +1 -0
- package/lib/ads/modules/yield-optimization/index.js +1 -0
- package/lib/ads/modules/zeotap/index.js +1 -0
- package/lib/ads/moli.js +39 -5
- package/lib/console/components/consentConfig.js +9 -2
- package/lib/console/tailwind.config.js +4 -1
- package/lib/gen/packageJson.js +1 -1
- package/lib/types/welect.js +1 -0
- package/package.json +4 -4
package/lib/ads/adService.js
CHANGED
|
@@ -56,6 +56,7 @@ export class AdService {
|
|
|
56
56
|
this.window = window;
|
|
57
57
|
this.adPipelineConfig = adPipelineConfig;
|
|
58
58
|
this.requestAdsCalls = 0;
|
|
59
|
+
this.globalAuctionContext = createGlobalAuctionContext(this.window, getDefaultLogger(), this.eventService);
|
|
59
60
|
this.adPipeline = new AdPipeline({
|
|
60
61
|
init: [() => Promise.reject('AdPipeline not initialized yet')],
|
|
61
62
|
configure: [],
|
|
@@ -63,7 +64,7 @@ export class AdService {
|
|
|
63
64
|
prepareRequestAds: [],
|
|
64
65
|
requestBids: [],
|
|
65
66
|
requestAds: () => Promise.resolve()
|
|
66
|
-
}, getDefaultLogger(), this.window,
|
|
67
|
+
}, getDefaultLogger(), this.window, this.globalAuctionContext);
|
|
67
68
|
this.initialize = (config, runtimeConfig, isLabelConditionMet = () => false) => {
|
|
68
69
|
const env = AdService.getEnvironment(runtimeConfig);
|
|
69
70
|
const adServer = config.adServer || 'gam';
|
|
@@ -100,7 +101,8 @@ export class AdService {
|
|
|
100
101
|
prepareRequestAds.push(a9ClearTargetingStep());
|
|
101
102
|
requestBids.push(a9RequestBids(config.a9));
|
|
102
103
|
}
|
|
103
|
-
const globalAuctionContext = createGlobalAuctionContext(this.window, this.logger, this.eventService, config.globalAuctionContext, isLabelConditionMet);
|
|
104
|
+
const globalAuctionContext = createGlobalAuctionContext(this.window, this.logger, this.eventService, config.globalAuctionContext, isLabelConditionMet, this.assetService);
|
|
105
|
+
this.globalAuctionContext = globalAuctionContext;
|
|
104
106
|
configure.push(globalAuctionContext.configureStep());
|
|
105
107
|
init.push(...runtimeConfig.adPipelineConfig.initSteps);
|
|
106
108
|
configure.push(...runtimeConfig.adPipelineConfig.configureSteps);
|
|
@@ -191,6 +193,9 @@ export class AdService {
|
|
|
191
193
|
return Promise.reject(e);
|
|
192
194
|
}
|
|
193
195
|
};
|
|
196
|
+
this.rewardedAd = () => {
|
|
197
|
+
return this.globalAuctionContext.rewardedAd();
|
|
198
|
+
};
|
|
194
199
|
this.getAdPipeline = () => {
|
|
195
200
|
return this.adPipeline;
|
|
196
201
|
};
|
|
@@ -199,7 +204,8 @@ export class AdService {
|
|
|
199
204
|
};
|
|
200
205
|
this.logger = new ProxyLogger(getDefaultLogger());
|
|
201
206
|
if (adPipelineConfig) {
|
|
202
|
-
this.
|
|
207
|
+
this.globalAuctionContext = createGlobalAuctionContext(window, this.logger, this.eventService);
|
|
208
|
+
this.adPipeline = new AdPipeline(adPipelineConfig, this.logger, window, this.globalAuctionContext);
|
|
203
209
|
}
|
|
204
210
|
}
|
|
205
211
|
refreshAdSlots(domIds, config, runtimeConfig, options) {
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { resolveAdUnitPath } from 'ad-tag/ads/adUnitPath';
|
|
2
|
+
import { AssetLoadMethod } from 'ad-tag/util/assetLoaderService';
|
|
3
|
+
const defaultRewardPayload = { amount: 1, type: 'reward' };
|
|
4
|
+
export const createRewardedAdContext = (config, window__, logger, assetLoaderService) => {
|
|
5
|
+
let gamAdUnitPath = config.gam?.adUnitPath;
|
|
6
|
+
let inFlight = false;
|
|
7
|
+
let welectBundle;
|
|
8
|
+
const loadWelectSdk = (welectConfig) => {
|
|
9
|
+
const preloadedApi = window__.Welect;
|
|
10
|
+
if (preloadedApi) {
|
|
11
|
+
return Promise.resolve(preloadedApi);
|
|
12
|
+
}
|
|
13
|
+
welectBundle =
|
|
14
|
+
welectBundle ??
|
|
15
|
+
assetLoaderService
|
|
16
|
+
.loadScript({
|
|
17
|
+
name: 'welect',
|
|
18
|
+
assetUrl: welectConfig.bundleUrl,
|
|
19
|
+
loadMethod: AssetLoadMethod.TAG
|
|
20
|
+
})
|
|
21
|
+
.then(() => {
|
|
22
|
+
const welectApi = window__.Welect;
|
|
23
|
+
return welectApi
|
|
24
|
+
? Promise.resolve(welectApi)
|
|
25
|
+
: Promise.reject(new Error('welect bundle loaded, but window.Welect is not defined'));
|
|
26
|
+
})
|
|
27
|
+
.catch(error => {
|
|
28
|
+
welectBundle = undefined;
|
|
29
|
+
return Promise.reject(error);
|
|
30
|
+
});
|
|
31
|
+
return welectBundle;
|
|
32
|
+
};
|
|
33
|
+
const attemptGam = (gamConfig) => new Promise(resolve => {
|
|
34
|
+
window__.googletag.cmd.push(() => {
|
|
35
|
+
const googletagRef = window__.googletag;
|
|
36
|
+
const pubads = googletagRef.pubads();
|
|
37
|
+
const adUnitPath = gamAdUnitPath ?? gamConfig.adUnitPath;
|
|
38
|
+
const slot = googletagRef.defineOutOfPageSlot(adUnitPath, googletagRef.enums.OutOfPageFormat.REWARDED);
|
|
39
|
+
if (!slot) {
|
|
40
|
+
logger.error('rewardedAd', `failed to define rewarded slot for ${adUnitPath}`);
|
|
41
|
+
resolve({ outcome: 'no-fill' });
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
let settled = false;
|
|
45
|
+
let timeoutId;
|
|
46
|
+
const destroySlot = () => googletagRef.destroySlots([slot]);
|
|
47
|
+
const removeListeners = () => {
|
|
48
|
+
pubads.removeEventListener('slotRenderEnded', onSlotRenderEnded);
|
|
49
|
+
pubads.removeEventListener('rewardedSlotReady', onRewardedSlotReady);
|
|
50
|
+
pubads.removeEventListener('rewardedSlotGranted', onRewardedSlotGranted);
|
|
51
|
+
pubads.removeEventListener('rewardedSlotClosed', onRewardedSlotClosed);
|
|
52
|
+
};
|
|
53
|
+
const settle = (attempt) => {
|
|
54
|
+
if (settled) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
settled = true;
|
|
58
|
+
window__.clearTimeout(timeoutId);
|
|
59
|
+
resolve(attempt);
|
|
60
|
+
};
|
|
61
|
+
const onSlotRenderEnded = (event) => {
|
|
62
|
+
if (event.slot !== slot) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (event.isEmpty) {
|
|
66
|
+
logger.debug('rewardedAd', 'gam attempt has no fill');
|
|
67
|
+
removeListeners();
|
|
68
|
+
destroySlot();
|
|
69
|
+
settle({ outcome: 'no-fill' });
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const onRewardedSlotReady = (event) => {
|
|
73
|
+
if (event.slot !== slot) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
window__.clearTimeout(timeoutId);
|
|
77
|
+
logger.debug('rewardedAd', 'gam rewarded slot ready');
|
|
78
|
+
event.makeRewardedVisible();
|
|
79
|
+
};
|
|
80
|
+
const onRewardedSlotGranted = (event) => {
|
|
81
|
+
if (event.slot !== slot) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const payload = event.payload ?? defaultRewardPayload;
|
|
85
|
+
logger.debug('rewardedAd', 'gam granted reward', payload);
|
|
86
|
+
settle({ outcome: 'granted', payload });
|
|
87
|
+
};
|
|
88
|
+
const onRewardedSlotClosed = (event) => {
|
|
89
|
+
if (event.slot !== slot) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
logger.debug('rewardedAd', 'gam rewarded slot closed');
|
|
93
|
+
removeListeners();
|
|
94
|
+
destroySlot();
|
|
95
|
+
settle({ outcome: 'canceled' });
|
|
96
|
+
};
|
|
97
|
+
pubads.addEventListener('slotRenderEnded', onSlotRenderEnded);
|
|
98
|
+
pubads.addEventListener('rewardedSlotReady', onRewardedSlotReady);
|
|
99
|
+
pubads.addEventListener('rewardedSlotGranted', onRewardedSlotGranted);
|
|
100
|
+
pubads.addEventListener('rewardedSlotClosed', onRewardedSlotClosed);
|
|
101
|
+
timeoutId = window__.setTimeout(() => {
|
|
102
|
+
logger.debug('rewardedAd', `gam attempt timed out after ${config.timeoutMs}ms without a rewardedSlotReady event`);
|
|
103
|
+
removeListeners();
|
|
104
|
+
destroySlot();
|
|
105
|
+
settle({ outcome: 'no-fill' });
|
|
106
|
+
}, config.timeoutMs);
|
|
107
|
+
slot.addService(pubads);
|
|
108
|
+
googletagRef.display(slot);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
const hasValidWelectToken = (welectConfig) => new Promise(resolve => {
|
|
112
|
+
let settled = false;
|
|
113
|
+
const settle = (validToken) => {
|
|
114
|
+
if (settled) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
settled = true;
|
|
118
|
+
window__.clearTimeout(timeoutId);
|
|
119
|
+
resolve(validToken);
|
|
120
|
+
};
|
|
121
|
+
const timeoutId = window__.setTimeout(() => {
|
|
122
|
+
logger.debug('rewardedAd', `welect token preflight timed out after ${config.timeoutMs}ms`);
|
|
123
|
+
settle(false);
|
|
124
|
+
}, config.timeoutMs);
|
|
125
|
+
loadWelectSdk(welectConfig)
|
|
126
|
+
.then(welectApi => {
|
|
127
|
+
const checkToken = welectApi.checkToken;
|
|
128
|
+
if (!checkToken) {
|
|
129
|
+
logger.error('rewardedAd', 'welect bundle does not provide checkToken');
|
|
130
|
+
settle(false);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
checkToken({
|
|
134
|
+
onValid: () => settle(true),
|
|
135
|
+
onInvalid: () => settle(false)
|
|
136
|
+
});
|
|
137
|
+
})
|
|
138
|
+
.catch(error => {
|
|
139
|
+
logger.error('rewardedAd', 'welect token preflight failed', error);
|
|
140
|
+
settle(false);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
const attemptWelect = (welectConfig) => new Promise(resolve => {
|
|
144
|
+
let settled = false;
|
|
145
|
+
const settle = (attempt) => {
|
|
146
|
+
if (settled) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
settled = true;
|
|
150
|
+
window__.clearTimeout(timeoutId);
|
|
151
|
+
resolve(attempt);
|
|
152
|
+
};
|
|
153
|
+
const timeoutId = window__.setTimeout(() => {
|
|
154
|
+
logger.debug('rewardedAd', `welect attempt timed out after ${config.timeoutMs}ms without an availability result`);
|
|
155
|
+
settle({ outcome: 'no-fill' });
|
|
156
|
+
}, config.timeoutMs);
|
|
157
|
+
loadWelectSdk(welectConfig)
|
|
158
|
+
.then(welectApi => {
|
|
159
|
+
if (settled) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const checkAvailability = welectApi.checkAvailability;
|
|
163
|
+
const runSession = welectApi.runSession;
|
|
164
|
+
if (!checkAvailability || !runSession) {
|
|
165
|
+
logger.error('rewardedAd', 'welect bundle does not provide checkAvailability/runSession');
|
|
166
|
+
settle({ outcome: 'no-fill' });
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
checkAvailability({
|
|
170
|
+
onAvailable: () => {
|
|
171
|
+
if (settled) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
window__.clearTimeout(timeoutId);
|
|
175
|
+
logger.debug('rewardedAd', 'welect session available');
|
|
176
|
+
runSession({
|
|
177
|
+
onSuccess: () => {
|
|
178
|
+
logger.debug('rewardedAd', 'welect granted reward', welectConfig.payload);
|
|
179
|
+
settle({ outcome: 'granted', payload: welectConfig.payload });
|
|
180
|
+
},
|
|
181
|
+
onAbort: () => {
|
|
182
|
+
logger.debug('rewardedAd', 'welect session aborted');
|
|
183
|
+
settle({ outcome: 'canceled' });
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
onUnavailable: () => {
|
|
188
|
+
logger.debug('rewardedAd', 'welect attempt has no fill');
|
|
189
|
+
settle({ outcome: 'no-fill' });
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
})
|
|
193
|
+
.catch(error => {
|
|
194
|
+
logger.error('rewardedAd', 'failed to load the welect bundle', error);
|
|
195
|
+
settle({ outcome: 'no-fill' });
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
const attemptChannel = (channel) => {
|
|
199
|
+
switch (channel) {
|
|
200
|
+
case 'gam':
|
|
201
|
+
return config.gam ? attemptGam(config.gam) : Promise.resolve({ outcome: 'no-fill' });
|
|
202
|
+
case 'welect':
|
|
203
|
+
return config.welect
|
|
204
|
+
? attemptWelect(config.welect)
|
|
205
|
+
: Promise.resolve({ outcome: 'no-fill' });
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
const requestRewardedAd = async () => {
|
|
209
|
+
if (inFlight) {
|
|
210
|
+
return { state: 'error', reason: 'already-in-progress' };
|
|
211
|
+
}
|
|
212
|
+
if (!config.enabled || config.priority.length === 0) {
|
|
213
|
+
return { state: 'empty' };
|
|
214
|
+
}
|
|
215
|
+
inFlight = true;
|
|
216
|
+
try {
|
|
217
|
+
const welectConfig = config.welect;
|
|
218
|
+
if (welectConfig &&
|
|
219
|
+
config.priority.includes('welect') &&
|
|
220
|
+
welectConfig.checkToken !== false &&
|
|
221
|
+
(await hasValidWelectToken(welectConfig))) {
|
|
222
|
+
logger.debug('rewardedAd', 'valid welect token - granting without an ad');
|
|
223
|
+
return { state: 'granted', channel: 'welect', payload: welectConfig.payload };
|
|
224
|
+
}
|
|
225
|
+
for (const channel of config.priority) {
|
|
226
|
+
const attempt = await attemptChannel(channel);
|
|
227
|
+
switch (attempt.outcome) {
|
|
228
|
+
case 'granted':
|
|
229
|
+
return { state: 'granted', channel, payload: attempt.payload };
|
|
230
|
+
case 'canceled':
|
|
231
|
+
return { state: 'canceled', channel };
|
|
232
|
+
case 'no-fill':
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return { state: 'empty' };
|
|
237
|
+
}
|
|
238
|
+
finally {
|
|
239
|
+
inFlight = false;
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
return {
|
|
243
|
+
requestRewardedAd,
|
|
244
|
+
updateAdUnitPaths: (variables) => {
|
|
245
|
+
if (config.gam) {
|
|
246
|
+
gamAdUnitPath = resolveAdUnitPath(config.gam.adUnitPath, variables);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
};
|
|
@@ -4,9 +4,11 @@ import { createFrequencyCapping } from './auctions/frequencyCapping';
|
|
|
4
4
|
import { createPreviousBidCpms } from './auctions/previousBidCpms';
|
|
5
5
|
import { mkConfigureStep } from './adPipeline';
|
|
6
6
|
import { createInterstitialContext } from 'ad-tag/ads/auctions/interstitialContext';
|
|
7
|
+
import { createRewardedAdContext } from 'ad-tag/ads/auctions/rewardedAdContext';
|
|
7
8
|
import { createTrackWinningBidder } from 'ad-tag/ads/auctions/trackWinningBidder';
|
|
8
9
|
import { resolveOverridableConfig } from 'ad-tag/ads/configOverrides';
|
|
9
|
-
|
|
10
|
+
import { createAssetLoaderService } from 'ad-tag/util/assetLoaderService';
|
|
11
|
+
export const createGlobalAuctionContext = (window, logger, eventService, config = {}, isLabelConditionMet = () => false, assetLoaderService = createAssetLoaderService(window)) => {
|
|
10
12
|
const resolveFeature = (base, feature) => {
|
|
11
13
|
if (!base) {
|
|
12
14
|
return undefined;
|
|
@@ -23,6 +25,7 @@ export const createGlobalAuctionContext = (window, logger, eventService, config
|
|
|
23
25
|
const frequencyCapConfig = resolveFeature(config.frequencyCap, 'frequencyCap');
|
|
24
26
|
const previousBidCpmsConfig = resolveFeature(config.previousBidCpms, 'previousBidCpms');
|
|
25
27
|
const interstitialConfig = resolveFeature(config.interstitial, 'interstitial');
|
|
28
|
+
const rewardedAdConfig = resolveFeature(config.rewardedAd, 'rewardedAd');
|
|
26
29
|
const trackWinningBidder = trackWinningBidderConfig?.enabled
|
|
27
30
|
? createTrackWinningBidder()
|
|
28
31
|
: undefined;
|
|
@@ -39,6 +42,9 @@ export const createGlobalAuctionContext = (window, logger, eventService, config
|
|
|
39
42
|
const interstitial = interstitialConfig?.enabled
|
|
40
43
|
? createInterstitialContext(interstitialConfig, window, window.Date.now, logger)
|
|
41
44
|
: undefined;
|
|
45
|
+
const rewardedAd = rewardedAdConfig?.enabled
|
|
46
|
+
? createRewardedAdContext(rewardedAdConfig, window, logger, assetLoaderService)
|
|
47
|
+
: undefined;
|
|
42
48
|
window.pbjs = window.pbjs || { que: [] };
|
|
43
49
|
window.googletag = window.googletag || {};
|
|
44
50
|
window.googletag.cmd = window.googletag.cmd || [];
|
|
@@ -99,6 +105,7 @@ export const createGlobalAuctionContext = (window, logger, eventService, config
|
|
|
99
105
|
const configureStep = mkConfigureStep('GlobalAuctionContext', context => {
|
|
100
106
|
frequencyCapping?.updateAdUnitPaths(context.adUnitPathVariables__);
|
|
101
107
|
interstitial?.updateAdUnitPaths(context.adUnitPathVariables__);
|
|
108
|
+
rewardedAd?.updateAdUnitPaths(context.adUnitPathVariables__);
|
|
102
109
|
return Promise.resolve();
|
|
103
110
|
});
|
|
104
111
|
return {
|
|
@@ -121,6 +128,9 @@ export const createGlobalAuctionContext = (window, logger, eventService, config
|
|
|
121
128
|
interstitialChannel: () => {
|
|
122
129
|
return interstitial?.interstitialChannel();
|
|
123
130
|
},
|
|
131
|
+
rewardedAd: () => {
|
|
132
|
+
return rewardedAd?.requestRewardedAd() ?? Promise.resolve({ state: 'empty' });
|
|
133
|
+
},
|
|
124
134
|
hasMinimumRequestAds(minRequestAds) {
|
|
125
135
|
if (!frequencyCapping) {
|
|
126
136
|
return true;
|
|
@@ -98,6 +98,7 @@ export const createBlocklistedUrls = () => {
|
|
|
98
98
|
const result = entry.reverseMatch === true ? !matcher(entry) : matcher(entry);
|
|
99
99
|
if (result) {
|
|
100
100
|
ctx.labelConfigService__.addLabel(entry.label);
|
|
101
|
+
ctx.logger__.debug('Blocklist URLs', `Added label ${entry.label} for url ${ctx.window__.location.href}`);
|
|
101
102
|
}
|
|
102
103
|
});
|
|
103
104
|
})
|
|
@@ -107,6 +108,7 @@ export const createBlocklistedUrls = () => {
|
|
|
107
108
|
const prepareRequestAdsSteps__ = () => [];
|
|
108
109
|
return {
|
|
109
110
|
name: 'Blocklist URLs',
|
|
111
|
+
configKey: 'blocklist',
|
|
110
112
|
description: '',
|
|
111
113
|
moduleType: 'policy',
|
|
112
114
|
config__,
|
|
@@ -16,32 +16,6 @@ export const createSkin = () => {
|
|
|
16
16
|
let skinModuleConfig = null;
|
|
17
17
|
let bidsBackHandler = [];
|
|
18
18
|
const config__ = () => skinModuleConfig;
|
|
19
|
-
const getSkinBids = (config, bidResponses) => {
|
|
20
|
-
const skinBidResponse = bidResponses[config.skinAdSlotDomId];
|
|
21
|
-
const isSkinBid = (bid) => {
|
|
22
|
-
const oneFilterApplied = config.formatFilter.some(filter => {
|
|
23
|
-
switch (filter.bidder) {
|
|
24
|
-
case '*':
|
|
25
|
-
return true;
|
|
26
|
-
case 'gumgum':
|
|
27
|
-
return (bid.bidder === prebidjs.GumGum &&
|
|
28
|
-
(filter.auid === undefined ||
|
|
29
|
-
(typeof bid.ad !== 'string' && bid.ad.auid === filter.auid)));
|
|
30
|
-
default:
|
|
31
|
-
return bid.bidder === filter.bidder;
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
return bid.cpm > 0 && oneFilterApplied;
|
|
35
|
-
};
|
|
36
|
-
return skinBidResponse
|
|
37
|
-
?
|
|
38
|
-
skinBidResponse.bids.filter(isSkinBid).sort((bid1, bid2) => bid2.cpm - bid1.cpm)
|
|
39
|
-
: [];
|
|
40
|
-
};
|
|
41
|
-
const getHighestSkinBidCpm = (config, bidResponses) => {
|
|
42
|
-
const skinBids = getSkinBids(config, bidResponses);
|
|
43
|
-
return skinBids.length > 0 ? skinBids[0].cpm : null;
|
|
44
|
-
};
|
|
45
19
|
const getConfigEffect = (config, bidResponses, log) => {
|
|
46
20
|
const skinBidResponse = bidResponses[config.skinAdSlotDomId];
|
|
47
21
|
const isSkinBid = (bid) => {
|
|
@@ -80,29 +54,12 @@ export const createSkin = () => {
|
|
|
80
54
|
}
|
|
81
55
|
return skinBids.length > 0 ? "BlockOtherSlots" : "NoBlocking";
|
|
82
56
|
};
|
|
83
|
-
const selectConfig = (skinModuleConfig, bidResponses, log) =>
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}));
|
|
89
|
-
const matchingConfigs = configEvaluations.filter(({ configEffect }) => configEffect !== "NoBlocking");
|
|
90
|
-
const compareWithOtherSkinsConfigs = matchingConfigs.filter(({ skinConfig }) => skinConfig.compareWithOtherSkins);
|
|
91
|
-
if (compareWithOtherSkinsConfigs.length > 1) {
|
|
92
|
-
return compareWithOtherSkinsConfigs.reduce((selectedConfig, currentConfig) => {
|
|
93
|
-
if (selectedConfig.highestSkinCpm === null ||
|
|
94
|
-
(currentConfig.highestSkinCpm !== null &&
|
|
95
|
-
currentConfig.highestSkinCpm > selectedConfig.highestSkinCpm)) {
|
|
96
|
-
return currentConfig;
|
|
97
|
-
}
|
|
98
|
-
return selectedConfig;
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
const fallbackConfig = matchingConfigs[0];
|
|
102
|
-
return fallbackConfig
|
|
103
|
-
? { skinConfig: fallbackConfig.skinConfig, configEffect: fallbackConfig.configEffect }
|
|
104
|
-
: undefined;
|
|
105
|
-
};
|
|
57
|
+
const selectConfig = (skinModuleConfig, bidResponses, log) => skinModuleConfig.configs
|
|
58
|
+
.map(config => ({
|
|
59
|
+
skinConfig: config,
|
|
60
|
+
configEffect: getConfigEffect(config, bidResponses, log)
|
|
61
|
+
}))
|
|
62
|
+
.find(({ configEffect }) => configEffect !== "NoBlocking");
|
|
106
63
|
const destroyAdSlot = (slotDefinitions, gWindow) => (adSlotDomId) => {
|
|
107
64
|
const adSlots = slotDefinitions
|
|
108
65
|
.map(slot => slot.adSlot)
|
|
@@ -178,6 +135,7 @@ export const createSkin = () => {
|
|
|
178
135
|
const prebidBidsBackHandler__ = () => bidsBackHandler;
|
|
179
136
|
return {
|
|
180
137
|
name,
|
|
138
|
+
configKey: 'skin',
|
|
181
139
|
description: 'Block other ad slots if a wallpaper has won the auction',
|
|
182
140
|
moduleType: 'prebid',
|
|
183
141
|
config__,
|
|
@@ -60,6 +60,7 @@ export const createIdentityLink = () => {
|
|
|
60
60
|
const prepareRequestAdsSteps__ = () => [];
|
|
61
61
|
return {
|
|
62
62
|
name,
|
|
63
|
+
configKey: 'identitylink',
|
|
63
64
|
description: "Provides LiveRamp's ATS (authenticated traffic solution) functionality to Moli.",
|
|
64
65
|
moduleType: 'identity',
|
|
65
66
|
config__,
|
|
@@ -102,6 +102,7 @@ export const createPrebidFirstPartyDataModule = () => {
|
|
|
102
102
|
const prepareRequestAdsSteps__ = () => [];
|
|
103
103
|
return {
|
|
104
104
|
name,
|
|
105
|
+
configKey: 'prebidFirstPartyData',
|
|
105
106
|
description: 'Module for passing first party data to prebid auctions',
|
|
106
107
|
moduleType: 'prebid',
|
|
107
108
|
config__,
|
|
@@ -100,6 +100,7 @@ export const createZeotap = () => {
|
|
|
100
100
|
const prepareRequestAdsSteps__ = () => [];
|
|
101
101
|
return {
|
|
102
102
|
name,
|
|
103
|
+
configKey: 'zeotap',
|
|
103
104
|
description: 'Provides Zeotap functionality (data collection and identity plus) to Moli.',
|
|
104
105
|
moduleType: 'identity',
|
|
105
106
|
config__,
|
package/lib/ads/moli.js
CHANGED
|
@@ -247,7 +247,8 @@ export const createMoliTag = (window) => {
|
|
|
247
247
|
modules
|
|
248
248
|
.map(module => {
|
|
249
249
|
const moduleName = module.name;
|
|
250
|
-
const
|
|
250
|
+
const configKey = module.configKey;
|
|
251
|
+
const base = state.config?.modules?.[configKey];
|
|
251
252
|
if (!base) {
|
|
252
253
|
return null;
|
|
253
254
|
}
|
|
@@ -262,15 +263,15 @@ export const createMoliTag = (window) => {
|
|
|
262
263
|
}
|
|
263
264
|
const resolvedModulesConfig = {
|
|
264
265
|
...state.config?.modules,
|
|
265
|
-
[
|
|
266
|
+
[configKey]: effectiveConfig
|
|
266
267
|
};
|
|
267
|
-
return { module, resolvedModulesConfig };
|
|
268
|
+
return { module, resolvedModulesConfig, effectiveConfig };
|
|
268
269
|
})
|
|
269
270
|
.filter((entry) => entry !== null)
|
|
270
|
-
.forEach(({ module, resolvedModulesConfig }) => {
|
|
271
|
+
.forEach(({ module, resolvedModulesConfig, effectiveConfig }) => {
|
|
271
272
|
try {
|
|
272
273
|
module.configure__(resolvedModulesConfig);
|
|
273
|
-
getLogger(state.runtimeConfig, window).debug('MoliGlobal', `configure ${module.moduleType} module ${module.name}`, module.config__());
|
|
274
|
+
getLogger(state.runtimeConfig, window).debug('MoliGlobal', `configure ${module.moduleType} module ${module.name}`, `module is ${effectiveConfig.enabled ? 'enabled' : 'disabled'}`, module.config__());
|
|
274
275
|
state.runtimeConfig.adPipelineConfig.initSteps.push(...module.initSteps__());
|
|
275
276
|
state.runtimeConfig.adPipelineConfig.configureSteps.push(...module.configureSteps__());
|
|
276
277
|
state.runtimeConfig.adPipelineConfig.prepareRequestAdsSteps.push(...module.prepareRequestAdsSteps__());
|
|
@@ -579,6 +580,38 @@ export const createMoliTag = (window) => {
|
|
|
579
580
|
}
|
|
580
581
|
}
|
|
581
582
|
}
|
|
583
|
+
function rewardedAd() {
|
|
584
|
+
switch (state.state) {
|
|
585
|
+
case 'configurable':
|
|
586
|
+
case 'configured':
|
|
587
|
+
case 'requestAds':
|
|
588
|
+
case 'spa-requestAds': {
|
|
589
|
+
return new Promise(resolve => {
|
|
590
|
+
let queued = true;
|
|
591
|
+
state.runtimeConfig.hooks.afterRequestAds.push(afterState => {
|
|
592
|
+
if (!queued) {
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
queued = false;
|
|
596
|
+
if (afterState === 'error') {
|
|
597
|
+
resolve({ state: 'error', reason: 'ad-tag-error' });
|
|
598
|
+
}
|
|
599
|
+
else {
|
|
600
|
+
adService.rewardedAd().then(resolve);
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
case 'finished':
|
|
606
|
+
case 'spa-finished': {
|
|
607
|
+
return adService.rewardedAd();
|
|
608
|
+
}
|
|
609
|
+
case 'error': {
|
|
610
|
+
getLogger(state.runtimeConfig, window).error('MoliGlobal', `rewardedAd is not allowed in state ${state.state}`);
|
|
611
|
+
return Promise.resolve({ state: 'error', reason: 'ad-tag-error' });
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
582
615
|
function triggerDelay() {
|
|
583
616
|
window.dispatchEvent(new CustomEvent('h5v.trigger-delay'));
|
|
584
617
|
return;
|
|
@@ -689,6 +722,7 @@ export const createMoliTag = (window) => {
|
|
|
689
722
|
refreshAdSlot: refreshAdSlot,
|
|
690
723
|
refreshBucket: refreshBucket,
|
|
691
724
|
refreshInfiniteAdSlot: refreshInfiniteAdSlot,
|
|
725
|
+
rewardedAd: rewardedAd,
|
|
692
726
|
triggerDelay: triggerDelay,
|
|
693
727
|
getState: getState,
|
|
694
728
|
openConsole: openConsole,
|
|
@@ -32,8 +32,10 @@ const cmpVendors = {
|
|
|
32
32
|
export const ConsentConfig = () => {
|
|
33
33
|
const [consentState, setConsentState] = React.useState({ messages: [] });
|
|
34
34
|
React.useEffect(() => {
|
|
35
|
+
let listenerId;
|
|
35
36
|
if (window.__tcfapi) {
|
|
36
37
|
window.__tcfapi('addEventListener', 2, event => {
|
|
38
|
+
listenerId = event.listenerId;
|
|
37
39
|
const tcModel = event.gdprApplies ? TCString.decode(event.tcString) : undefined;
|
|
38
40
|
setConsentState(prevState => ({
|
|
39
41
|
...prevState,
|
|
@@ -59,7 +61,12 @@ export const ConsentConfig = () => {
|
|
|
59
61
|
messages: [errorMessage, ...prevState.messages]
|
|
60
62
|
}));
|
|
61
63
|
}
|
|
62
|
-
|
|
64
|
+
return () => {
|
|
65
|
+
if (window.__tcfapi && listenerId != null) {
|
|
66
|
+
window.__tcfapi('removeEventListener', 2, () => undefined, listenerId);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}, []);
|
|
63
70
|
const cmpVendorTag = () => {
|
|
64
71
|
if (consentState.tcModel) {
|
|
65
72
|
const cmpVendor = cmpVendors[consentState.tcModel.cmpId.toString()];
|
|
@@ -97,7 +104,7 @@ export const ConsentConfig = () => {
|
|
|
97
104
|
React.createElement("span", { className: "inline-block min-w-36 max-w-96 pr-1 font-medium" }, "TCF Policy Version"),
|
|
98
105
|
tcModel ? (React.createElement(Tag, null, tcModel.policyVersion.toString())) : (React.createElement(Tag, { variant: "red" }, "Unknown")))));
|
|
99
106
|
};
|
|
100
|
-
const isCmpFunctionAvailable = () =>
|
|
107
|
+
const isCmpFunctionAvailable = () => typeof window.__tcfapi === 'function';
|
|
101
108
|
const consentData = () => {
|
|
102
109
|
if (isCmpFunctionAvailable()) {
|
|
103
110
|
return (React.createElement("div", null,
|
package/lib/gen/packageJson.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@highfivve/ad-tag",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.10.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "An ad tag implementation called moli",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"@types/core-js": "^2.5.8",
|
|
68
68
|
"@types/domready": "^1.0.0",
|
|
69
69
|
"@types/jsdom": "^20.0.0",
|
|
70
|
-
"@types/mocha": "
|
|
70
|
+
"@types/mocha": "10.0.10",
|
|
71
71
|
"@types/node": "^20.10.4",
|
|
72
72
|
"@types/react": "^18.3.3",
|
|
73
73
|
"@types/react-dom": "^18.3.0",
|
|
@@ -89,8 +89,8 @@
|
|
|
89
89
|
"eslint-plugin-jsdoc": "^34.8.1",
|
|
90
90
|
"eslint-plugin-preact": "^0.1.0",
|
|
91
91
|
"jsdom": "19.0.0",
|
|
92
|
-
"mocha": "
|
|
93
|
-
"mocha-junit-reporter": "2.
|
|
92
|
+
"mocha": "11.7.6",
|
|
93
|
+
"mocha-junit-reporter": "2.2.1",
|
|
94
94
|
"np": "^10.0.5",
|
|
95
95
|
"postcss": "^8.4.47",
|
|
96
96
|
"postcss-cli": "^11.0.0",
|