@highfivve/ad-tag 5.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +149 -0
- package/lib/ads/a9.js +190 -0
- package/lib/ads/adPipeline.js +159 -0
- package/lib/ads/adService.js +251 -0
- package/lib/ads/adUnitPath.js +37 -0
- package/lib/ads/auctions/adRequestThrottling.js +22 -0
- package/lib/ads/auctions/biddersDisabling.js +90 -0
- package/lib/ads/auctions/frequencyCapping.js +189 -0
- package/lib/ads/auctions/interstitialContext.js +92 -0
- package/lib/ads/auctions/previousBidCpms.js +33 -0
- package/lib/ads/auctions/resume.js +3 -0
- package/lib/ads/bridge/bridge.js +62 -0
- package/lib/ads/consent.js +63 -0
- package/lib/ads/eventService.js +44 -0
- package/lib/ads/globalAuctionContext.js +98 -0
- package/lib/ads/googleAdManager.js +380 -0
- package/lib/ads/keyValues.js +1 -0
- package/lib/ads/labelConfigService.js +42 -0
- package/lib/ads/modules/ad-reload/adVisibilityService.js +158 -0
- package/lib/ads/modules/ad-reload/index.js +163 -0
- package/lib/ads/modules/ad-reload/userActivityService.js +70 -0
- package/lib/ads/modules/adex/adex-mapping.js +77 -0
- package/lib/ads/modules/adex/adexUtiq.js +15 -0
- package/lib/ads/modules/adex/index.js +142 -0
- package/lib/ads/modules/adex/sendAdvertisingId.js +20 -0
- package/lib/ads/modules/blocklist-url/index.js +118 -0
- package/lib/ads/modules/cleanup/index.js +93 -0
- package/lib/ads/modules/confiant/index.js +47 -0
- package/lib/ads/modules/emetriq/index.js +154 -0
- package/lib/ads/modules/emetriq/trackInApp.js +34 -0
- package/lib/ads/modules/emetriq/trackLoginEvent.js +59 -0
- package/lib/ads/modules/generic-skin/index.js +150 -0
- package/lib/ads/modules/identitylink/index.js +58 -0
- package/lib/ads/modules/interstitial/index.js +38 -0
- package/lib/ads/modules/interstitial/interstitialAd.js +111 -0
- package/lib/ads/modules/lazy-load/index.js +191 -0
- package/lib/ads/modules/lazy-load/selectInfiniteSlot.js +11 -0
- package/lib/ads/modules/prebid-first-party-data/index.js +115 -0
- package/lib/ads/modules/pubstack/index.js +59 -0
- package/lib/ads/modules/sticky-footer-ad/desktopFloorAd.js +63 -0
- package/lib/ads/modules/sticky-footer-ad/index.js +43 -0
- package/lib/ads/modules/sticky-footer-ad/mobileSticky.js +93 -0
- package/lib/ads/modules/sticky-footer-ad-v2/footerStickyAd.js +118 -0
- package/lib/ads/modules/sticky-footer-ad-v2/index.js +43 -0
- package/lib/ads/modules/sticky-header-ad/fadeOutCallback.js +25 -0
- package/lib/ads/modules/sticky-header-ad/index.js +103 -0
- package/lib/ads/modules/sticky-header-ad/renderResult.js +24 -0
- package/lib/ads/modules/utiq/index.js +79 -0
- package/lib/ads/modules/yield-optimization/dynamicFloorPrice.js +86 -0
- package/lib/ads/modules/yield-optimization/index.js +57 -0
- package/lib/ads/modules/yield-optimization/isYieldOptimizationConfigDynamic.js +6 -0
- package/lib/ads/modules/yield-optimization/yieldOptimizationService.js +169 -0
- package/lib/ads/modules/zeotap/index.js +111 -0
- package/lib/ads/moli.js +645 -0
- package/lib/ads/moliGlobal.js +11 -0
- package/lib/ads/prebid-outstream.js +13 -0
- package/lib/ads/prebid.js +406 -0
- package/lib/ads/sizeConfigService.js +49 -0
- package/lib/ads/spa.js +32 -0
- package/lib/bundle/adReload.js +2 -0
- package/lib/bundle/adex.js +2 -0
- package/lib/bundle/blocklistUrls.js +2 -0
- package/lib/bundle/cleanup.js +2 -0
- package/lib/bundle/confiant.js +2 -0
- package/lib/bundle/configureFromEndpoint.js +40 -0
- package/lib/bundle/emetriq.js +2 -0
- package/lib/bundle/identityLink.js +2 -0
- package/lib/bundle/init.js +2 -0
- package/lib/bundle/interstitialModule.js +2 -0
- package/lib/bundle/lazyLoad.js +2 -0
- package/lib/bundle/prebidFirstPartyData.js +2 -0
- package/lib/bundle/pubstack.js +2 -0
- package/lib/bundle/skin.js +2 -0
- package/lib/bundle/stickyFooterAd.js +2 -0
- package/lib/bundle/stickyFooterAds2.js +2 -0
- package/lib/bundle/stickyHeaderAd.js +2 -0
- package/lib/bundle/utiq.js +2 -0
- package/lib/bundle/yieldOptimization.js +2 -0
- package/lib/bundle/zeotap.js +2 -0
- package/lib/console/components/adSlotConfig.js +225 -0
- package/lib/console/components/consentConfig.js +138 -0
- package/lib/console/components/globalConfig.js +634 -0
- package/lib/console/components/labelConfigDebug.js +19 -0
- package/lib/console/components/sizeConfigDebug.js +34 -0
- package/lib/console/components/tag.js +4 -0
- package/lib/console/debug.js +38 -0
- package/lib/console/util/array.js +1 -0
- package/lib/console/util/calculateAdDensity.js +36 -0
- package/lib/console/util/debounce.js +12 -0
- package/lib/console/util/debugLogger.js +6 -0
- package/lib/console/util/extractPositionFromPath.js +8 -0
- package/lib/console/util/prebid.js +3 -0
- package/lib/console/util/stringUtils.js +23 -0
- package/lib/console/util/themingService.js +31 -0
- package/lib/console/util/windowResizeService.js +22 -0
- package/lib/console/validations/adReloadValidations.js +47 -0
- package/lib/console/validations/bucketValidations.js +78 -0
- package/lib/console/validations/sizesConfigValidations.js +97 -0
- package/lib/gen/packageJson.js +3 -0
- package/lib/index.js +3 -0
- package/lib/types/apstag.js +1 -0
- package/lib/types/dom.js +1 -0
- package/lib/types/emetriq.js +1 -0
- package/lib/types/googletag.js +1 -0
- package/lib/types/identitylink.js +1 -0
- package/lib/types/module.js +1 -0
- package/lib/types/moliConfig.js +1 -0
- package/lib/types/moliRuntime.js +1 -0
- package/lib/types/prebidjs.js +45 -0
- package/lib/types/supplyChainObject.js +1 -0
- package/lib/types/tcfapi.js +48 -0
- package/lib/util/addNewInfiniteSlotToConfig.js +11 -0
- package/lib/util/arrayUtils.js +9 -0
- package/lib/util/assetLoaderService.js +91 -0
- package/lib/util/browserStorageKeys.js +7 -0
- package/lib/util/debugDelay.js +12 -0
- package/lib/util/domready.js +15 -0
- package/lib/util/environmentOverride.js +16 -0
- package/lib/util/extractAdTagVersion.js +16 -0
- package/lib/util/extractTopPrivateDomainFromHostname.js +17 -0
- package/lib/util/localStorage.js +26 -0
- package/lib/util/logging.js +106 -0
- package/lib/util/objectUtils.js +29 -0
- package/lib/util/query.js +40 -0
- package/lib/util/queryParameters.js +5 -0
- package/lib/util/resolveOverrides.js +21 -0
- package/lib/util/resolveStoredRequestIdInOrtb2Object.js +12 -0
- package/lib/util/sizes.js +3 -0
- package/lib/util/test-slots.js +150 -0
- package/lib/util/uuid.js +10 -0
- package/package.json +127 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { mkPrepareRequestAdsStep, HIGH_PRIORITY, mkConfigureStepOncePerRequestAdsCycle } from 'ad-tag/ads/adPipeline';
|
|
2
|
+
export class Cleanup {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.name = 'cleanup';
|
|
5
|
+
this.description = 'cleanup out-of-page formats on navigation or ad-reload';
|
|
6
|
+
this.moduleType = 'creatives';
|
|
7
|
+
this.cleanupConfig = null;
|
|
8
|
+
this.cleanUp = (context, configs) => {
|
|
9
|
+
configs.forEach(config => {
|
|
10
|
+
if ('cssSelectors' in config.deleteMethod) {
|
|
11
|
+
config.deleteMethod.cssSelectors.forEach((selector) => {
|
|
12
|
+
const elements = context.window__.document.querySelectorAll(selector);
|
|
13
|
+
context.logger__.debug('Cleanup Module', `Remove elements with selector ${selector} from dom`, elements);
|
|
14
|
+
elements.forEach((element) => {
|
|
15
|
+
try {
|
|
16
|
+
element.remove();
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
context.logger__.error('Cleanup Module', `Error removing element with selector ${selector}`, e);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
config.deleteMethod.jsAsString.forEach(jsLineAsString => {
|
|
26
|
+
try {
|
|
27
|
+
context.logger__.debug('Cleanup Module', `Try to execute string as JS: '${jsLineAsString}'`);
|
|
28
|
+
const jsFunction = new Function(jsLineAsString);
|
|
29
|
+
jsFunction();
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
context.logger__.error('Cleanup Module', `Error executing JS string: '${jsLineAsString}'`, e);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
this.hasBidderWonLastAuction = (context, config) => {
|
|
39
|
+
const prebidWinningBids = context.window__.pbjs.getAllWinningBids();
|
|
40
|
+
const bidderThatWonLastAuctionOnSlot = prebidWinningBids
|
|
41
|
+
.filter(bid => bid.adUnitCode === config.domId)
|
|
42
|
+
.at(-1)?.bidder;
|
|
43
|
+
return bidderThatWonLastAuctionOnSlot === config.bidder;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
configure__(modulesConfig) {
|
|
47
|
+
if (modulesConfig?.cleanup && modulesConfig.cleanup.enabled) {
|
|
48
|
+
this.cleanupConfig = modulesConfig.cleanup;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
config__() {
|
|
52
|
+
return this.cleanupConfig;
|
|
53
|
+
}
|
|
54
|
+
initSteps__() {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
configureSteps__() {
|
|
58
|
+
const config = this.cleanupConfig;
|
|
59
|
+
return config
|
|
60
|
+
? [
|
|
61
|
+
mkConfigureStepOncePerRequestAdsCycle('destroy-out-of-page-ad-format', (context) => {
|
|
62
|
+
if (context.runtimeConfig__.environment === 'test') {
|
|
63
|
+
return Promise.resolve();
|
|
64
|
+
}
|
|
65
|
+
context.window__.pbjs.que.push(() => {
|
|
66
|
+
const configsOfDomIdsThatNeedToBeCleaned = config.configs.filter(config => this.hasBidderWonLastAuction(context, config));
|
|
67
|
+
this.cleanUp(context, configsOfDomIdsThatNeedToBeCleaned);
|
|
68
|
+
});
|
|
69
|
+
return Promise.resolve();
|
|
70
|
+
})
|
|
71
|
+
]
|
|
72
|
+
: [];
|
|
73
|
+
}
|
|
74
|
+
prepareRequestAdsSteps__() {
|
|
75
|
+
const config = this.cleanupConfig;
|
|
76
|
+
return config
|
|
77
|
+
? [
|
|
78
|
+
mkPrepareRequestAdsStep('cleanup-before-ad-reload', HIGH_PRIORITY, (context, slots) => {
|
|
79
|
+
if (context.runtimeConfig__.environment === 'test') {
|
|
80
|
+
return Promise.resolve();
|
|
81
|
+
}
|
|
82
|
+
context.window__.pbjs.que.push(() => {
|
|
83
|
+
const configsOfDomIdsThatNeedToBeCleaned = config.configs
|
|
84
|
+
.filter(config => slots.map(slot => slot.moliSlot.domId).includes(config.domId))
|
|
85
|
+
.filter(config => this.hasBidderWonLastAuction(context, config));
|
|
86
|
+
this.cleanUp(context, configsOfDomIdsThatNeedToBeCleaned);
|
|
87
|
+
});
|
|
88
|
+
return Promise.resolve();
|
|
89
|
+
})
|
|
90
|
+
]
|
|
91
|
+
: [];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { AssetLoadMethod } from 'ad-tag/util/assetLoaderService';
|
|
2
|
+
import { mkInitStep } from 'ad-tag/ads/adPipeline';
|
|
3
|
+
export class Confiant {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.name = 'confiant';
|
|
6
|
+
this.description = 'ad fraud detection and protection module';
|
|
7
|
+
this.moduleType = 'ad-fraud';
|
|
8
|
+
this.gvlid = '56';
|
|
9
|
+
this.confiantConfig = null;
|
|
10
|
+
}
|
|
11
|
+
config__() {
|
|
12
|
+
return this.confiantConfig;
|
|
13
|
+
}
|
|
14
|
+
configure__(moduleConfig) {
|
|
15
|
+
if (moduleConfig?.confiant && moduleConfig.confiant.enabled) {
|
|
16
|
+
this.confiantConfig = moduleConfig.confiant;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
initSteps__() {
|
|
20
|
+
const config = this.confiantConfig;
|
|
21
|
+
return config ? [mkInitStep('confiant-init', ctx => this.loadConfiant(ctx, config))] : [];
|
|
22
|
+
}
|
|
23
|
+
loadConfiant(context, config) {
|
|
24
|
+
if (context.env__ === 'test') {
|
|
25
|
+
return Promise.resolve();
|
|
26
|
+
}
|
|
27
|
+
if (context.tcData__.gdprApplies &&
|
|
28
|
+
(!context.tcData__.purpose.consents['1'] ||
|
|
29
|
+
!(!this.confiantConfig?.checkGVLID || context.tcData__.vendor.consents[this.gvlid]))) {
|
|
30
|
+
return Promise.resolve();
|
|
31
|
+
}
|
|
32
|
+
context.assetLoaderService__
|
|
33
|
+
.loadScript({
|
|
34
|
+
name: this.name,
|
|
35
|
+
loadMethod: AssetLoadMethod.TAG,
|
|
36
|
+
assetUrl: config.assetUrl
|
|
37
|
+
})
|
|
38
|
+
.catch(error => context.logger__.error('failed to load confiant', error));
|
|
39
|
+
return Promise.resolve();
|
|
40
|
+
}
|
|
41
|
+
configureSteps__() {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
prepareRequestAdsSteps__() {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { AssetLoadMethod } from 'ad-tag/util/assetLoaderService';
|
|
2
|
+
import { trackInApp } from './trackInApp';
|
|
3
|
+
import { shouldTrackLoginEvent, trackLoginEvent } from './trackLoginEvent';
|
|
4
|
+
import { mkConfigureStepOncePerRequestAdsCycle, mkInitStep } from 'ad-tag/ads/adPipeline';
|
|
5
|
+
export class Emetriq {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.name = 'emetriq';
|
|
8
|
+
this.description = 'Provides Emetriq data collection functionality to Moli.';
|
|
9
|
+
this.moduleType = 'dmp';
|
|
10
|
+
this.gvlid = '213';
|
|
11
|
+
this.emetriqConfig = null;
|
|
12
|
+
}
|
|
13
|
+
config__() {
|
|
14
|
+
return this.emetriqConfig;
|
|
15
|
+
}
|
|
16
|
+
configure__(moduleConfig) {
|
|
17
|
+
if (moduleConfig?.emetriq && moduleConfig.emetriq.enabled) {
|
|
18
|
+
this.emetriqConfig = moduleConfig.emetriq;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
initSteps__() {
|
|
22
|
+
const config = this.emetriqConfig;
|
|
23
|
+
return config
|
|
24
|
+
? [
|
|
25
|
+
mkInitStep('load-emetriq', ctx => {
|
|
26
|
+
const customParams = Emetriq.staticCustomParams({ ...ctx.config__.targeting?.keyValues, ...ctx.runtimeConfig__.keyValues }, config.customMappingDefinition);
|
|
27
|
+
if (ctx.env__ === 'test') {
|
|
28
|
+
return Promise.resolve();
|
|
29
|
+
}
|
|
30
|
+
if (this.checkIfConsentIsMissing(ctx)) {
|
|
31
|
+
return Promise.resolve();
|
|
32
|
+
}
|
|
33
|
+
Emetriq.syncDelay(ctx, config.syncDelay).then(additionalIdentifier => {
|
|
34
|
+
switch (config.os) {
|
|
35
|
+
case 'web':
|
|
36
|
+
this.loadEmetriqScript(ctx, config, additionalIdentifier, customParams);
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return Promise.resolve();
|
|
41
|
+
})
|
|
42
|
+
]
|
|
43
|
+
: [];
|
|
44
|
+
}
|
|
45
|
+
configureSteps__() {
|
|
46
|
+
const config = this.emetriqConfig;
|
|
47
|
+
return config
|
|
48
|
+
? [
|
|
49
|
+
mkConfigureStepOncePerRequestAdsCycle('track-emetriq', ctx => {
|
|
50
|
+
const customParams = Emetriq.staticCustomParams({ ...ctx.config__.targeting?.keyValues, ...ctx.runtimeConfig__.keyValues }, config.customMappingDefinition);
|
|
51
|
+
if (ctx.env__ === 'test') {
|
|
52
|
+
return Promise.resolve();
|
|
53
|
+
}
|
|
54
|
+
if (this.checkIfConsentIsMissing(ctx)) {
|
|
55
|
+
return Promise.resolve();
|
|
56
|
+
}
|
|
57
|
+
if (config.login &&
|
|
58
|
+
shouldTrackLoginEvent(ctx.window__.sessionStorage, Date.now(), ctx.logger__)) {
|
|
59
|
+
trackLoginEvent(ctx, config, ctx.window__.document, ctx.logger__);
|
|
60
|
+
}
|
|
61
|
+
Emetriq.syncDelay(ctx, config.syncDelay).then(additionalIdentifier => {
|
|
62
|
+
switch (config.os) {
|
|
63
|
+
case 'android':
|
|
64
|
+
case 'ios':
|
|
65
|
+
trackInApp(ctx, config, additionalIdentifier, customParams, ctx.window__.document);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return Promise.resolve();
|
|
70
|
+
})
|
|
71
|
+
]
|
|
72
|
+
: [];
|
|
73
|
+
}
|
|
74
|
+
prepareRequestAdsSteps__() {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
checkIfConsentIsMissing(ctx) {
|
|
78
|
+
if (ctx.tcData__.gdprApplies && !ctx.tcData__.vendor.consents[this.gvlid]) {
|
|
79
|
+
ctx.logger__.warn(this.name, 'missing consent');
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
loadEmetriqScript(context, webConfig, additionalIdentifier, additionalCustomParams) {
|
|
85
|
+
const window = context.window__;
|
|
86
|
+
window._enqAdpParam = {
|
|
87
|
+
...webConfig._enqAdpParam,
|
|
88
|
+
...additionalIdentifier,
|
|
89
|
+
...additionalCustomParams
|
|
90
|
+
};
|
|
91
|
+
return context.assetLoaderService__
|
|
92
|
+
.loadScript({
|
|
93
|
+
name: this.name,
|
|
94
|
+
loadMethod: AssetLoadMethod.TAG,
|
|
95
|
+
assetUrl: `https://ups.xplosion.de/loader/${webConfig._enqAdpParam.sid}/default.js`
|
|
96
|
+
})
|
|
97
|
+
.catch(error => context.logger__.error('failed to load emetriq', error));
|
|
98
|
+
}
|
|
99
|
+
static syncDelay(ctx, delay) {
|
|
100
|
+
if (delay) {
|
|
101
|
+
if (typeof delay === 'number') {
|
|
102
|
+
return new Promise(resolve => ctx.window__.setTimeout(() => resolve({}), delay));
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
if (ctx.window__.pbjs) {
|
|
106
|
+
return new Promise(resolve => {
|
|
107
|
+
ctx.window__.pbjs.que.push(() => {
|
|
108
|
+
const listener = () => {
|
|
109
|
+
resolve(Emetriq.prebidIdentifiers(ctx));
|
|
110
|
+
ctx.window__.pbjs.offEvent('auctionEnd', listener);
|
|
111
|
+
};
|
|
112
|
+
ctx.window__.pbjs.onEvent('auctionEnd', listener);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
ctx.logger__.error('emetriq', 'No sync delay, because window.pbjs is not defined!');
|
|
118
|
+
return Promise.resolve({});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return Promise.resolve({});
|
|
123
|
+
}
|
|
124
|
+
static staticCustomParams(targeting, mappings) {
|
|
125
|
+
let additionalCustomParams = {};
|
|
126
|
+
(mappings ?? []).forEach(({ param, key }) => {
|
|
127
|
+
const value = targeting[key];
|
|
128
|
+
if (value) {
|
|
129
|
+
additionalCustomParams[param] = typeof value === 'string' ? value : value.join(',');
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
return additionalCustomParams;
|
|
133
|
+
}
|
|
134
|
+
static prebidIdentifiers(ctx) {
|
|
135
|
+
const identifier = {};
|
|
136
|
+
const userIds = ctx.window__.pbjs.getUserIds();
|
|
137
|
+
if (userIds.amxId) {
|
|
138
|
+
identifier.id_amxid = userIds.amxId;
|
|
139
|
+
}
|
|
140
|
+
if (userIds.idl_env) {
|
|
141
|
+
identifier.id_liveramp = userIds.idl_env;
|
|
142
|
+
}
|
|
143
|
+
if (userIds.IDP) {
|
|
144
|
+
identifier.id_zeotap = userIds.IDP;
|
|
145
|
+
}
|
|
146
|
+
if (userIds.pubcid) {
|
|
147
|
+
identifier.id_sharedid = userIds.pubcid;
|
|
148
|
+
}
|
|
149
|
+
if (userIds.id5id) {
|
|
150
|
+
identifier.id_id5 = userIds.id5id.uid;
|
|
151
|
+
}
|
|
152
|
+
return identifier;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const extractDeviceIdParam = (context, advertiserIdKey) => {
|
|
2
|
+
const deviceId = context.config__.targeting?.keyValues[advertiserIdKey];
|
|
3
|
+
if (deviceId) {
|
|
4
|
+
return `&device_id=${typeof deviceId === 'string' ? deviceId : deviceId[0]}`;
|
|
5
|
+
}
|
|
6
|
+
return '';
|
|
7
|
+
};
|
|
8
|
+
export const trackInApp = (context, appConfig, additionalIdentifier, additionalCustomParams, document) => {
|
|
9
|
+
const deviceIdParam = extractDeviceIdParam(context, appConfig.advertiserIdKey);
|
|
10
|
+
const consentString = context.tcData__.gdprApplies
|
|
11
|
+
? `gdpr=1&gdpr_consent=${context.tcData__.tcString}`
|
|
12
|
+
: 'gdpr=0';
|
|
13
|
+
const linkParam = appConfig.linkOrKeyword.link
|
|
14
|
+
? `&link=${encodeURIComponent(appConfig.linkOrKeyword.link)}`
|
|
15
|
+
: '';
|
|
16
|
+
const keywordsParam = appConfig.linkOrKeyword.keywords
|
|
17
|
+
? `&keywords=${encodeURIComponent(appConfig.linkOrKeyword.keywords)}`
|
|
18
|
+
: '';
|
|
19
|
+
let additionalIdsParam = '';
|
|
20
|
+
const identifiers = { ...appConfig.additionalIdentifier, ...additionalIdentifier };
|
|
21
|
+
Object.entries(identifiers).forEach(([key, value]) => {
|
|
22
|
+
additionalIdsParam += `&${key}=${encodeURIComponent(value)}`;
|
|
23
|
+
});
|
|
24
|
+
let additionalCustomParam = '';
|
|
25
|
+
const customParams = { ...appConfig.customKeywords, ...additionalCustomParams };
|
|
26
|
+
Object.entries(customParams).forEach(([key, value]) => {
|
|
27
|
+
additionalCustomParam += `&${key}=${encodeURIComponent(value)}`;
|
|
28
|
+
});
|
|
29
|
+
const pixel = document.createElement('img');
|
|
30
|
+
pixel.src = `https://aps.xplosion.de/data?sid=${appConfig.sid}${deviceIdParam}&os=${appConfig.os}&app_id=${appConfig.appId}${keywordsParam}${linkParam}${additionalIdsParam}${additionalCustomParam}&${consentString}`;
|
|
31
|
+
pixel.width = 1;
|
|
32
|
+
pixel.height = 1;
|
|
33
|
+
document.body.append(pixel);
|
|
34
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const extractAdIdOrIdfa = (context, moduleConfig) => {
|
|
2
|
+
if (moduleConfig.os === 'web') {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
const deviceId = context.config__.targeting?.keyValues[moduleConfig.advertiserIdKey];
|
|
6
|
+
if (deviceId) {
|
|
7
|
+
return typeof deviceId === 'string' ? deviceId : deviceId[0];
|
|
8
|
+
}
|
|
9
|
+
return;
|
|
10
|
+
};
|
|
11
|
+
export const shouldTrackLoginEvent = (storage, currentDate, logger) => {
|
|
12
|
+
try {
|
|
13
|
+
const oneDayMilliseconds = 86400000;
|
|
14
|
+
const key = 'moli_emetriq';
|
|
15
|
+
const value = storage.getItem(key);
|
|
16
|
+
const storedDate = value ? Number.parseInt(value, 10) : 0;
|
|
17
|
+
const shouldTrack = currentDate - oneDayMilliseconds > storedDate;
|
|
18
|
+
if (shouldTrack) {
|
|
19
|
+
storage.setItem(key, currentDate.toString());
|
|
20
|
+
logger.debug('emetriq', `eligible for login event tracking. Last tracked at ${new Date(storedDate)}`);
|
|
21
|
+
}
|
|
22
|
+
return shouldTrack;
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
logger.error('emetriq', 'could not access session storage', e);
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
export const trackLoginEvent = (context, moduleConfig, document, logger) => {
|
|
30
|
+
if (!moduleConfig.login) {
|
|
31
|
+
logger.warn('emetriq', 'login configuration missing!');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const url = new URL(`https://xdn-ttp.de/lns/import-event-${moduleConfig.login.partner}`);
|
|
35
|
+
url.searchParams.append('guid', moduleConfig.login.guid);
|
|
36
|
+
if (context.tcData__.gdprApplies) {
|
|
37
|
+
url.searchParams.append('gdpr', '1');
|
|
38
|
+
url.searchParams.append('gdpr_consent', context.tcData__.tcString);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
url.searchParams.append('gdpr', '0');
|
|
42
|
+
}
|
|
43
|
+
const adIdOrIdfa = extractAdIdOrIdfa(context, moduleConfig);
|
|
44
|
+
if (adIdOrIdfa) {
|
|
45
|
+
switch (moduleConfig.os) {
|
|
46
|
+
case 'android':
|
|
47
|
+
url.searchParams.append('adid', adIdOrIdfa);
|
|
48
|
+
break;
|
|
49
|
+
case 'ios':
|
|
50
|
+
url.searchParams.append('idfa', adIdOrIdfa);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const pixel = document.createElement('img');
|
|
55
|
+
pixel.src = url.href;
|
|
56
|
+
pixel.width = 1;
|
|
57
|
+
pixel.height = 1;
|
|
58
|
+
document.body.append(pixel);
|
|
59
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { prebidjs } from 'ad-tag/types/prebidjs';
|
|
2
|
+
import { flatten, isNotNull, uniquePrimitiveFilter } from 'ad-tag/util/arrayUtils';
|
|
3
|
+
export const filterHighestNonSkinBid = (bidResponses, blockedAdSlotDomIds) => {
|
|
4
|
+
const adSlotIds = Object.keys(bidResponses);
|
|
5
|
+
return flatten(adSlotIds
|
|
6
|
+
.map(domId => ({ adSlotId: domId, ...bidResponses[domId] }))
|
|
7
|
+
.filter(bidObject => blockedAdSlotDomIds.indexOf(bidObject.adSlotId) > -1)
|
|
8
|
+
.filter(bidObject => isNotNull(bidObject.bids))
|
|
9
|
+
.map(bidObject => bidObject
|
|
10
|
+
.bids
|
|
11
|
+
.sort((bid1, bid2) => bid2.cpm - bid1.cpm)
|
|
12
|
+
.slice(0, 1)));
|
|
13
|
+
};
|
|
14
|
+
export class Skin {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.name = 'skin';
|
|
17
|
+
this.description = 'Block other ad slots if a wallpaper has won the auction';
|
|
18
|
+
this.moduleType = 'prebid';
|
|
19
|
+
this.skinModuleConfig = null;
|
|
20
|
+
this.bidsBackHandler = [];
|
|
21
|
+
this.getConfigEffect = (config, bidResponses, log) => {
|
|
22
|
+
const skinBidResponse = bidResponses[config.skinAdSlotDomId];
|
|
23
|
+
const isSkinBid = (bid) => {
|
|
24
|
+
const oneFilterApplied = config.formatFilter.some(filter => {
|
|
25
|
+
switch (filter.bidder) {
|
|
26
|
+
case '*':
|
|
27
|
+
return true;
|
|
28
|
+
case 'gumgum':
|
|
29
|
+
return (bid.bidder === prebidjs.GumGum &&
|
|
30
|
+
(filter.auid === undefined ||
|
|
31
|
+
(typeof bid.ad !== 'string' && bid.ad.auid === filter.auid)));
|
|
32
|
+
default:
|
|
33
|
+
return bid.bidder === filter.bidder;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return bid.cpm > 0 && oneFilterApplied;
|
|
37
|
+
};
|
|
38
|
+
const nonSkinBids = filterHighestNonSkinBid(bidResponses, config.blockedAdSlotDomIds);
|
|
39
|
+
const combinedNonSkinCpm = nonSkinBids.reduce((prev, current) => prev + current.cpm, 0);
|
|
40
|
+
const skinBids = skinBidResponse
|
|
41
|
+
?
|
|
42
|
+
skinBidResponse.bids.filter(isSkinBid).sort((bid1, bid2) => bid2.cpm - bid1.cpm)
|
|
43
|
+
: [];
|
|
44
|
+
const skinConfigEffect = skinBids.length > 0
|
|
45
|
+
? skinBids[0].cpm > combinedNonSkinCpm
|
|
46
|
+
?
|
|
47
|
+
"BlockOtherSlots"
|
|
48
|
+
:
|
|
49
|
+
"BlockSkinSlot"
|
|
50
|
+
:
|
|
51
|
+
"NoBlocking";
|
|
52
|
+
log.debug(this.name, 'nonSkinBids', nonSkinBids);
|
|
53
|
+
log.debug(this.name, 'skinBids', skinBids);
|
|
54
|
+
if (config.enableCpmComparison) {
|
|
55
|
+
return skinConfigEffect;
|
|
56
|
+
}
|
|
57
|
+
return skinBids.length > 0 ? "BlockOtherSlots" : "NoBlocking";
|
|
58
|
+
};
|
|
59
|
+
this.selectConfig = (skinModuleConfig, bidResponses, log) => skinModuleConfig.configs
|
|
60
|
+
.map(config => ({
|
|
61
|
+
skinConfig: config,
|
|
62
|
+
configEffect: this.getConfigEffect(config, bidResponses, log)
|
|
63
|
+
}))
|
|
64
|
+
.find(({ configEffect }) => configEffect !== "NoBlocking");
|
|
65
|
+
this.destroyAdSlot = (slotDefinitions, gWindow) => (adSlotDomId) => {
|
|
66
|
+
const adSlots = slotDefinitions
|
|
67
|
+
.map(slot => slot.adSlot)
|
|
68
|
+
.filter((slot) => slot.getSlotElementId() === adSlotDomId);
|
|
69
|
+
gWindow.googletag.destroySlots(adSlots);
|
|
70
|
+
};
|
|
71
|
+
this.runSkinConfigs = (skinModuleConfig) => (ctx, bidResponses, slotDefinitions) => {
|
|
72
|
+
const skinConfigWithEffect = this.selectConfig(skinModuleConfig, bidResponses, ctx.logger__);
|
|
73
|
+
if (skinConfigWithEffect) {
|
|
74
|
+
const { skinConfig, configEffect } = skinConfigWithEffect;
|
|
75
|
+
if (configEffect === "BlockOtherSlots") {
|
|
76
|
+
ctx.logger__.debug('SkinModule', 'Skin configuration applied', skinConfig);
|
|
77
|
+
skinConfig.blockedAdSlotDomIds.forEach(this.destroyAdSlot(slotDefinitions, ctx.window__));
|
|
78
|
+
if (skinConfig.hideBlockedSlots) {
|
|
79
|
+
skinConfig.blockedAdSlotDomIds.forEach(this.hideAdSlot(ctx.window__, ctx.logger__));
|
|
80
|
+
}
|
|
81
|
+
if (skinConfig.hideSkinAdSlot) {
|
|
82
|
+
this.hideAdSlot(ctx.window__, ctx.logger__)(skinConfig.skinAdSlotDomId);
|
|
83
|
+
}
|
|
84
|
+
if (skinConfig.hideBlockedSlotsSelector) {
|
|
85
|
+
ctx.window__.document
|
|
86
|
+
.querySelectorAll(skinConfig.hideBlockedSlotsSelector)
|
|
87
|
+
.forEach(node => {
|
|
88
|
+
ctx.logger__.debug('SkinModule', `Set display:none for container with selector ${skinConfig.hideBlockedSlotsSelector}`);
|
|
89
|
+
node.style.setProperty('display', 'none');
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (skinConfig.targeting) {
|
|
93
|
+
try {
|
|
94
|
+
ctx.window__.googletag
|
|
95
|
+
.pubads()
|
|
96
|
+
.setTargeting(skinConfig.targeting.key, skinConfig.targeting.value ?? '1');
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
ctx.logger__.error('SkinModule', e);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (skinConfig.enableCpmComparison) {
|
|
104
|
+
ctx.logger__.debug('SkinModule', 'Skin configuration ignored because cpm was low', skinConfig);
|
|
105
|
+
this.destroyAdSlot(slotDefinitions, ctx.window__)(skinConfig.skinAdSlotDomId);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
skinModuleConfig.configs
|
|
110
|
+
.filter(skinConfig => skinConfig.destroySkinSlot)
|
|
111
|
+
.map(skinConfig => skinConfig.skinAdSlotDomId)
|
|
112
|
+
.filter(uniquePrimitiveFilter)
|
|
113
|
+
.forEach(this.destroyAdSlot(slotDefinitions, ctx.window__));
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
this.hideAdSlot = (_window, log) => (domId) => {
|
|
117
|
+
const element = _window.document.getElementById(domId);
|
|
118
|
+
try {
|
|
119
|
+
if (element) {
|
|
120
|
+
log.debug('SkinModule', `Set display:none for ${domId}`);
|
|
121
|
+
element.style.setProperty('display', 'none');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
log.error('SkinModule', `Couldn't set the the wallpaper div ${domId} to display:none;`, e);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
config__() {
|
|
130
|
+
return this.skinModuleConfig;
|
|
131
|
+
}
|
|
132
|
+
configure__(moduleConfig) {
|
|
133
|
+
if (moduleConfig?.skin && moduleConfig.skin.enabled) {
|
|
134
|
+
this.skinModuleConfig = moduleConfig.skin;
|
|
135
|
+
this.bidsBackHandler.push(this.runSkinConfigs(moduleConfig.skin));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
initSteps__() {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
configureSteps__() {
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
prepareRequestAdsSteps__() {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
prebidBidsBackHandler__() {
|
|
148
|
+
return this.bidsBackHandler;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { mkInitStep } from 'ad-tag/ads/adPipeline';
|
|
2
|
+
import { AssetLoadMethod } from 'ad-tag/util/assetLoaderService';
|
|
3
|
+
export class IdentityLink {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.name = 'identitylink';
|
|
6
|
+
this.description = "Provides LiveRamp's ATS (authenticated traffic solution) functionality to Moli.";
|
|
7
|
+
this.moduleType = 'identity';
|
|
8
|
+
this.gvlid = '97';
|
|
9
|
+
this.identityLinkConfig = null;
|
|
10
|
+
}
|
|
11
|
+
config__() {
|
|
12
|
+
return this.identityLinkConfig;
|
|
13
|
+
}
|
|
14
|
+
configure__(moduleConfig) {
|
|
15
|
+
if (moduleConfig?.identitylink && moduleConfig.identitylink.enabled) {
|
|
16
|
+
this.identityLinkConfig = moduleConfig.identitylink;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
initSteps__() {
|
|
20
|
+
const config = this.identityLinkConfig;
|
|
21
|
+
return config
|
|
22
|
+
? [
|
|
23
|
+
mkInitStep(this.name, ctx => {
|
|
24
|
+
this.loadAts(ctx, config);
|
|
25
|
+
return Promise.resolve();
|
|
26
|
+
})
|
|
27
|
+
]
|
|
28
|
+
: [];
|
|
29
|
+
}
|
|
30
|
+
configureSteps__() {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
prepareRequestAdsSteps__() {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
loadAts(context, moduleConfig) {
|
|
37
|
+
if (context.env__ === 'test') {
|
|
38
|
+
return Promise.resolve();
|
|
39
|
+
}
|
|
40
|
+
if (context.tcData__.gdprApplies && !context.tcData__.vendor.consents[this.gvlid]) {
|
|
41
|
+
return Promise.resolve();
|
|
42
|
+
}
|
|
43
|
+
const window = context.window__;
|
|
44
|
+
window.addEventListener('envelopeModuleReady', () => {
|
|
45
|
+
window.atsenvelopemodule.setAdditionalData({
|
|
46
|
+
type: 'emailHashes',
|
|
47
|
+
id: moduleConfig.hashedEmailAddresses
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
return context.assetLoaderService__
|
|
51
|
+
.loadScript({
|
|
52
|
+
name: this.name,
|
|
53
|
+
loadMethod: AssetLoadMethod.TAG,
|
|
54
|
+
assetUrl: `https://launchpad-wrapper.privacymanager.io/${moduleConfig.launchPadId}/launchpad-liveramp.js`
|
|
55
|
+
})
|
|
56
|
+
.catch(error => context.logger__.error('failed to load emetriq', error));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { LOW_PRIORITY, mkPrepareRequestAdsStep } from 'ad-tag/ads/adPipeline';
|
|
2
|
+
import { initInterstitialModule } from 'ad-tag/ads/modules/interstitial/interstitialAd';
|
|
3
|
+
export const createInterstitialModule = () => {
|
|
4
|
+
let interstitialModuleConfig = null;
|
|
5
|
+
return {
|
|
6
|
+
name: 'interstitial-module',
|
|
7
|
+
description: 'interstitial ad creatives',
|
|
8
|
+
moduleType: 'creatives',
|
|
9
|
+
config__() {
|
|
10
|
+
return interstitialModuleConfig;
|
|
11
|
+
},
|
|
12
|
+
configure__(moduleConfig) {
|
|
13
|
+
if (moduleConfig?.interstitial && moduleConfig.interstitial.enabled) {
|
|
14
|
+
interstitialModuleConfig = moduleConfig.interstitial;
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
prepareRequestAdsSteps__() {
|
|
18
|
+
const config = interstitialModuleConfig;
|
|
19
|
+
return config
|
|
20
|
+
? [
|
|
21
|
+
mkPrepareRequestAdsStep('interstitial-module', LOW_PRIORITY, (ctx, slots) => {
|
|
22
|
+
const interstitialSlot = slots.find(slot => slot.moliSlot.domId === config.interstitialDomId);
|
|
23
|
+
if (interstitialSlot) {
|
|
24
|
+
initInterstitialModule(ctx.window__, ctx.env__, ctx.logger__, interstitialSlot.moliSlot.domId, config.disallowedAdvertiserIds, config.closeAutomaticallyAfterMs);
|
|
25
|
+
}
|
|
26
|
+
return Promise.resolve();
|
|
27
|
+
})
|
|
28
|
+
]
|
|
29
|
+
: [];
|
|
30
|
+
},
|
|
31
|
+
configureSteps__() {
|
|
32
|
+
return [];
|
|
33
|
+
},
|
|
34
|
+
initSteps__() {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
};
|