prebid.js 5.18.0 → 5.20.2

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 (114) hide show
  1. package/browsers.json +1 -0
  2. package/integrationExamples/gpt/akamaidap_segments_example.html +132 -0
  3. package/modules/.submodules.json +1 -0
  4. package/modules/adfBidAdapter.js +21 -16
  5. package/modules/adtelligentBidAdapter.js +2 -1
  6. package/modules/adxcgBidAdapter.js +311 -359
  7. package/modules/airgridRtdProvider.js +1 -1
  8. package/modules/akamaiDapRtdProvider.js +474 -0
  9. package/modules/akamaiDapRtdProvider.md +47 -0
  10. package/modules/appnexusBidAdapter.js +9 -3
  11. package/modules/atsAnalyticsAdapter.js +67 -46
  12. package/modules/atsAnalyticsAdapter.md +1 -0
  13. package/modules/betweenBidAdapter.js +20 -3
  14. package/modules/browsiRtdProvider.js +106 -18
  15. package/modules/cleanioRtdProvider.js +192 -0
  16. package/modules/cleanioRtdProvider.md +59 -0
  17. package/modules/codefuelBidAdapter.js +183 -0
  18. package/modules/codefuelBidAdapter.md +111 -0
  19. package/modules/connectIdSystem.js +104 -0
  20. package/modules/connectIdSystem.md +33 -0
  21. package/modules/deepintentBidAdapter.js +106 -9
  22. package/modules/deepintentBidAdapter.md +36 -1
  23. package/modules/deltaprojectsBidAdapter.js +252 -0
  24. package/modules/deltaprojectsBidAdapter.md +32 -0
  25. package/modules/engageyaBidAdapter.js +157 -0
  26. package/modules/gridBidAdapter.js +1 -0
  27. package/modules/gumgumBidAdapter.js +8 -0
  28. package/modules/inskinBidAdapter.js +7 -3
  29. package/modules/ipromBidAdapter.js +79 -0
  30. package/modules/ixBidAdapter.js +8 -1
  31. package/modules/jixieBidAdapter.js +8 -2
  32. package/modules/justpremiumBidAdapter.js +6 -1
  33. package/modules/kinessoIdSystem.js +1 -1
  34. package/modules/limelightDigitalBidAdapter.js +22 -2
  35. package/modules/livewrappedAnalyticsAdapter.js +49 -1
  36. package/modules/multibid/index.js +3 -3
  37. package/modules/nativoBidAdapter.js +5 -1
  38. package/modules/nextMillenniumBidAdapter.js +12 -3
  39. package/modules/oguryBidAdapter.js +14 -1
  40. package/modules/openxBidAdapter.js +34 -22
  41. package/modules/operaadsBidAdapter.js +21 -1
  42. package/modules/otmBidAdapter.js +146 -0
  43. package/modules/otmBidAdapter.md +27 -26
  44. package/modules/outbrainBidAdapter.js +5 -0
  45. package/modules/playwireBidAdapter.md +61 -0
  46. package/modules/publinkIdSystem.js +11 -6
  47. package/modules/rtdModule/index.js +2 -2
  48. package/modules/sonobiBidAdapter.js +7 -0
  49. package/modules/sortableBidAdapter.js +1 -0
  50. package/modules/teadsBidAdapter.js +3 -0
  51. package/modules/tripleliftBidAdapter.js +22 -5
  52. package/modules/trustxBidAdapter.js +8 -6
  53. package/modules/undertoneBidAdapter.js +9 -5
  54. package/modules/undertoneBidAdapter.md +5 -1
  55. package/modules/userId/eids.js +18 -0
  56. package/modules/userId/eids.md +7 -0
  57. package/modules/userId/userId.md +12 -0
  58. package/modules/ventesBidAdapter.js +370 -0
  59. package/modules/ventesBidAdapter.md +94 -0
  60. package/modules/videobyteBidAdapter.js +13 -6
  61. package/modules/videobyteBidAdapter.md +49 -0
  62. package/modules/yahoosspBidAdapter.js +6 -6
  63. package/modules/yieldmoSyntheticInventoryModule.js +46 -0
  64. package/modules/yieldmoSyntheticInventoryModule.md +68 -0
  65. package/package.json +1 -1
  66. package/src/adapterManager.js +5 -0
  67. package/src/adapters/bidderFactory.js +4 -3
  68. package/src/auction.js +11 -11
  69. package/src/constants.json +1 -0
  70. package/src/secureCreatives.js +6 -7
  71. package/src/targeting.js +11 -9
  72. package/test/spec/modules/adfBidAdapter_spec.js +83 -29
  73. package/test/spec/modules/adtelligentBidAdapter_spec.js +1 -0
  74. package/test/spec/modules/adxcgBidAdapter_spec.js +827 -571
  75. package/test/spec/modules/akamaiDapRtdProvider_spec.js +246 -0
  76. package/test/spec/modules/appnexusBidAdapter_spec.js +16 -1
  77. package/test/spec/modules/atsAnalyticsAdapter_spec.js +42 -9
  78. package/test/spec/modules/betweenBidAdapter_spec.js +41 -0
  79. package/test/spec/modules/browsiRtdProvider_spec.js +62 -7
  80. package/test/spec/modules/cleanioRtdProvider_spec.js +188 -0
  81. package/test/spec/modules/codefuelBidAdapter_spec.js +316 -0
  82. package/test/spec/modules/connectIdSystem_spec.js +189 -0
  83. package/test/spec/modules/deepintentBidAdapter_spec.js +153 -3
  84. package/test/spec/modules/deltaprojectsBidAdapter_spec.js +399 -0
  85. package/test/spec/modules/engageyaBidAdapter_spec.js +286 -0
  86. package/test/spec/modules/eplanningBidAdapter_spec.js +8 -8
  87. package/test/spec/modules/gumgumBidAdapter_spec.js +5 -1
  88. package/test/spec/modules/ipromBidAdapter_spec.js +195 -0
  89. package/test/spec/modules/ixBidAdapter_spec.js +13 -3
  90. package/test/spec/modules/jixieBidAdapter_spec.js +13 -11
  91. package/test/spec/modules/justpremiumBidAdapter_spec.js +9 -2
  92. package/test/spec/modules/limelightDigitalBidAdapter_spec.js +155 -1
  93. package/test/spec/modules/livewrappedAnalyticsAdapter_spec.js +52 -7
  94. package/test/spec/modules/multibid_spec.js +31 -31
  95. package/test/spec/modules/nextMillenniumBidAdapter_spec.js +13 -1
  96. package/test/spec/modules/oguryBidAdapter_spec.js +53 -12
  97. package/test/spec/modules/openxBidAdapter_spec.js +85 -13
  98. package/test/spec/modules/operaadsBidAdapter_spec.js +38 -6
  99. package/test/spec/modules/otmBidAdapter_spec.js +67 -0
  100. package/test/spec/modules/outbrainBidAdapter_spec.js +18 -0
  101. package/test/spec/modules/publinkIdSystem_spec.js +6 -6
  102. package/test/spec/modules/sonobiBidAdapter_spec.js +34 -1
  103. package/test/spec/modules/sortableBidAdapter_spec.js +11 -0
  104. package/test/spec/modules/teadsBidAdapter_spec.js +132 -0
  105. package/test/spec/modules/tripleliftBidAdapter_spec.js +128 -0
  106. package/test/spec/modules/trustxBidAdapter_spec.js +3 -3
  107. package/test/spec/modules/undertoneBidAdapter_spec.js +52 -0
  108. package/test/spec/modules/ventesBidAdapter_spec.js +845 -0
  109. package/test/spec/modules/videobyteBidAdapter_spec.js +2 -2
  110. package/test/spec/modules/yieldmoSyntheticInventoryModule_spec.js +89 -0
  111. package/test/spec/unit/core/adapterManager_spec.js +37 -2
  112. package/test/spec/unit/core/bidderFactory_spec.js +61 -1
  113. package/test/spec/unit/pbjs_api_spec.js +37 -2
  114. package/test/spec/unit/secureCreatives_spec.js +54 -25
