@highfivve/ad-tag 5.9.3 → 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/blocklist-url/index.js +1 -0
- package/lib/ads/modules/generic-skin/index.js +6 -49
- package/lib/ads/moli.js +36 -3
- 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
|
})
|
|
@@ -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)
|
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,
|
|
@@ -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",
|