@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.
Files changed (132) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +149 -0
  3. package/lib/ads/a9.js +190 -0
  4. package/lib/ads/adPipeline.js +159 -0
  5. package/lib/ads/adService.js +251 -0
  6. package/lib/ads/adUnitPath.js +37 -0
  7. package/lib/ads/auctions/adRequestThrottling.js +22 -0
  8. package/lib/ads/auctions/biddersDisabling.js +90 -0
  9. package/lib/ads/auctions/frequencyCapping.js +189 -0
  10. package/lib/ads/auctions/interstitialContext.js +92 -0
  11. package/lib/ads/auctions/previousBidCpms.js +33 -0
  12. package/lib/ads/auctions/resume.js +3 -0
  13. package/lib/ads/bridge/bridge.js +62 -0
  14. package/lib/ads/consent.js +63 -0
  15. package/lib/ads/eventService.js +44 -0
  16. package/lib/ads/globalAuctionContext.js +98 -0
  17. package/lib/ads/googleAdManager.js +380 -0
  18. package/lib/ads/keyValues.js +1 -0
  19. package/lib/ads/labelConfigService.js +42 -0
  20. package/lib/ads/modules/ad-reload/adVisibilityService.js +158 -0
  21. package/lib/ads/modules/ad-reload/index.js +163 -0
  22. package/lib/ads/modules/ad-reload/userActivityService.js +70 -0
  23. package/lib/ads/modules/adex/adex-mapping.js +77 -0
  24. package/lib/ads/modules/adex/adexUtiq.js +15 -0
  25. package/lib/ads/modules/adex/index.js +142 -0
  26. package/lib/ads/modules/adex/sendAdvertisingId.js +20 -0
  27. package/lib/ads/modules/blocklist-url/index.js +118 -0
  28. package/lib/ads/modules/cleanup/index.js +93 -0
  29. package/lib/ads/modules/confiant/index.js +47 -0
  30. package/lib/ads/modules/emetriq/index.js +154 -0
  31. package/lib/ads/modules/emetriq/trackInApp.js +34 -0
  32. package/lib/ads/modules/emetriq/trackLoginEvent.js +59 -0
  33. package/lib/ads/modules/generic-skin/index.js +150 -0
  34. package/lib/ads/modules/identitylink/index.js +58 -0
  35. package/lib/ads/modules/interstitial/index.js +38 -0
  36. package/lib/ads/modules/interstitial/interstitialAd.js +111 -0
  37. package/lib/ads/modules/lazy-load/index.js +191 -0
  38. package/lib/ads/modules/lazy-load/selectInfiniteSlot.js +11 -0
  39. package/lib/ads/modules/prebid-first-party-data/index.js +115 -0
  40. package/lib/ads/modules/pubstack/index.js +59 -0
  41. package/lib/ads/modules/sticky-footer-ad/desktopFloorAd.js +63 -0
  42. package/lib/ads/modules/sticky-footer-ad/index.js +43 -0
  43. package/lib/ads/modules/sticky-footer-ad/mobileSticky.js +93 -0
  44. package/lib/ads/modules/sticky-footer-ad-v2/footerStickyAd.js +118 -0
  45. package/lib/ads/modules/sticky-footer-ad-v2/index.js +43 -0
  46. package/lib/ads/modules/sticky-header-ad/fadeOutCallback.js +25 -0
  47. package/lib/ads/modules/sticky-header-ad/index.js +103 -0
  48. package/lib/ads/modules/sticky-header-ad/renderResult.js +24 -0
  49. package/lib/ads/modules/utiq/index.js +79 -0
  50. package/lib/ads/modules/yield-optimization/dynamicFloorPrice.js +86 -0
  51. package/lib/ads/modules/yield-optimization/index.js +57 -0
  52. package/lib/ads/modules/yield-optimization/isYieldOptimizationConfigDynamic.js +6 -0
  53. package/lib/ads/modules/yield-optimization/yieldOptimizationService.js +169 -0
  54. package/lib/ads/modules/zeotap/index.js +111 -0
  55. package/lib/ads/moli.js +645 -0
  56. package/lib/ads/moliGlobal.js +11 -0
  57. package/lib/ads/prebid-outstream.js +13 -0
  58. package/lib/ads/prebid.js +406 -0
  59. package/lib/ads/sizeConfigService.js +49 -0
  60. package/lib/ads/spa.js +32 -0
  61. package/lib/bundle/adReload.js +2 -0
  62. package/lib/bundle/adex.js +2 -0
  63. package/lib/bundle/blocklistUrls.js +2 -0
  64. package/lib/bundle/cleanup.js +2 -0
  65. package/lib/bundle/confiant.js +2 -0
  66. package/lib/bundle/configureFromEndpoint.js +40 -0
  67. package/lib/bundle/emetriq.js +2 -0
  68. package/lib/bundle/identityLink.js +2 -0
  69. package/lib/bundle/init.js +2 -0
  70. package/lib/bundle/interstitialModule.js +2 -0
  71. package/lib/bundle/lazyLoad.js +2 -0
  72. package/lib/bundle/prebidFirstPartyData.js +2 -0
  73. package/lib/bundle/pubstack.js +2 -0
  74. package/lib/bundle/skin.js +2 -0
  75. package/lib/bundle/stickyFooterAd.js +2 -0
  76. package/lib/bundle/stickyFooterAds2.js +2 -0
  77. package/lib/bundle/stickyHeaderAd.js +2 -0
  78. package/lib/bundle/utiq.js +2 -0
  79. package/lib/bundle/yieldOptimization.js +2 -0
  80. package/lib/bundle/zeotap.js +2 -0
  81. package/lib/console/components/adSlotConfig.js +225 -0
  82. package/lib/console/components/consentConfig.js +138 -0
  83. package/lib/console/components/globalConfig.js +634 -0
  84. package/lib/console/components/labelConfigDebug.js +19 -0
  85. package/lib/console/components/sizeConfigDebug.js +34 -0
  86. package/lib/console/components/tag.js +4 -0
  87. package/lib/console/debug.js +38 -0
  88. package/lib/console/util/array.js +1 -0
  89. package/lib/console/util/calculateAdDensity.js +36 -0
  90. package/lib/console/util/debounce.js +12 -0
  91. package/lib/console/util/debugLogger.js +6 -0
  92. package/lib/console/util/extractPositionFromPath.js +8 -0
  93. package/lib/console/util/prebid.js +3 -0
  94. package/lib/console/util/stringUtils.js +23 -0
  95. package/lib/console/util/themingService.js +31 -0
  96. package/lib/console/util/windowResizeService.js +22 -0
  97. package/lib/console/validations/adReloadValidations.js +47 -0
  98. package/lib/console/validations/bucketValidations.js +78 -0
  99. package/lib/console/validations/sizesConfigValidations.js +97 -0
  100. package/lib/gen/packageJson.js +3 -0
  101. package/lib/index.js +3 -0
  102. package/lib/types/apstag.js +1 -0
  103. package/lib/types/dom.js +1 -0
  104. package/lib/types/emetriq.js +1 -0
  105. package/lib/types/googletag.js +1 -0
  106. package/lib/types/identitylink.js +1 -0
  107. package/lib/types/module.js +1 -0
  108. package/lib/types/moliConfig.js +1 -0
  109. package/lib/types/moliRuntime.js +1 -0
  110. package/lib/types/prebidjs.js +45 -0
  111. package/lib/types/supplyChainObject.js +1 -0
  112. package/lib/types/tcfapi.js +48 -0
  113. package/lib/util/addNewInfiniteSlotToConfig.js +11 -0
  114. package/lib/util/arrayUtils.js +9 -0
  115. package/lib/util/assetLoaderService.js +91 -0
  116. package/lib/util/browserStorageKeys.js +7 -0
  117. package/lib/util/debugDelay.js +12 -0
  118. package/lib/util/domready.js +15 -0
  119. package/lib/util/environmentOverride.js +16 -0
  120. package/lib/util/extractAdTagVersion.js +16 -0
  121. package/lib/util/extractTopPrivateDomainFromHostname.js +17 -0
  122. package/lib/util/localStorage.js +26 -0
  123. package/lib/util/logging.js +106 -0
  124. package/lib/util/objectUtils.js +29 -0
  125. package/lib/util/query.js +40 -0
  126. package/lib/util/queryParameters.js +5 -0
  127. package/lib/util/resolveOverrides.js +21 -0
  128. package/lib/util/resolveStoredRequestIdInOrtb2Object.js +12 -0
  129. package/lib/util/sizes.js +3 -0
  130. package/lib/util/test-slots.js +150 -0
  131. package/lib/util/uuid.js +10 -0
  132. 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
+ };