@@ -1,4 +1,4 @@
1
- import { logMessage, groupBy, uniques } from '../src/utils.js';
1
+ import { logMessage, groupBy, uniques, flatten, deepAccess } from '../src/utils.js';
2
2
  import { registerBidder } from '../src/adapters/bidderFactory.js';
3
3
  import { BANNER, VIDEO } from '../src/mediaTypes.js';
4
4
  import {ajax} from '../src/ajax.js';
@@ -92,6 +92,26 @@ export const spec = {
92
92
  }
93
93
  return bidResponses;
94
94
  },
95
+
96
+ getUserSyncs: (syncOptions, serverResponses, gdprConsent, uspConsent) => {
97
+ const syncs = serverResponses.map(response => response.body).reduce(flatten, [])
98
+ .map(response => deepAccess(response, 'ext.sync')).filter(Boolean);
99
+ const iframeSyncUrls = !syncOptions.iframeEnabled ? [] : syncs.map(sync => sync.iframe).filter(Boolean)
100
+ .filter(uniques).map(url => {
101
+ return {
102
+ type: 'iframe',
103
+ url: url
104
+ }
105
+ });
106
+ const pixelSyncUrls = !syncOptions.pixelEnabled ? [] : syncs.map(sync => sync.pixel).filter(Boolean)
107
+ .filter(uniques).map(url => {
108
+ return {
109
+ type: 'image',
110
+ url: url
111
+ }
112
+ });
113
+ return [iframeSyncUrls, pixelSyncUrls].reduce(flatten, []);
114
+ }
95
115
  };
