@highfivve/ad-tag 5.9.3 → 5.10.2
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/googleAdManager.js +1 -2
- package/lib/ads/modules/blocklist-url/index.js +1 -0
- package/lib/ads/modules/generic-skin/index.js +6 -49
- package/lib/ads/modules/zeotap/index.js +1 -1
- package/lib/ads/moli.js +36 -3
- package/lib/ads/moliGlobal.js +1 -1
- package/lib/ads/prebid.js +1 -2
- package/lib/console/components/consentConfig.js +9 -2
- package/lib/console/components/globalConfig.js +2 -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 +8 -8
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;
|
|
@@ -217,8 +217,7 @@ export const gptDefineSlots = () => (context, slots) => {
|
|
|
217
217
|
const slotDefinitions = slots.map(moliSlot => {
|
|
218
218
|
const sizeConfigService = new SizeConfigService(moliSlot.sizeConfig, context.labelConfigService__.getSupportedLabels(), context.window__);
|
|
219
219
|
const filterSupportedSizes = sizeConfigService.filterSupportedSizes;
|
|
220
|
-
if (!(sizeConfigService.filterSlot(moliSlot) &&
|
|
221
|
-
context.labelConfigService__.filterSlot(moliSlot))) {
|
|
220
|
+
if (!(sizeConfigService.filterSlot(moliSlot) && context.labelConfigService__.filterSlot(moliSlot))) {
|
|
222
221
|
return Promise.resolve(null);
|
|
223
222
|
}
|
|
224
223
|
const sizes = filterSupportedSizes(moliSlot.sizes);
|
|
@@ -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
|
})
|
|
@@ -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)
|
|
@@ -30,7 +30,7 @@ export const createZeotap = () => {
|
|
|
30
30
|
return keyValues
|
|
31
31
|
? new Map(Object.keys(keyValues)
|
|
32
32
|
.map(key => [key, keyValues[key]])
|
|
33
|
-
.filter((
|
|
33
|
+
.filter((entry) => !!entry[1]))
|
|
34
34
|
: new Map();
|
|
35
35
|
};
|
|
36
36
|
const parameterFromKeyValue = (kv, keyValuesMap) => {
|
package/lib/ads/moli.js
CHANGED
|
@@ -265,13 +265,13 @@ export const createMoliTag = (window) => {
|
|
|
265
265
|
...state.config?.modules,
|
|
266
266
|
[configKey]: effectiveConfig
|
|
267
267
|
};
|
|
268
|
-
return { module, resolvedModulesConfig };
|
|
268
|
+
return { module, resolvedModulesConfig, effectiveConfig };
|
|
269
269
|
})
|
|
270
270
|
.filter((entry) => entry !== null)
|
|
271
|
-
.forEach(({ module, resolvedModulesConfig }) => {
|
|
271
|
+
.forEach(({ module, resolvedModulesConfig, effectiveConfig }) => {
|
|
272
272
|
try {
|
|
273
273
|
module.configure__(resolvedModulesConfig);
|
|
274
|
-
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__());
|
|
275
275
|
state.runtimeConfig.adPipelineConfig.initSteps.push(...module.initSteps__());
|
|
276
276
|
state.runtimeConfig.adPipelineConfig.configureSteps.push(...module.configureSteps__());
|
|
277
277
|
state.runtimeConfig.adPipelineConfig.prepareRequestAdsSteps.push(...module.prepareRequestAdsSteps__());
|
|
@@ -580,6 +580,38 @@ export const createMoliTag = (window) => {
|
|
|
580
580
|
}
|
|
581
581
|
}
|
|
582
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
|
+
}
|
|
583
615
|
function triggerDelay() {
|
|
584
616
|
window.dispatchEvent(new CustomEvent('h5v.trigger-delay'));
|
|
585
617
|
return;
|
|
@@ -690,6 +722,7 @@ export const createMoliTag = (window) => {
|
|
|
690
722
|
refreshAdSlot: refreshAdSlot,
|
|
691
723
|
refreshBucket: refreshBucket,
|
|
692
724
|
refreshInfiniteAdSlot: refreshInfiniteAdSlot,
|
|
725
|
+
rewardedAd: rewardedAd,
|
|
693
726
|
triggerDelay: triggerDelay,
|
|
694
727
|
getState: getState,
|
|
695
728
|
openConsole: openConsole,
|
package/lib/ads/moliGlobal.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createMoliTag } from './moli';
|
|
|
2
2
|
export const initAdTag = (window) => {
|
|
3
3
|
const moliWindow = window;
|
|
4
4
|
const queueCommands = moliWindow.moli
|
|
5
|
-
? [...moliWindow.moli.que]
|
|
5
|
+
? [...moliWindow.moli.que]
|
|
6
6
|
: [];
|
|
7
7
|
const moli = createMoliTag(window);
|
|
8
8
|
moliWindow.moli = moli;
|
package/lib/ads/prebid.js
CHANGED
|
@@ -323,8 +323,7 @@ export const prebidDefineSlots = () => (context, slots) => {
|
|
|
323
323
|
const slotDefinitions = slots.map(moliSlot => {
|
|
324
324
|
const sizeConfigService = new SizeConfigService(moliSlot.sizeConfig, context.labelConfigService__.getSupportedLabels(), context.window__);
|
|
325
325
|
const filterSupportedSizes = sizeConfigService.filterSupportedSizes;
|
|
326
|
-
if (!(sizeConfigService.filterSlot(moliSlot) &&
|
|
327
|
-
context.labelConfigService__.filterSlot(moliSlot))) {
|
|
326
|
+
if (!(sizeConfigService.filterSlot(moliSlot) && context.labelConfigService__.filterSlot(moliSlot))) {
|
|
328
327
|
return Promise.resolve(null);
|
|
329
328
|
}
|
|
330
329
|
const adSlot = {
|
|
@@ -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,
|
|
@@ -154,8 +154,8 @@ export class GlobalConfig extends Component {
|
|
|
154
154
|
React.createElement(SubHeadline, null, "Consent management"),
|
|
155
155
|
React.createElement(TagContainer, null,
|
|
156
156
|
React.createElement(TagLabel, null, "allowAuctionWithoutConsent"),
|
|
157
|
-
React.createElement(Tag, null, (
|
|
158
|
-
|
|
157
|
+
React.createElement(Tag, null, (config.prebid.config.consentManagement.gdpr?.allowAuctionWithoutConsent ??
|
|
158
|
+
true).toString())),
|
|
159
159
|
config.prebid.config.consentManagement.gdpr?.cmpApi && (React.createElement(TagContainer, null,
|
|
160
160
|
React.createElement(TagLabel, null, "CMP API"),
|
|
161
161
|
React.createElement(Tag, null, config.prebid.config.consentManagement.gdpr?.cmpApi ?? 'iab'))),
|
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.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "An ad tag implementation called moli",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -60,14 +60,14 @@
|
|
|
60
60
|
"@rollup/plugin-commonjs": "^26.0.1",
|
|
61
61
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
62
62
|
"@rollup/plugin-replace": "^5.0.7",
|
|
63
|
-
"@rollup/plugin-terser": "^0.
|
|
63
|
+
"@rollup/plugin-terser": "^1.0.0",
|
|
64
64
|
"@rollup/plugin-typescript": "^11.1.6",
|
|
65
65
|
"@types/chai": "4.2.18",
|
|
66
66
|
"@types/chai-as-promised": "^7.1.4",
|
|
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",
|
|
@@ -83,20 +83,20 @@
|
|
|
83
83
|
"commander": "^12.1.0",
|
|
84
84
|
"cssnano": "^5.1.15",
|
|
85
85
|
"daisyui": "^4.12.24",
|
|
86
|
-
"esbuild": "^0.
|
|
86
|
+
"esbuild": "^0.28.1",
|
|
87
87
|
"eslint": "^7.26.0",
|
|
88
88
|
"eslint-config-prettier": "^8.3.0",
|
|
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.
|
|
94
|
-
"np": "^10.0
|
|
92
|
+
"mocha": "^11.7.6",
|
|
93
|
+
"mocha-junit-reporter": "2.2.1",
|
|
94
|
+
"np": "^10.3.0",
|
|
95
95
|
"postcss": "^8.4.47",
|
|
96
96
|
"postcss-cli": "^11.0.0",
|
|
97
97
|
"postcss-color-function": "^4.1.0",
|
|
98
98
|
"postcss-custom-media": "^8.0.2",
|
|
99
|
-
"postcss-custom-properties": "^
|
|
99
|
+
"postcss-custom-properties": "^15.0.1",
|
|
100
100
|
"postcss-extend": "^1.0.5",
|
|
101
101
|
"postcss-import": "^14.1.0",
|
|
102
102
|
"postcss-mixins": "^8.0.0",
|