@highfivve/ad-tag 5.6.3 → 5.7.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/a9.js CHANGED
@@ -3,7 +3,6 @@ import { AssetLoadMethod } from '../util/assetLoaderService';
3
3
  import { isSizeEqual } from '../util/sizes';
4
4
  import { SizeConfigService } from './sizeConfigService';
5
5
  import { tcfapi } from '../types/tcfapi';
6
- var TCPurpose = tcfapi.responses.TCPurpose;
7
6
  import * as adUnitPath from './adUnitPath';
8
7
  const isA9SlotDefinition = (slotDefinition) => {
9
8
  return !!slotDefinition.moliSlot.a9;
@@ -11,13 +10,13 @@ const isA9SlotDefinition = (slotDefinition) => {
11
10
  const hasRequiredConsent = (tcData) => !tcData.gdprApplies ||
12
11
  (tcData.vendor.consents['793'] &&
13
12
  [
14
- TCPurpose.STORE_INFORMATION_ON_DEVICE,
15
- TCPurpose.SELECT_BASIC_ADS,
16
- TCPurpose.CREATE_PERSONALISED_ADS_PROFILE,
17
- TCPurpose.SELECT_PERSONALISED_ADS,
18
- TCPurpose.MEASURE_AD_PERFORMANCE,
19
- TCPurpose.APPLY_MARKET_RESEARCH,
20
- TCPurpose.DEVELOP_IMPROVE_PRODUCTS
13
+ tcfapi.responses.TCPurpose.STORE_INFORMATION_ON_DEVICE,
14
+ tcfapi.responses.TCPurpose.SELECT_BASIC_ADS,
15
+ tcfapi.responses.TCPurpose.CREATE_PERSONALISED_ADS_PROFILE,
16
+ tcfapi.responses.TCPurpose.SELECT_PERSONALISED_ADS,
17
+ tcfapi.responses.TCPurpose.MEASURE_AD_PERFORMANCE,
18
+ tcfapi.responses.TCPurpose.APPLY_MARKET_RESEARCH,
19
+ tcfapi.responses.TCPurpose.DEVELOP_IMPROVE_PRODUCTS
21
20
  ].every(purpose => tcData.purpose.consents[purpose]));
22
21
  export const a9Init = (config, assetService) => mkInitStep('a9-init', (context) => new Promise(resolve => {
23
22
  context.window__.apstag = context.window__.apstag || {
@@ -84,9 +83,12 @@ export const a9Configure = (config, schainConfig) => mkConfigureStep('a9-configu
84
83
  resolve();
85
84
  });
86
85
  });
87
- export const a9PublisherAudiences = (config) => mkConfigureStepOnce('a9-publisher-audiences', (context, _slots) => new Promise(resolve => {
88
- const publisherAudience = config.publisherAudience;
89
- if (publisherAudience && publisherAudience.enabled) {
86
+ export const a9PublisherAudiences = (config, audienceTargeting) => mkConfigureStepOnce('a9-publisher-audiences', (context, _slots) => new Promise(resolve => {
87
+ const runtimeHem = audienceTargeting?.hem?.sha256;
88
+ const publisherAudience = runtimeHem !== undefined
89
+ ? { enabled: !!config.publisherAudience?.enabled, sha256Email: runtimeHem }
90
+ : config.publisherAudience;
91
+ if (publisherAudience !== undefined && publisherAudience.enabled) {
90
92
  const tokenConfig = {
91
93
  hashedRecords: [
92
94
  {
@@ -96,7 +96,7 @@ export class AdService {
96
96
  if (config.a9 && config.a9.enabled !== false && env === 'production' && isGam) {
97
97
  init.push(a9Init(config.a9, this.assetService));
98
98
  configure.push(a9Configure(config.a9, config.schain));
99
- configure.push(a9PublisherAudiences(config.a9));
99
+ configure.push(a9PublisherAudiences(config.a9, runtimeConfig.audience));
100
100
  prepareRequestAds.push(a9ClearTargetingStep());
101
101
  requestBids.push(a9RequestBids(config.a9));
102
102
  }
@@ -0,0 +1,49 @@
1
+ export const criteoEnrichWithFpd = (runtimeConfig, userSyncConfig, source) => (bidderConfigs) => {
2
+ const criteoEnabled = userSyncConfig?.userIds?.find(uid => uid.name === 'criteo') !== undefined;
3
+ if (!criteoEnabled) {
4
+ return bidderConfigs;
5
+ }
6
+ const uids = [];
7
+ if (runtimeConfig.audience?.hem?.sha256 !== undefined) {
8
+ uids.push({
9
+ id: runtimeConfig.audience.hem.sha256,
10
+ atype: 3,
11
+ ext: { stype: 'hemsha256' }
12
+ });
13
+ }
14
+ if (runtimeConfig.audience?.hem?.sha256ofMD5 !== undefined) {
15
+ uids.push({
16
+ id: runtimeConfig.audience.hem.sha256ofMD5,
17
+ atype: 3,
18
+ ext: { stype: 'hemsha256md5' }
19
+ });
20
+ }
21
+ if (uids.length === 0) {
22
+ return bidderConfigs;
23
+ }
24
+ return [
25
+ ...bidderConfigs.filter(config => !config.options.bidders.every(b => b === 'criteo')),
26
+ {
27
+ options: {
28
+ bidders: ['criteo'],
29
+ config: {
30
+ ortb2: {
31
+ user: {
32
+ ext: {
33
+ data: {
34
+ eids: [
35
+ {
36
+ source: source,
37
+ uids: uids
38
+ }
39
+ ]
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ },
46
+ merge: true
47
+ }
48
+ ];
49
+ };
package/lib/ads/id5.js ADDED
@@ -0,0 +1,30 @@
1
+ const createPd = (runtimeConfig) => {
2
+ const sha256Email = runtimeConfig.audience?.hem?.sha256;
3
+ if (!sha256Email) {
4
+ return null;
5
+ }
6
+ const pdKeys = {
7
+ 1: sha256Email
8
+ };
9
+ const pdRaw = Object.entries(pdKeys)
10
+ .map(([key, value]) => {
11
+ return `${key}=${encodeURIComponent(value)}`;
12
+ })
13
+ .join('&');
14
+ return btoa(pdRaw);
15
+ };
16
+ export const enrichId5WithFpd = (runtimeConfig, userIds) => {
17
+ return userIds?.map(idProvider => {
18
+ if (idProvider.name === 'id5Id') {
19
+ const pd = createPd(runtimeConfig);
20
+ return {
21
+ ...idProvider,
22
+ params: {
23
+ ...idProvider.params,
24
+ ...(pd && { pd })
25
+ }
26
+ };
27
+ }
28
+ return idProvider;
29
+ });
30
+ };
@@ -60,7 +60,7 @@ export class AdVisibilityService {
60
60
  this.setUpdateTimer(true);
61
61
  this.logger?.debug('AdVisibilityService', 'initialized');
62
62
  }
63
- trackSlot(slot, refreshCallback, advertiserId) {
63
+ trackSlot(slot, refreshCallback, advertiserId, companyIds) {
64
64
  const slotDomId = slot.getSlotElementId();
65
65
  const domElement = this.observedDomElementForSlot(slot);
66
66
  if (domElement) {
@@ -75,9 +75,11 @@ export class AdVisibilityService {
75
75
  (override?.variant === 'disabled' &&
76
76
  (override.disableAllAdVisibilityChecks ||
77
77
  !(override.disabledAdVisibilityCheckAdvertiserIds &&
78
- override.disabledAdVisibilityCheckAdvertiserIds.length > 0 &&
79
- advertiserId) ||
80
- override.disabledAdVisibilityCheckAdvertiserIds.includes(advertiserId)))
78
+ override.disabledAdVisibilityCheckAdvertiserIds.length > 0) ||
79
+ (advertiserId &&
80
+ override.disabledAdVisibilityCheckAdvertiserIds.includes(advertiserId)) ||
81
+ (companyIds &&
82
+ override.disabledAdVisibilityCheckAdvertiserIds.some(id => companyIds.includes(id)))))
81
83
  ? this.window.performance.now()
82
84
  : undefined,
83
85
  durationVisibleSum: 0,
@@ -28,12 +28,13 @@ export class AdReload {
28
28
  this.adVisibilityService = new AdVisibilityService(new UserActivityService(window, config.userActivityLevelControl, logger), this.refreshIntervalMs, config.refreshIntervalMsOverrides ?? {}, false, !!config.disableAdVisibilityChecks, config.viewabilityOverrides ?? {}, window, logger);
29
29
  };
30
30
  this.setupSlotRenderListener = (config, slotsToMonitor, reloadAdSlotCallback, window, logger) => window.googletag.pubads().addEventListener('slotRenderEnded', renderEndedEvent => {
31
- const { slot: googleTagSlot, campaignId, advertiserId, yieldGroupIds, isEmpty: slotIsEmpty } = renderEndedEvent;
31
+ const { slot: googleTagSlot, campaignId, advertiserId, companyIds, yieldGroupIds, isEmpty: slotIsEmpty } = renderEndedEvent;
32
32
  const slotDomId = googleTagSlot.getSlotElementId();
33
33
  const slotIsMonitored = slotsToMonitor.indexOf(slotDomId) > -1;
34
34
  const orderIdNotExcluded = !campaignId || config.excludeOrderIds.indexOf(campaignId) === -1;
35
35
  const orderIdIncluded = !!campaignId && config.includeOrderIds.indexOf(campaignId) > -1;
36
- const advertiserIdIncluded = !!advertiserId && config.includeAdvertiserIds.indexOf(advertiserId) > -1;
36
+ const advertiserIdIncluded = (!!advertiserId && config.includeAdvertiserIds.indexOf(advertiserId) > -1) ||
37
+ (!!companyIds && config.includeAdvertiserIds.some(id => companyIds.includes(id)));
37
38
  const yieldGroupIdIncluded = !!yieldGroupIds && config.includeYieldGroupIds.some(id => yieldGroupIds.indexOf(id) > -1);
38
39
  const trackingSlotAllowed = !slotIsEmpty &&
39
40
  slotIsMonitored &&
@@ -51,7 +52,7 @@ export class AdReload {
51
52
  }
52
53
  const slotAlreadyTracked = !!this.adVisibilityService?.isSlotTracked(slotDomId);
53
54
  if (trackingSlotAllowed) {
54
- this.adVisibilityService.trackSlot(googleTagSlot, reloadAdSlotCallback, advertiserId);
55
+ this.adVisibilityService.trackSlot(googleTagSlot, reloadAdSlotCallback, advertiserId, companyIds);
55
56
  }
56
57
  else if (slotAlreadyTracked) {
57
58
  this.adVisibilityService.removeSlotTracking(googleTagSlot);
@@ -32,7 +32,13 @@ export const trackLoginEvent = (context, moduleConfig, document, logger) => {
32
32
  return;
33
33
  }
34
34
  const url = new URL(`https://xdn-ttp.de/lns/import-event-${moduleConfig.login.partner}`);
35
- url.searchParams.append('guid', moduleConfig.login.guid);
35
+ const hemSha256 = context.runtimeConfig__.audience?.hem?.sha256;
36
+ const resolvedGuid = hemSha256 !== undefined ? context.window__.btoa(hemSha256) : moduleConfig.login.guid;
37
+ if (resolvedGuid === undefined) {
38
+ logger.warn('emetriq', 'no guid provided for login event tracking');
39
+ return;
40
+ }
41
+ url.searchParams.append('guid', resolvedGuid);
36
42
  if (context.tcData__.gdprApplies) {
37
43
  url.searchParams.append('gdpr', '1');
38
44
  url.searchParams.append('gdpr_consent', context.tcData__.tcString);
@@ -42,9 +42,22 @@ export class IdentityLink {
42
42
  }
43
43
  const window = context.window__;
44
44
  window.addEventListener('envelopeModuleReady', () => {
45
+ const hashedEmailAddresses = [...moduleConfig.hashedEmailAddresses];
46
+ const sha1 = context.runtimeConfig__.audience?.hem?.sha1;
47
+ const sha256 = context.runtimeConfig__.audience?.hem?.sha256;
48
+ const md5 = context.runtimeConfig__.audience?.hem?.md5;
49
+ if (sha1 !== undefined) {
50
+ hashedEmailAddresses[0] = sha1;
51
+ }
52
+ if (sha256 !== undefined) {
53
+ hashedEmailAddresses[1] = sha256;
54
+ }
55
+ if (md5 !== undefined) {
56
+ hashedEmailAddresses[2] = md5;
57
+ }
45
58
  window.atsenvelopemodule.setAdditionalData({
46
59
  type: 'emailHashes',
47
- id: moduleConfig.hashedEmailAddresses
60
+ id: hashedEmailAddresses
48
61
  });
49
62
  });
50
63
  return context.assetLoaderService__
@@ -14,6 +14,10 @@ const interstitialRenderedEvent = (interstitialDomId, disallowedAdvertiserIds, w
14
14
  else if (event.advertiserId && disallowedAdvertiserIds.includes(event.advertiserId)) {
15
15
  resolve({ result: 'disallowed', slot: event.slot });
16
16
  }
17
+ else if (event.companyIds &&
18
+ disallowedAdvertiserIds.some(id => event.companyIds?.includes(id))) {
19
+ resolve({ result: 'disallowed', slot: event.slot });
20
+ }
17
21
  else {
18
22
  event.slot.setConfig({ safeFrame: { forceSafeFrame: true } });
19
23
  resolve({ result: 'standard', slot: event.slot });
@@ -22,6 +22,7 @@ const renderFooterAd = (window, floorAdDomId, disallowedAdvertiserIds, log) => (
22
22
  slot.getSlotElementId() !== floorAdDomId ||
23
23
  event.isEmpty ||
24
24
  (!!event.advertiserId && disallowedAdvertiserIds.includes(event.advertiserId)) ||
25
+ (!!event.companyIds && disallowedAdvertiserIds.some(id => event.companyIds?.includes(id))) ||
25
26
  window.matchMedia('(max-width: 767px)').matches) {
26
27
  log.debug('[footer-ad]', 'remove footer ad container');
27
28
  return;
@@ -23,6 +23,10 @@ const stickyRenderedEvent = (adSticky, mobileStickyDomId, disallowedAdvertiserId
23
23
  else if (!!event.advertiserId && disallowedAdvertiserIds.includes(event.advertiserId)) {
24
24
  resolve('disallowed');
25
25
  }
26
+ else if (!!event.companyIds &&
27
+ disallowedAdvertiserIds.some(id => event.companyIds?.includes(id))) {
28
+ resolve('disallowed');
29
+ }
26
30
  else {
27
31
  resolve('standard');
28
32
  }
@@ -15,6 +15,10 @@ const stickyRenderedEvent = (mobileStickyDomId, disallowedAdvertiserIds, window)
15
15
  else if (event.advertiserId && disallowedAdvertiserIds.includes(event.advertiserId)) {
16
16
  resolve('disallowed');
17
17
  }
18
+ else if (!!event.companyIds &&
19
+ disallowedAdvertiserIds.some(id => event.companyIds?.includes(id))) {
20
+ resolve('disallowed');
21
+ }
18
22
  else {
19
23
  resolve('standard');
20
24
  }
@@ -10,6 +10,10 @@ export const adRenderResult = (ctx, headerSlot, disallowedAdvertiserIds, minVisi
10
10
  if (event.advertiserId && disallowedAdvertiserIds.includes(event.advertiserId)) {
11
11
  resolve('disallowed');
12
12
  }
13
+ else if (event.companyIds &&
14
+ disallowedAdvertiserIds.some(id => event.companyIds?.includes(id))) {
15
+ resolve('disallowed');
16
+ }
13
17
  else if (event.isEmpty) {
14
18
  resolve('empty');
15
19
  }
package/lib/ads/moli.js CHANGED
@@ -589,6 +589,24 @@ export const createMoliTag = (window) => {
589
589
  addLabel(domain);
590
590
  }
591
591
  }
592
+ function setAudience(audience) {
593
+ switch (state.state) {
594
+ case 'configurable':
595
+ case 'configured': {
596
+ state.runtimeConfig.audience = audience;
597
+ break;
598
+ }
599
+ case 'spa-finished':
600
+ case 'spa-requestAds': {
601
+ state.nextRuntimeConfig.audience = audience;
602
+ break;
603
+ }
604
+ default: {
605
+ getLogger(state.runtimeConfig, window).error('MoliGlobal', `Setting audience targeting after configuration: ${JSON.stringify(audience)}`);
606
+ break;
607
+ }
608
+ }
609
+ }
592
610
  function newEmptyRuntimeConfig(previous, options) {
593
611
  return {
594
612
  environment: previous?.environment ?? 'production',
@@ -639,6 +657,7 @@ export const createMoliTag = (window) => {
639
657
  triggerDelay: triggerDelay,
640
658
  getState: getState,
641
659
  openConsole: openConsole,
660
+ setAudience: setAudience,
642
661
  addEventListener: eventService.addEventListener,
643
662
  removeEventListener: eventService.removeEventListener
644
663
  };
package/lib/ads/prebid.js CHANGED
@@ -8,6 +8,8 @@ import { AssetLoadMethod } from '../util/assetLoaderService';
8
8
  import { packageJson } from 'ad-tag/gen/packageJson';
9
9
  import { prebidOutstreamRenderer } from 'ad-tag/ads/prebid-outstream';
10
10
  import { isGamInterstitial } from 'ad-tag/ads/auctions/interstitialContext';
11
+ import { criteoEnrichWithFpd } from 'ad-tag/ads/criteo';
12
+ import { enrichId5WithFpd } from 'ad-tag/ads/id5';
11
13
  const prebidTimeout = (context) => {
12
14
  return new Promise((_, reject) => {
13
15
  context.window__.setTimeout(() => reject('Prebid did not resolve in time. Maybe you forgot to import the prebid distribution in the ad tag'), 5000);
@@ -152,7 +154,8 @@ export const prebidInit = (assetService) => mkInitStep('prebid-init', context =>
152
154
  });
153
155
  export const prebidRemoveAdUnits = (prebidConfig) => mkConfigureStep('prebid-remove-adunits', (context) => new Promise(resolve => {
154
156
  if (prebidConfig.ephemeralAdUnits !== true) {
155
- context.window__.pbjs = context.window__.pbjs || { que: [] };
157
+ context.window__.pbjs =
158
+ context.window__.pbjs || { que: [] };
156
159
  const adUnits = context.window__.pbjs.adUnits;
157
160
  if (adUnits) {
158
161
  context.window__.pbjs.que.push(() => {
@@ -165,7 +168,8 @@ export const prebidRemoveAdUnits = (prebidConfig) => mkConfigureStep('prebid-rem
165
168
  }));
166
169
  export const prebidClearAuction = () => {
167
170
  return mkConfigureStepOncePerRequestAdsCycle('prebid-clear-auction', (context) => new Promise(resolve => {
168
- context.window__.pbjs = context.window__.pbjs || { que: [] };
171
+ context.window__.pbjs =
172
+ context.window__.pbjs || { que: [] };
169
173
  context.window__.pbjs.que.push(() => {
170
174
  context.logger__.debug('Prebid', 'Clearing prebid auctions');
171
175
  context.window__.pbjs.clearAllAuctions();
@@ -203,12 +207,19 @@ export const prebidConfigure = (prebidConfig, schainConfig) => {
203
207
  }
204
208
  });
205
209
  }
210
+ const enrichedUserIds = enrichId5WithFpd(context.runtimeConfig__, prebidConfig.config.userSync?.userIds);
206
211
  context.window__.pbjs.setConfig({
207
212
  ...prebidConfig.config,
208
213
  ...{ schain: mkSupplyChainConfig([schainConfig.supplyChainStartNode]) },
209
- ...{ floors: prebidConfig.config.floors || {} }
214
+ ...{ floors: prebidConfig.config.floors || {} },
215
+ ...{
216
+ userSync: prebidConfig.config.userSync
217
+ ? { ...prebidConfig.config.userSync, userIds: enrichedUserIds }
218
+ : prebidConfig.config.userSync
219
+ }
210
220
  });
211
- prebidConfig.bidderConfigs?.forEach(({ options, merge }) => {
221
+ const bidderConfigs = criteoEnrichWithFpd(context.runtimeConfig__, prebidConfig.config.userSync, context.window__.location.host)(prebidConfig.bidderConfigs || []);
222
+ bidderConfigs.forEach(({ options, merge }) => {
212
223
  context.window__.pbjs.setBidderConfig(options, merge);
213
224
  });
214
225
  prebidConfig.schain.nodes.forEach(({ bidder, node, appendNode }) => {
@@ -1,3 +1,3 @@
1
1
  export const packageJson = {
2
- version: '5.6.3'
2
+ version: '5.7.1'
3
3
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@highfivve/ad-tag",
3
- "version": "5.6.3",
3
+ "version": "5.7.1",
4
4
  "license": "Apache-2.0",
5
5
  "description": "An ad tag implementation called moli",
6
6
  "main": "./lib/index.js",