96
116
 
97
117
  registerBidder(spec);
@@ -125,7 +145,7 @@ function buildPlacement(bidRequest) {
125
145
  break;
126
146
  }
127
147
  }
128
- sizes = (sizes || []).concat(bidRequest.sizes || []).filter(uniques);
148
+ sizes = (sizes || []).concat(bidRequest.sizes || []);
129
149
  return {
130
150
  host: bidRequest.params.host,
131
151
  adUnit: {
@@ -3,6 +3,7 @@ import {ajax} from '../src/ajax.js';
3
3
  import adapter from '../src/AnalyticsAdapter.js';
4
4
  import CONSTANTS from '../src/constants.json';
5
5
  import adapterManager from '../src/adapterManager.js';
6
+ import { getGlobal } from '../src/prebidGlobal.js';
6
7
 
7
8
  const ANALYTICSTYPE = 'endpoint';
8
9
  const URL = 'https://lwadm.com/analytics/10';
@@ -11,8 +12,10 @@ const REQUESTSENT = 1;
11
12
  const RESPONSESENT = 2;
12
13
  const WINSENT = 4;
13
14
  const TIMEOUTSENT = 8;
15
+ const ADRENDERFAILEDSENT = 16;
14
16
 
15
17
  let initOptions;
18
+ let prebidGlobal = getGlobal();
16
19
  export const BID_WON_TIMEOUT = 500;
17
20
 
18
21
  const cache = {
@@ -78,6 +81,7 @@ let livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE
78
81
  bidResponse.width = args.width;
79
82
  bidResponse.height = args.height;
80
83
  bidResponse.cpm = args.cpm;
84
+ bidResponse.originalCpm = prebidGlobal.convertCurrency(args.originalCpm, args.originalCurrency, args.currency);
81
85
  bidResponse.ttr = args.timeToRespond;
82
86
  bidResponse.readyToSend = 1;
83
87
  bidResponse.mediaType = args.mediaType == 'native' ? 2 : (args.mediaType == 'video' ? 4 : 1);
@@ -114,6 +118,16 @@ let livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE
114
118
  livewrappedAnalyticsAdapter.sendEvents();
115
119
  }
116
120
  break;
121
+ case CONSTANTS.EVENTS.AD_RENDER_FAILED:
122
+ logInfo('LIVEWRAPPED_AD_RENDER_FAILED:', args);
123
+ let adRenderFailedBid = cache.auctions[args.bid.auctionId].bids[args.bid.requestId];
124
+ adRenderFailedBid.adRenderFailed = true;
125
+ adRenderFailedBid.reason = args.reason;
126
+ adRenderFailedBid.message = args.message;
127
+ if (adRenderFailedBid.sendStatus != 0) {
128
+ livewrappedAnalyticsAdapter.sendEvents();
129
+ }
130
+ break;
117
131
  case CONSTANTS.EVENTS.BID_TIMEOUT:
118
132
  logInfo('LIVEWRAPPED_BID_TIMEOUT:', args);
119
133
  args.forEach(timeout => {
@@ -151,13 +165,15 @@ livewrappedAnalyticsAdapter.sendEvents = function() {
151
165
  wins: getWins(sentRequests.gdpr, sentRequests.auctionIds),
152
166
  timeouts: getTimeouts(sentRequests.auctionIds),
153
167
  bidAdUnits: getbidAdUnits(),
168
+ rf: getAdRenderFailed(sentRequests.auctionIds),
154
169
  rcv: getAdblockerRecovered()
155
170
  };
156
171
 
157
172
  if (events.requests.length == 0 &&
158
173
  events.responses.length == 0 &&
159
174
  events.wins.length == 0 &&
160
- events.timeouts.length == 0) {
175
+ events.timeouts.length == 0 &&
176
+ events.rf.length == 0) {
161
177
  return;
162
178
  }
163
179
 
@@ -224,6 +240,7 @@ function getResponses(gdpr, auctionIds) {
224
240
  width: bid.width,
225
241
  height: bid.height,
226
242
  cpm: bid.cpm,
243
+ orgCpm: bid.originalCpm,
227
244
  ttr: bid.ttr,
228
245
  IsBid: bid.isBid,
229
246
  mediaType: bid.mediaType,
@@ -263,6 +280,7 @@ function getWins(gdpr, auctionIds) {
263
280
  width: bid.width,
264
281
  height: bid.height,
265
282
  cpm: bid.cpm,
283
+ orgCpm: bid.originalCpm,
266
284
  mediaType: bid.mediaType,
267
285
  gdpr: gdprPos,
268
286
  floor: bid.lwFloor ? bid.lwFloor : (bid.floorData ? bid.floorData.floorValue : undefined),
@@ -338,6 +356,36 @@ function getTimeouts(auctionIds) {
338
356
  return timeouts;
339
357
  }
340
358
 
359
+ function getAdRenderFailed(auctionIds) {
360
+ var adRenderFails = [];
361
+
362
+ Object.keys(cache.auctions).forEach(auctionId => {
363
+ let auctionIdPos = getAuctionIdPos(auctionIds, auctionId);
364
+ Object.keys(cache.auctions[auctionId].bids).forEach(bidId => {
365
+ let auction = cache.auctions[auctionId];
366
+ let bid = auction.bids[bidId];
367
+ if (!(bid.sendStatus & ADRENDERFAILEDSENT) && bid.adRenderFailed) {
368
+ bid.sendStatus |= ADRENDERFAILEDSENT;
369
+
370
+ adRenderFails.push({
371
+ bidder: bid.bidder,
372
+ adUnit: bid.adUnit,
373
+ adUnitId: bid.adUnitId,
374
+ timeStamp: auction.timeStamp,
375
+ auctionId: auctionIdPos,
376
+ auc: bid.auc,
377
+ buc: bid.buc,
378
+ lw: bid.lw,
379
+ rsn: bid.reason,
380
+ msg: bid.message
381
+ });
382
+ }
383
+ });
384
+ });
385
+
386
+ return adRenderFails;
387
+ }
388
+
341
389
  function getbidAdUnits() {
342
390
  var bidAdUnits = [];
343
391
 
@@ -112,7 +112,7 @@ export function addBidResponseHook(fn, adUnitCode, bid) {
112
112
  if (multiConfig[bid.bidderCode].prefix) bid.multibidPrefix = multiConfig[bid.bidderCode].prefix;
113
113
  bid.originalBidder = bid.bidderCode;
114
114
  // Check if stored bids for auction include adUnitCode.bidder and max limit not reach for ad unit
115
- if (deepAccess(multibidUnits, `${adUnitCode}.${bid.bidderCode}`)) {
115
+ if (deepAccess(multibidUnits, [adUnitCode, bid.bidderCode])) {
116
116
  // Store request id under new property originalRequestId, create new unique bidId,
117
117
  // and push bid into multibid stored bids for auction if max not reached and bid cpm above floor
118
118
  if (!multibidUnits[adUnitCode][bid.bidderCode].maxReached && (!floor || floor <= bid.cpm)) {
@@ -131,9 +131,9 @@ export function addBidResponseHook(fn, adUnitCode, bid) {
131
131
  logWarn(`Filtering multibid received from bidder ${bid.bidderCode}: ` + ((multibidUnits[adUnitCode][bid.bidderCode].maxReached) ? `Maximum bid limit reached for ad unit code ${adUnitCode}` : 'Bid cpm under floors value.'));
132
132
  }
133
133
  } else {
134
- if (deepAccess(bid, 'floorData.floorValue')) deepSetValue(multibidUnits, `${adUnitCode}.${bid.bidderCode}`, {floor: deepAccess(bid, 'floorData.floorValue')});
134
+ if (deepAccess(bid, 'floorData.floorValue')) deepSetValue(multibidUnits, [adUnitCode, bid.bidderCode], {floor: deepAccess(bid, 'floorData.floorValue')});
135
135
 
136
- deepSetValue(multibidUnits, `${adUnitCode}.${bid.bidderCode}`, {ads: [bid]});
136
+ deepSetValue(multibidUnits, [adUnitCode, bid.bidderCode], {ads: [bid]});
137
137
  if (multibidUnits[adUnitCode][bid.bidderCode].ads.length === multiConfig[bid.bidderCode].maxbids) multibidUnits[adUnitCode][bid.bidderCode].maxReached = true;
138
138
 
139
139
  fn.call(this, adUnitCode, bid);
@@ -98,7 +98,11 @@ export const spec = {
98
98
  ]
99
99
 
100
100
  if (placementIds.size > 0) {
101
- params.unshift({ key: 'ntv_ptd', value: [...placementIds].join(',') })
101
+ // Convert Set to Array (IE 11 Safe)
102
+ const placements = []
103
+ placementIds.forEach((value) => placements.push(value))
104
+ // Append to query string paramters
105
+ params.unshift({ key: 'ntv_ptd', value: placements.join(',') })
102
106
  }
103
107
 
104
108
  if (bidderRequest.gdprConsent) {
@@ -30,14 +30,23 @@ export const spec = {
30
30
  }
31
31
  }
32
32
  }
33
+
33
34
  const gdprConsent = bidderRequest && bidderRequest.gdprConsent;
35
+ const uspConsent = bidderRequest && bidderRequest.uspConsent
36
+
37
+ if (gdprConsent || uspConsent) {
38
+ postBody.regs = { ext: {} }
34
39
 
35
- if (gdprConsent) {
40
+ if (uspConsent) {
41
+ postBody.regs.ext.us_privacy = uspConsent;
42
+ }
36
43
  if (typeof gdprConsent.gdprApplies !== 'undefined') {
37
- postBody.gdprApplies = !!gdprConsent.gdprApplies;
44
+ postBody.regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0;
38
45
  }
39
46
  if (typeof gdprConsent.consentString !== 'undefined') {
40
- postBody.consentString = gdprConsent.consentString;
47
+ postBody.user = {
48
+ ext: { consent: gdprConsent.consentString }
49
+ }
41
50
  }
42
51
  }
43
52
 
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  import { BANNER } from '../src/mediaTypes.js';
4
- import { getAdUnitSizes, logWarn, isFn } from '../src/utils.js';
4
+ import { getAdUnitSizes, logWarn, isFn, getWindowTop, getWindowSelf } from '../src/utils.js';
5
5
  import { registerBidder } from '../src/adapters/bidderFactory.js';
6
6
  import { ajax } from '../src/ajax.js'
7
7
 
@@ -145,7 +145,19 @@ function getFloor(bid) {
145
145
  return floorResult.currency === 'USD' ? floorResult.floor : 0;
146
146
  }
147
147
 
148
+ function getWindowContext() {
149
+ try {
150
+ return getWindowTop()
151
+ } catch (e) {
152
+ return getWindowSelf()
153
+ }
154
+ }
155
+
148
156
  function onBidWon(bid) {
157
+ const w = getWindowContext()
158
+ w.OG_PREBID_BID_OBJECT = {
159
+ ...(bid && { ...bid }),
160
+ }
149
161
  if (bid && bid.hasOwnProperty('nurl') && bid.nurl.length > 0) ajax(bid['nurl'], null);
150
162
  }
151
163
 
@@ -165,6 +177,7 @@ export const spec = {
165
177
  interpretResponse,
166
178
  getFloor,
167
179
  onBidWon,
180
+ getWindowContext,
168
181
  onTimeout
169
182
  }
170
183
 
@@ -256,27 +256,14 @@ function buildCommonQueryParamsFromBids(bids, bidderRequest) {
256
256
  nocache: new Date().getTime()
257
257
  };
258
258
 
259
- const firstPartyData = config.getConfig('ortb2.user.data')
260
- if (Array.isArray(firstPartyData) && firstPartyData.length > 0) {
261
- // extract and merge valid segments by provider/taxonomy
262
- const fpd = firstPartyData
263
- .filter(
264
- data => (Array.isArray(data.segment) &&
265
- data.segment.length > 0 &&
266
- data.name !== undefined &&
267
- data.name.length > 0)
268
- )
269
- .reduce((acc, data) => {
270
- const name = typeof data.ext === 'object' && data.ext.segtax ? `${data.name}/${data.ext.segtax}` : data.name;
271
- acc[name] = (acc[name] || []).concat(data.segment.map(seg => seg.id));
272
- return acc;
273
- }, {})
274
- const sm = Object.keys(fpd)
275
- .map((name, _) => name + ':' + fpd[name].join('|'))
276
- .join(',')
277
- if (sm.length > 0) {
278
- defaultParams.sm = encodeURIComponent(sm);
279
- }
259
+ const userDataSegments = buildFpdQueryParams('ortb2.user.data');
260
+ if (userDataSegments.length > 0) {
261
+ defaultParams.sm = userDataSegments;
262
+ }
263
+
264
+ const siteContentDataSegments = buildFpdQueryParams('ortb2.site.content.data');
265
+ if (siteContentDataSegments.length > 0) {
266
+ defaultParams.scsm = siteContentDataSegments;
280
267
  }
281
268
 
282
269
  if (bids[0].params.platform) {
@@ -317,12 +304,37 @@ function buildCommonQueryParamsFromBids(bids, bidderRequest) {
317
304
  return defaultParams;
318
305
  }
319
306
 
307
+ function buildFpdQueryParams(fpdPath) {
308
+ const firstPartyData = config.getConfig(fpdPath);
309
+ if (!Array.isArray(firstPartyData) || !firstPartyData.length) {
310
+ return '';
311
+ }
312
+ const fpd = firstPartyData
313
+ .filter(
314
+ data => (Array.isArray(data.segment) &&
315
+ data.segment.length > 0 &&
316
+ data.name !== undefined &&
317
+ data.name.length > 0)
318
+ )
319
+ .reduce((acc, data) => {
320
+ const name = typeof data.ext === 'object' && data.ext.segtax ? `${data.name}/${data.ext.segtax}` : data.name;
321
+ acc[name] = (acc[name] || []).concat(data.segment.map(seg => seg.id));
322
+ return acc;
323
+ }, {})
324
+ return Object.keys(fpd)
325
+ .map((name, _) => name + ':' + fpd[name].join('|'))
326
+ .join(',')
327
+ }
328
+
320
329
  function appendUserIdsToQueryParams(queryParams, userIds) {
321
330
  _each(userIds, (userIdObjectOrValue, userIdProviderKey) => {
322
331
  const key = USER_ID_CODE_TO_QUERY_ARG[userIdProviderKey];
323
332
 
324
333
  if (USER_ID_CODE_TO_QUERY_ARG.hasOwnProperty(userIdProviderKey)) {
325
334
  switch (userIdProviderKey) {
335
+ case 'merkleId':
336
+ queryParams[key] = userIdObjectOrValue.id;
337
+ break;
326
338
  case 'flocId':
327
339
  queryParams[key] = userIdObjectOrValue.id;
328
340
  break;
@@ -333,7 +345,7 @@ function appendUserIdsToQueryParams(queryParams, userIds) {
333
345
  queryParams[key] = userIdObjectOrValue.lipbid;
334
346
  if (Array.isArray(userIdObjectOrValue.segments) && userIdObjectOrValue.segments.length > 0) {
335
347
  const liveIntentSegments = 'liveintent:' + userIdObjectOrValue.segments.join('|')
336
- queryParams.sm = `${queryParams.sm ? queryParams.sm + encodeURIComponent(',') : ''}${encodeURIComponent(liveIntentSegments)}`;
348
+ queryParams.sm = `${queryParams.sm ? queryParams.sm + ',' : ''}${liveIntentSegments}`;
337
349
  }
338
350
  break;
339
351
  case 'parrableId':
@@ -8,6 +8,7 @@ import { OUTSTREAM } from '../src/video.js';
8
8
  const BIDDER_CODE = 'operaads';
9
9
 
10
10
  const ENDPOINT = 'https://s.adx.opera.com/ortb/v2/';
11
+ const USER_SYNC_ENDPOINT = 'https://s.adx.opera.com/usersync/page';
11
12
 
12
13
  const OUTSTREAM_RENDERER_URL = 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js';
13
14
 
@@ -137,6 +138,25 @@ export const spec = {
137
138
  * @return {UserSync[]} The user syncs which should be dropped.
138
139
  */
139
140
  getUserSyncs: function (syncOptions, serverResponses, gdprConsent, uspConsent) {
141
+ if ('iframeEnabled' in syncOptions && syncOptions.iframeEnabled) {
142
+ return [{
143
+ type: 'iframe',
144
+ url: USER_SYNC_ENDPOINT
145
+ }];
146
+ }
147
+ if ('pixelEnabled' in syncOptions && syncOptions.pixelEnabled) {
148
+ const pixels = deepAccess(serverResponses, '0.body.pixels')
149
+ if (Array.isArray(pixels)) {
150
+ const userSyncPixels = []
151
+ for (const pixel of pixels) {
152
+ userSyncPixels.push({
153
+ type: 'image',
154
+ url: pixel
155
+ })
156
+ }
157
+ return userSyncPixels;
158
+ }
159
+ }
140
160
  return [];
141
161
  },
142
162
 
@@ -212,7 +232,7 @@ function buildOpenRtbBidRequest(bidRequest, bidderRequest) {
212
232
  ext: {}
213
233
  },
214
234
  user: {
215
- id: getUserId(bidRequest)
235
+ buyeruid: getUserId(bidRequest)
216
236
  }
217
237
  }
218
238
 
@@ -0,0 +1,146 @@
1
+ import { registerBidder } from '../src/adapters/bidderFactory.js';
2
+ import {logInfo, logError, getBidIdParameter, _each, getValue, isFn, isPlainObject} from '../src/utils.js';
3
+ import { BANNER } from '../src/mediaTypes.js';
4
+
5
+ const BIDDER_CODE = 'otm';
6
+ const OTM_BID_URL = 'https://ssp.otm-r.com/adjson';
7
+ const DEF_CUR = 'RUB'
8
+
9
+ export const spec = {
10
+
11
+ code: BIDDER_CODE,
12
+ url: OTM_BID_URL,
13
+ supportedMediaTypes: [ BANNER ],
14
+
15
+ /**
16
+ * Determines whether or not the given bid request is valid.
17
+ *
18
+ * @param {object} bid The bid to validate.
19
+ * @return boolean True if this is a valid bid, and false otherwise.
20
+ */
21
+ isBidRequestValid: function (bid) {
22
+ return !!bid.params.tid;
23
+ },
24
+
25
+ /**
26
+ * Build bidder requests.
27
+ *
28
+ * @param validBidRequests
29
+ * @param bidderRequest
30
+ * @returns {[]}
31
+ */
32
+ buildRequests: function (validBidRequests, bidderRequest) {
33
+ logInfo('validBidRequests', validBidRequests);
34
+
35
+ const bidRequests = [];
36
+ let tz = new Date().getTimezoneOffset()
37
+ let referrer = '';
38
+ if (bidderRequest && bidderRequest.refererInfo) {
39
+ referrer = bidderRequest.refererInfo.referer;
40
+ }
41
+
42
+ _each(validBidRequests, (bid) => {
43
+ let domain = getValue(bid.params, 'domain') || ''
44
+ let tid = getValue(bid.params, 'tid')
45
+ let cur = getValue(bid.params, 'currency') || DEF_CUR
46
+ let bidid = getBidIdParameter('bidId', bid)
47
+ let transactionid = getBidIdParameter('transactionId', bid)
48
+ let auctionid = getBidIdParameter('auctionId', bid)
49
+ let bidfloor = _getBidFloor(bid)
50
+
51
+ _each(bid.sizes, size => {
52
+ let width = 0;
53
+ let height = 0;
54
+ if (size.length && typeof size[0] === 'number' && typeof size[1] === 'number') {
55
+ width = size[0];
56
+ height = size[1];
57
+ }
58
+ bidRequests.push({
59
+ method: 'GET',
60
+ url: OTM_BID_URL,
61
+ data: {
62
+ tz: tz,
63
+ w: width,
64
+ h: height,
65
+ domain: domain,
66
+ l: referrer,
67
+ s: tid,
68
+ cur: cur,
69
+ bidid: bidid,
70
+ transactionid: transactionid,
71
+ auctionid: auctionid,
72
+ bidfloor: bidfloor,
73
+ },
74
+ })
75
+ })
76
+ })
77
+ return bidRequests;
78
+ },
79
+
80
+ /**
81
+ * Generate response.
82
+ *
83
+ * @param serverResponse
84
+ * @param request
85
+ * @returns {[]|*[]}
86
+ */
87
+ interpretResponse: function (serverResponse, request) {
88
+ logInfo('serverResponse', serverResponse.body);
89
+
90
+ const responsesBody = serverResponse ? serverResponse.body : {};
91
+ const bidResponses = [];
92
+ try {
93
+ if (responsesBody.length === 0) {
94
+ return [];
95
+ }
96
+
97
+ _each(responsesBody, (bid) => {
98
+ if (bid.ad) {
99
+ bidResponses.push({
100
+ requestId: bid.bidid,
101
+ cpm: bid.cpm,
102
+ width: bid.w,
103
+ height: bid.h,
104
+ creativeId: bid.creativeid,
105
+ currency: bid.currency || 'RUB',
106
+ netRevenue: true,
107
+ ad: bid.ad,
108
+ ttl: bid.ttl,
109
+ transactionId: bid.transactionid,
110
+ meta: {
111
+ advertiserDomains: bid.adDomain ? [bid.adDomain] : []
112
+ }
113
+ });
114
+ }
115
+ });
116
+ } catch (error) {
117
+ logError(error);
118
+ }
119
+
120
+ return bidResponses;
121
+ }
122
+ };
123
+
124
+ /**
125
+ * Get floor value
126
+ * @param bid
127
+ * @returns {null|*}
128
+ * @private
129
+ */
130
+ function _getBidFloor(bid) {
131
+ if (!isFn(bid.getFloor)) {
132
+ return bid.params.bidfloor ? bid.params.bidfloor : 0;
133
+ }
134
+
135
+ let floor = bid.getFloor({
136
+ currency: DEF_CUR,
137
+ mediaType: '*',
138
+ size: '*'
139
+ });
140
+ if (isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === DEF_CUR) {
141
+ return floor.floor;
142
+ }
143
+ return 0;
144
+ }
145
+
146
+ registerBidder(spec);
@@ -1,36 +1,37 @@
1
1
  # Overview
2
2
 
3
- Module Name: OTM Bidder Adapter
4
- Module Type: Bidder Adapter
5
- Maintainer: ?
3
+ **Module Name**: OTM Bidder Adapter
4
+ **Module Type**: Bidder Adapter
5
+ **Maintainer**: e.kretsu@otm-r.com
6
6
 
7
7
  # Description
8
8
 
9
- You can use this adapter to get a bid from otm-r.com.
9
+ OTM Bidder Adapter for Prebid.js. About: https://otm-r.com
10
10
 
11
- About us : http://otm-r.com
11
+ Use `otm` as bidder:
12
12
 
13
+ # Params
14
+ - `tid` required, specific id AdUnit slot.
15
+ - `domain` optional, specific custom domain.
16
+ - `bidfloor` optional.
13
17
 
14
- # Test Parameters
15
- ```javascript
16
- var adUnits = [
17
- {
18
- code: 'div-otm-example',
19
- sizes: [[320, 480]],
20
- bids: [
21
- {
22
- bidder: "otm",
23
- params: {
24
- tid: "99",
25
- bidfloor: 20
26
- }
27
- }
28
- ]
29
- }
30
- ];
18
+ ## AdUnits configuration example
31
19
  ```
20
+ var adUnits = [{
21
+ code: 'your-slot', //use exactly the same code as your slot div id.
22
+ mediaTypes: {
23
+ banner: {
24
+ sizes: [[320, 480]]
25
+ }
26
+ },
27
+ bids: [{
28
+ bidder: 'otm',
29
+ params: {
30
+ tid: 'XXXXX',
31
+ domain: 'specific custom domain, if needed',
32
+ bidfloor: 20
33
+ }
34
+ }]
35
+ }];
32
36
 
33
- Where:
34
-
35
- * tid - A tag id (should have low cardinality)
36
- * bidfloor - Floor price
37
+ ```
@@ -40,6 +40,7 @@ export const spec = {
40
40
  const publisher = setOnAny(validBidRequests, 'params.publisher');
41
41
  const bcat = setOnAny(validBidRequests, 'params.bcat');
42
42
  const badv = setOnAny(validBidRequests, 'params.badv');
43
+ const eids = setOnAny(validBidRequests, 'userIdAsEids')
43
44
  const cur = CURRENCY;
44
45
  const endpointUrl = config.getConfig('outbrain.bidderUrl');
45
46
  const timeout = bidderRequest.timeout;
@@ -105,6 +106,10 @@ export const spec = {
105
106
  deepSetValue(request, 'regs.coppa', config.getConfig('coppa') & 1)
106
107
  }
107
108
 
109
+ if (eids) {
110
+ deepSetValue(request, 'user.ext.eids', eids);
111
+ }
112
+
108
113
  return {
109
114
  method: 'POST',
110
115
  url: endpointUrl,
@@ -0,0 +1,61 @@
1
+ # Overview
2
+
3
+ Module Name: Playwire Bidder Adapter
4
+ Module Type: Bidder Adapter
5
+ Maintainer: grid-tech@themediagrid.com
6
+
7
+ # Description
8
+
9
+ Module that connects to Grid demand source to fetch bids.
10
+ The adapter is GDPR compliant and supports banner and video (instream and outstream).
11
+
12
+ # Test Parameters
13
+ ```
14
+ var adUnits = [
15
+ {
16
+ code: 'test-div',
17
+ sizes: [[300, 250]],
18
+ bids: [
19
+ {
20
+ bidder: "playwire",
21
+ params: {
22
+ uid: '1',
23
+ bidFloor: 0.5
24
+ }
25
+ }
26
+ ]
27
+ },{
28
+ code: 'test-div',
29
+ sizes: [[728, 90]],
30
+ bids: [
31
+ {
32
+ bidder: "playwire",
33
+ params: {
34
+ uid: 2,
35
+ keywords: {
36
+ brandsafety: ['disaster'],
37
+ topic: ['stress', 'fear']
38
+ }
39
+ }
40
+ }
41
+ ]
42
+ },
43
+ {
44
+ code: 'test-div',
45
+ sizes: [[728, 90]],
46
+ mediaTypes: { video: {
47
+ context: 'instream',
48
+ playerSize: [728, 90],
49
+ mimes: ['video/mp4']
50
+ },
51
+ bids: [
52
+ {
53
+ bidder: "playwire",
54
+ params: {
55
+ uid: 11
56
+ }
57
+ }
58
+ ]
59
+ }
60
+ ];
61
+ ```