@highfivve/ad-tag 5.11.10 → 5.12.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 +22 -3
- package/lib/ads/modules/custom/index.js +6 -4
- package/lib/ads/modules/inline-ai/index.js +114 -0
- package/lib/ads/moli.js +8 -32
- package/lib/bundle/inline-ai.js +2 -0
- package/lib/console/components/moduleConfigs.js +10 -0
- package/lib/gen/packageJson.js +1 -1
- package/lib/util/mkInfiniteSlot.js +4 -0
- package/package.json +1 -1
- package/lib/util/addNewInfiniteSlotToConfig.js +0 -11
package/lib/ads/adService.js
CHANGED
|
@@ -5,6 +5,7 @@ import domready from '../util/domready';
|
|
|
5
5
|
import { prebidClearAuction, prebidConfigure, prebidDefineSlots, prebidInit, prebidPrepareRequestAds, prebidRemoveAdUnits, prebidRenderAds, prebidRequestBids } from './prebid';
|
|
6
6
|
import { a9Configure, a9Init, a9RequestBids, a9ClearTargetingStep, a9PublisherAudiences } from './a9';
|
|
7
7
|
import { flatten, isNotNull } from '../util/arrayUtils';
|
|
8
|
+
import { mkInfiniteSlot } from '../util/mkInfiniteSlot';
|
|
8
9
|
import { executeDebugDelay, getDebugDelayFromLocalStorage } from '../util/debugDelay';
|
|
9
10
|
import { createGlobalAuctionContext } from './globalAuctionContext';
|
|
10
11
|
import { getDeviceLabel } from 'ad-tag/ads/labelConfigService';
|
|
@@ -137,6 +138,7 @@ export class AdService {
|
|
|
137
138
|
this.logger.info('AdService', `RequestAds[${this.requestAdsCalls}]`, refreshSlots, refreshBuckets);
|
|
138
139
|
this.eventService.emit('beforeRequestAds', { runtimeConfig: runtimeConfig });
|
|
139
140
|
try {
|
|
141
|
+
const queuedInfiniteSlots = this.resolveInfiniteSlots(refreshInfiniteSlots, config);
|
|
140
142
|
const immediatelyLoadedSlots = config.slots
|
|
141
143
|
.map(slot => {
|
|
142
144
|
if (isManualSlot(slot)) {
|
|
@@ -145,9 +147,7 @@ export class AdService {
|
|
|
145
147
|
: null;
|
|
146
148
|
}
|
|
147
149
|
else if (isInfiniteSlot(slot)) {
|
|
148
|
-
return
|
|
149
|
-
? slot
|
|
150
|
-
: null;
|
|
150
|
+
return null;
|
|
151
151
|
}
|
|
152
152
|
else if (isBackfillSlot(slot)) {
|
|
153
153
|
return null;
|
|
@@ -157,6 +157,7 @@ export class AdService {
|
|
|
157
157
|
}
|
|
158
158
|
})
|
|
159
159
|
.filter(isNotNull)
|
|
160
|
+
.concat(queuedInfiniteSlots)
|
|
160
161
|
.filter(isSlotAvailable(this.window));
|
|
161
162
|
if (config.buckets?.enabled) {
|
|
162
163
|
const buckets = new Map();
|
|
@@ -193,6 +194,16 @@ export class AdService {
|
|
|
193
194
|
return Promise.reject(e);
|
|
194
195
|
}
|
|
195
196
|
};
|
|
197
|
+
this.resolveInfiniteSlots = (infiniteSlots, config) => infiniteSlots
|
|
198
|
+
.map(({ idOfConfiguredSlot, artificialDomId }) => {
|
|
199
|
+
const configuredSlot = config.slots.find(slot => slot.domId === idOfConfiguredSlot && isInfiniteSlot(slot));
|
|
200
|
+
if (!configuredSlot) {
|
|
201
|
+
this.logger.error('AdService', `no ad slot with an 'infinite' loading behaviour configured for domId ${idOfConfiguredSlot}`);
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
return mkInfiniteSlot(configuredSlot, artificialDomId);
|
|
205
|
+
})
|
|
206
|
+
.filter(isNotNull);
|
|
196
207
|
this.rewardedAd = () => {
|
|
197
208
|
return this.globalAuctionContext.rewardedAd();
|
|
198
209
|
};
|
|
@@ -208,6 +219,14 @@ export class AdService {
|
|
|
208
219
|
this.adPipeline = new AdPipeline(adPipelineConfig, this.logger, window, this.globalAuctionContext);
|
|
209
220
|
}
|
|
210
221
|
}
|
|
222
|
+
refreshInfiniteAdSlots(infiniteSlots, config, runtimeConfig) {
|
|
223
|
+
if (infiniteSlots.length === 0) {
|
|
224
|
+
return Promise.resolve();
|
|
225
|
+
}
|
|
226
|
+
const availableSlots = this.resolveInfiniteSlots(infiniteSlots, config).filter(isSlotAvailable(this.window));
|
|
227
|
+
this.logger.debug('AdService', 'refresh infinite ad slots', availableSlots);
|
|
228
|
+
return this.adPipeline.run(availableSlots, config, runtimeConfig, this.requestAdsCalls);
|
|
229
|
+
}
|
|
211
230
|
refreshAdSlots(domIds, config, runtimeConfig, options) {
|
|
212
231
|
if (domIds.length === 0) {
|
|
213
232
|
return Promise.resolve();
|
|
@@ -6,7 +6,7 @@ const hasConsent = (context, scriptConfig) => {
|
|
|
6
6
|
}
|
|
7
7
|
switch (scriptConfig.consent.cmpApi) {
|
|
8
8
|
case 'tcf':
|
|
9
|
-
return (!context.tcData__.gdprApplies ||
|
|
9
|
+
return Boolean(!context.tcData__.gdprApplies ||
|
|
10
10
|
context.tcData__.vendor.consents[scriptConfig.consent.vendorId]);
|
|
11
11
|
}
|
|
12
12
|
};
|
|
@@ -32,15 +32,17 @@ export const customModule = () => {
|
|
|
32
32
|
.forEach(scriptConfig => {
|
|
33
33
|
try {
|
|
34
34
|
const script = context.window__.document.createElement('script');
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
if (scriptConfig.src) {
|
|
36
|
+
script.type = 'text/javascript';
|
|
37
|
+
script.src = scriptConfig.src;
|
|
38
|
+
}
|
|
37
39
|
if (scriptConfig.attributes) {
|
|
38
40
|
Object.entries(scriptConfig.attributes).forEach(([key, value]) => {
|
|
39
41
|
script.setAttribute(key, value);
|
|
40
42
|
});
|
|
41
43
|
}
|
|
42
44
|
context.window__.document.head.appendChild(script);
|
|
43
|
-
context.logger__?.info(name, `Injected script from URL: ${scriptConfig.src}`);
|
|
45
|
+
context.logger__?.info(name, `Injected script from URL: ${scriptConfig.src ?? '(attribute-driven)'}`);
|
|
44
46
|
}
|
|
45
47
|
catch (e) {
|
|
46
48
|
context.logger__?.error(name, 'Failed to inject script from config', scriptConfig, e);
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { AssetLoadMethod } from 'ad-tag/util/assetLoaderService';
|
|
2
|
+
import { mkConfigureStepOncePerRequestAdsCycle, mkInitStep } from 'ad-tag/ads/adPipeline';
|
|
3
|
+
const name = 'inline-ai';
|
|
4
|
+
export const createInlineAi = () => {
|
|
5
|
+
let inlineAiConfig = null;
|
|
6
|
+
let scriptLoaded = false;
|
|
7
|
+
const config__ = () => inlineAiConfig;
|
|
8
|
+
const configure__ = (moduleConfig) => {
|
|
9
|
+
if (moduleConfig?.inlineAi?.enabled) {
|
|
10
|
+
inlineAiConfig = moduleConfig.inlineAi;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
const hasConsent = (context) => !context.tcData__.gdprApplies || !!context.tcData__.purpose.consents['1'];
|
|
14
|
+
const isPlacementActive = (context, placement) => !placement.labelCondition ||
|
|
15
|
+
context.labelConfigService__.isLabelConditionMet(placement.labelCondition);
|
|
16
|
+
const mountCommand = (placement) => {
|
|
17
|
+
switch (placement.type) {
|
|
18
|
+
case 'widget':
|
|
19
|
+
return ['mount', placement.type];
|
|
20
|
+
case 'search-fab':
|
|
21
|
+
return placement.options
|
|
22
|
+
? ['mount', placement.type, undefined, placement.options]
|
|
23
|
+
: ['mount', placement.type];
|
|
24
|
+
case 'search-embed':
|
|
25
|
+
case 'search-icon':
|
|
26
|
+
return placement.options
|
|
27
|
+
? ['mount', placement.type, placement.target, placement.options]
|
|
28
|
+
: ['mount', placement.type, placement.target];
|
|
29
|
+
case 'key-takeaways':
|
|
30
|
+
case 'single-question':
|
|
31
|
+
case 'basic-embed':
|
|
32
|
+
return ['mount', placement.type, placement.target];
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const mountPlacements = (context, cmd, config) => {
|
|
36
|
+
(config.placements ?? [])
|
|
37
|
+
.filter(placement => isPlacementActive(context, placement))
|
|
38
|
+
.forEach(placement => cmd.push(mountCommand(placement)));
|
|
39
|
+
};
|
|
40
|
+
const applyMode = (context, cmd, config) => {
|
|
41
|
+
switch (config.mode) {
|
|
42
|
+
case 'auto':
|
|
43
|
+
return;
|
|
44
|
+
case 'programmatic':
|
|
45
|
+
cmd.push(['init', { publisherId: config.publisherId }]);
|
|
46
|
+
mountPlacements(context, cmd, config);
|
|
47
|
+
return;
|
|
48
|
+
case 'hybrid':
|
|
49
|
+
mountPlacements(context, cmd, config);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const loadScript = (context, config) => {
|
|
54
|
+
if (scriptLoaded) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
scriptLoaded = true;
|
|
58
|
+
context.assetLoaderService__
|
|
59
|
+
.loadScript({
|
|
60
|
+
name,
|
|
61
|
+
loadMethod: AssetLoadMethod.TAG,
|
|
62
|
+
assetUrl: `${config.scriptUrl}?key=${config.publisherId}`,
|
|
63
|
+
type: 'module'
|
|
64
|
+
})
|
|
65
|
+
.catch(error => context.logger__.error(name, 'failed to load InlineAI script', error));
|
|
66
|
+
};
|
|
67
|
+
const isFirstRun = (context) => context.requestAdsCalls__ === 1;
|
|
68
|
+
const runInlineAi = (context, config) => {
|
|
69
|
+
if (context.env__ === 'test') {
|
|
70
|
+
return Promise.resolve();
|
|
71
|
+
}
|
|
72
|
+
if (!hasConsent(context)) {
|
|
73
|
+
context.logger__.warn(name, 'no gdpr consent, InlineAI will not be loaded');
|
|
74
|
+
return Promise.resolve();
|
|
75
|
+
}
|
|
76
|
+
if (config.mode !== 'auto') {
|
|
77
|
+
context.window__.InlineAI = context.window__.InlineAI || { cmd: [] };
|
|
78
|
+
const cmd = context.window__.InlineAI.cmd;
|
|
79
|
+
if (!isFirstRun(context)) {
|
|
80
|
+
cmd.push(['destroy']);
|
|
81
|
+
}
|
|
82
|
+
applyMode(context, cmd, config);
|
|
83
|
+
}
|
|
84
|
+
loadScript(context, config);
|
|
85
|
+
return Promise.resolve();
|
|
86
|
+
};
|
|
87
|
+
const initSteps__ = () => {
|
|
88
|
+
const config = inlineAiConfig;
|
|
89
|
+
return config
|
|
90
|
+
? [
|
|
91
|
+
mkInitStep('inline-ai-init', context => context.config__.spa?.enabled ? Promise.resolve() : runInlineAi(context, config))
|
|
92
|
+
]
|
|
93
|
+
: [];
|
|
94
|
+
};
|
|
95
|
+
const configureSteps__ = () => {
|
|
96
|
+
const config = inlineAiConfig;
|
|
97
|
+
return config
|
|
98
|
+
? [
|
|
99
|
+
mkConfigureStepOncePerRequestAdsCycle('inline-ai-configure', context => context.config__.spa?.enabled ? runInlineAi(context, config) : Promise.resolve())
|
|
100
|
+
]
|
|
101
|
+
: [];
|
|
102
|
+
};
|
|
103
|
+
return {
|
|
104
|
+
name,
|
|
105
|
+
configKey: 'inlineAi',
|
|
106
|
+
description: 'loads the InlineAI SDK and drives placement rendering',
|
|
107
|
+
moduleType: 'engagement',
|
|
108
|
+
config__,
|
|
109
|
+
configure__,
|
|
110
|
+
initSteps__,
|
|
111
|
+
configureSteps__,
|
|
112
|
+
prepareRequestAdsSteps__: () => []
|
|
113
|
+
};
|
|
114
|
+
};
|
package/lib/ads/moli.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AssetLoadMethod, createAssetLoaderService } from '../util/assetLoaderService';
|
|
2
2
|
import { getLogger } from '../util/logging';
|
|
3
|
-
import { addNewInfiniteSlotToConfig } from '../util/addNewInfiniteSlotToConfig';
|
|
4
3
|
import { AdService } from './adService';
|
|
5
4
|
import { createEventService } from './eventService';
|
|
6
5
|
import { getAbTestValues, getActiveEnvironmentOverride, setEnvironmentOverrideInStorage } from '../util/environmentOverride';
|
|
@@ -231,16 +230,6 @@ export const createMoliTag = (window) => {
|
|
|
231
230
|
}
|
|
232
231
|
return Promise.resolve(null);
|
|
233
232
|
}
|
|
234
|
-
function refreshQueuedInfiniteSlots(config, runtimeConfig) {
|
|
235
|
-
const { refreshInfiniteSlots } = runtimeConfig;
|
|
236
|
-
if (refreshInfiniteSlots.length === 0) {
|
|
237
|
-
return config;
|
|
238
|
-
}
|
|
239
|
-
const log = getLogger(runtimeConfig, window);
|
|
240
|
-
const configWithInfiniteSlots = refreshInfiniteSlots.reduce((currentConfig, slot) => addNewInfiniteSlotToConfig(currentConfig, slot.idOfConfiguredSlot, slot.artificialDomId, log), config);
|
|
241
|
-
adService.refreshAdSlots(refreshInfiniteSlots.map(slot => slot.artificialDomId), configWithInfiniteSlots, runtimeConfig);
|
|
242
|
-
return configWithInfiniteSlots;
|
|
243
|
-
}
|
|
244
233
|
function requestAds() {
|
|
245
234
|
switch (state.state) {
|
|
246
235
|
case 'configurable': {
|
|
@@ -296,13 +285,7 @@ export const createMoliTag = (window) => {
|
|
|
296
285
|
getLogger(state.runtimeConfig, window).error('MoliGlobal', `failed to configure ${module.moduleType} module ${module.name}`, e);
|
|
297
286
|
}
|
|
298
287
|
});
|
|
299
|
-
const
|
|
300
|
-
let config = state.config;
|
|
301
|
-
if (refreshInfiniteSlots.length > 0) {
|
|
302
|
-
refreshInfiniteSlots.forEach(slot => {
|
|
303
|
-
config = addNewInfiniteSlotToConfig(config, slot.idOfConfiguredSlot, slot.artificialDomId, getLogger(state.runtimeConfig, window));
|
|
304
|
-
});
|
|
305
|
-
}
|
|
288
|
+
const config = state.config;
|
|
306
289
|
const log = getLogger(state.runtimeConfig, window);
|
|
307
290
|
if (state.runtimeConfig.hooks && state.runtimeConfig.hooks.beforeRequestAds) {
|
|
308
291
|
state.runtimeConfig.hooks.beforeRequestAds.forEach(hook => {
|
|
@@ -334,8 +317,8 @@ export const createMoliTag = (window) => {
|
|
|
334
317
|
const validateLocation = config.spa?.validateLocation ?? 'href';
|
|
335
318
|
if (state.state === 'spa-requestAds' &&
|
|
336
319
|
allowRefreshAdSlot(validateLocation, state.href, window.location)) {
|
|
337
|
-
const { runtimeConfig } = state;
|
|
338
|
-
|
|
320
|
+
const { runtimeConfig, config } = state;
|
|
321
|
+
adService.refreshInfiniteAdSlots(runtimeConfig.refreshInfiniteSlots, config, runtimeConfig);
|
|
339
322
|
if (state.runtimeConfig.refreshSlots.length > 0) {
|
|
340
323
|
adService.refreshAdSlots(runtimeConfig.refreshSlots, config, runtimeConfig);
|
|
341
324
|
}
|
|
@@ -446,7 +429,8 @@ export const createMoliTag = (window) => {
|
|
|
446
429
|
})
|
|
447
430
|
.then(requestedConfig => {
|
|
448
431
|
const runtimeConfig = state.runtimeConfig;
|
|
449
|
-
const config =
|
|
432
|
+
const config = requestedConfig;
|
|
433
|
+
adService.refreshInfiniteAdSlots(runtimeConfig.refreshInfiniteSlots, config, runtimeConfig);
|
|
450
434
|
if (state.runtimeConfig.refreshSlots.length > 0) {
|
|
451
435
|
adService.refreshAdSlots(state.runtimeConfig.refreshSlots, config, state.runtimeConfig);
|
|
452
436
|
}
|
|
@@ -535,16 +519,12 @@ export const createMoliTag = (window) => {
|
|
|
535
519
|
case 'spa-finished':
|
|
536
520
|
const validateLocation = state.config.spa?.validateLocation ?? 'href';
|
|
537
521
|
if (allowRefreshAdSlot(validateLocation, state.href, window.location)) {
|
|
538
|
-
state = {
|
|
539
|
-
...state,
|
|
540
|
-
config: addNewInfiniteSlotToConfig(state.config, idOfConfiguredSlot, domId, getLogger(state.runtimeConfig, window))
|
|
541
|
-
};
|
|
542
522
|
return adService
|
|
543
|
-
.
|
|
523
|
+
.refreshInfiniteAdSlots([{ artificialDomId: domId, idOfConfiguredSlot: idOfConfiguredSlot }], state.config, state.runtimeConfig)
|
|
544
524
|
.then(() => 'refreshed');
|
|
545
525
|
}
|
|
546
526
|
else {
|
|
547
|
-
state.
|
|
527
|
+
state.nextRuntimeConfig.refreshInfiniteSlots.push({
|
|
548
528
|
artificialDomId: domId,
|
|
549
529
|
idOfConfiguredSlot: idOfConfiguredSlot
|
|
550
530
|
});
|
|
@@ -552,12 +532,8 @@ export const createMoliTag = (window) => {
|
|
|
552
532
|
}
|
|
553
533
|
case 'finished':
|
|
554
534
|
case 'requestAds': {
|
|
555
|
-
state = {
|
|
556
|
-
...state,
|
|
557
|
-
config: addNewInfiniteSlotToConfig(state.config, idOfConfiguredSlot, domId, getLogger(state.runtimeConfig, window))
|
|
558
|
-
};
|
|
559
535
|
return adService
|
|
560
|
-
.
|
|
536
|
+
.refreshInfiniteAdSlots([{ artificialDomId: domId, idOfConfiguredSlot: idOfConfiguredSlot }], state.config, state.runtimeConfig)
|
|
561
537
|
.then(() => 'refreshed');
|
|
562
538
|
}
|
|
563
539
|
default: {
|
|
@@ -323,6 +323,15 @@ const MoliAnalyticsModule = ({ config }) => (React.createElement(React.Fragment,
|
|
|
323
323
|
"ms"))));
|
|
324
324
|
const StylesModule = ({ config }) => (React.createElement(Row, { label: "Stylesheet" },
|
|
325
325
|
React.createElement(Tag, null, config.href)));
|
|
326
|
+
const InlineAiModule = ({ config }) => (React.createElement(React.Fragment, null,
|
|
327
|
+
React.createElement(Row, { label: "Publisher ID" },
|
|
328
|
+
React.createElement(Tag, null, config.publisherId)),
|
|
329
|
+
React.createElement(Row, { label: "Mode" },
|
|
330
|
+
React.createElement(Tag, null, config.mode)),
|
|
331
|
+
React.createElement(Row, { label: "Script URL" },
|
|
332
|
+
React.createElement(Tag, { variant: "grey" }, config.scriptUrl)),
|
|
333
|
+
React.createElement(Row, { label: "Placements" },
|
|
334
|
+
React.createElement(ListTags, { values: config.placements?.map(placement => placement.name) }))));
|
|
326
335
|
const GenericModule = ({ config, subEntry }) => (React.createElement(React.Fragment, null, Object.entries(config)
|
|
327
336
|
.filter(([key]) => key !== 'enabled')
|
|
328
337
|
.map(([key, value]) => (React.createElement(TagContainer, { key: key, subEntry: subEntry },
|
|
@@ -338,6 +347,7 @@ const moduleComponents = {
|
|
|
338
347
|
emetriq: EmetriqModule,
|
|
339
348
|
geoedge: GeoEdgeModule,
|
|
340
349
|
identitylink: IdentityLinkModule,
|
|
350
|
+
inlineAi: InlineAiModule,
|
|
341
351
|
pubstack: PubstackModule,
|
|
342
352
|
skin: SkinModule,
|
|
343
353
|
styles: StylesModule,
|
package/lib/gen/packageJson.js
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export const addNewInfiniteSlotToConfig = (config, idOfConfiguredSlot, artificialIdOfNewSlot, logger) => {
|
|
2
|
-
const configuredInfiniteAdSlot = config.slots.find(configSlot => configSlot.domId === idOfConfiguredSlot);
|
|
3
|
-
if (configuredInfiniteAdSlot) {
|
|
4
|
-
const newAdSlot = { ...configuredInfiniteAdSlot, domId: artificialIdOfNewSlot };
|
|
5
|
-
return { ...config, slots: [...config.slots, newAdSlot] };
|
|
6
|
-
}
|
|
7
|
-
else {
|
|
8
|
-
logger.error('MoliGlobal', `no infinite ad slot configured!`, config);
|
|
9
|
-
return config;
|
|
10
|
-
}
|
|
11
|
-
};
|