prebid.js 6.5.0 → 6.9.0

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 (139) hide show
  1. package/.eslintrc.js +8 -1
  2. package/integrationExamples/gpt/amp/creative.html +11 -33
  3. package/integrationExamples/gpt/weboramaRtdProvider_example.html +154 -115
  4. package/integrationExamples/gpt/x-domain/creative.html +63 -29
  5. package/modules/.submodules.json +2 -1
  6. package/modules/adagioBidAdapter.js +0 -8
  7. package/modules/adagioBidAdapter.md +1 -1
  8. package/modules/adbookpspBidAdapter.js +27 -10
  9. package/modules/adhashBidAdapter.js +3 -3
  10. package/modules/adkernelBidAdapter.js +2 -1
  11. package/modules/admanBidAdapter.js +10 -4
  12. package/modules/adomikAnalyticsAdapter.js +23 -11
  13. package/modules/adqueryIdSystem.js +103 -0
  14. package/modules/adqueryIdSystem.md +35 -0
  15. package/modules/appnexusBidAdapter.js +14 -2
  16. package/modules/asealBidAdapter.js +58 -0
  17. package/modules/asealBidAdapter.md +52 -0
  18. package/modules/bliinkBidAdapter.js +2 -1
  19. package/modules/brandmetricsRtdProvider.js +168 -0
  20. package/modules/brandmetricsRtdProvider.md +40 -0
  21. package/modules/colossussspBidAdapter.js +12 -8
  22. package/modules/colossussspBidAdapter.md +15 -1
  23. package/modules/compassBidAdapter.js +10 -3
  24. package/modules/consumableBidAdapter.md +1 -1
  25. package/modules/conversantBidAdapter.js +7 -0
  26. package/modules/criteoBidAdapter.js +10 -1
  27. package/modules/criteoIdSystem.js +29 -7
  28. package/modules/currency.js +26 -1
  29. package/modules/displayioBidAdapter.js +157 -0
  30. package/modules/displayioBidAdapter.md +148 -0
  31. package/modules/e_volutionBidAdapter.js +158 -0
  32. package/modules/glimpseBidAdapter.js +66 -44
  33. package/modules/gnetBidAdapter.js +3 -3
  34. package/modules/gnetBidAdapter.md +4 -4
  35. package/modules/gumgumBidAdapter.js +56 -42
  36. package/modules/idImportLibrary.js +45 -8
  37. package/modules/idImportLibrary.md +4 -0
  38. package/modules/improvedigitalBidAdapter.js +29 -2
  39. package/modules/interactiveOffersBidAdapter.js +9 -6
  40. package/modules/jwplayerRtdProvider.js +71 -6
  41. package/modules/jwplayerRtdProvider.md +27 -11
  42. package/modules/kargoBidAdapter.js +2 -2
  43. package/modules/lunamediahbBidAdapter.js +32 -4
  44. package/modules/nextMillenniumBidAdapter.js +3 -1
  45. package/modules/oguryBidAdapter.js +14 -14
  46. package/modules/onetagBidAdapter.js +4 -2
  47. package/modules/pilotxBidAdapter.js +147 -0
  48. package/modules/pilotxBidAdapter.md +50 -0
  49. package/modules/priceFloors.js +2 -1
  50. package/modules/proxistoreBidAdapter.js +0 -2
  51. package/modules/pubmaticAnalyticsAdapter.js +16 -0
  52. package/modules/richaudienceBidAdapter.js +10 -4
  53. package/modules/riseBidAdapter.js +18 -7
  54. package/modules/rtbhouseBidAdapter.js +14 -4
  55. package/modules/rtdModule/index.js +14 -15
  56. package/modules/rubiconAnalyticsAdapter.js +8 -2
  57. package/modules/seedingAllianceBidAdapter.js +3 -3
  58. package/modules/sharethroughBidAdapter.js +12 -17
  59. package/modules/showheroes-bsBidAdapter.js +13 -2
  60. package/modules/sortableAnalyticsAdapter.js +5 -4
  61. package/modules/sovrnBidAdapter.js +93 -18
  62. package/modules/sovrnBidAdapter.md +80 -2
  63. package/modules/synacormediaBidAdapter.js +31 -10
  64. package/modules/tappxBidAdapter.js +8 -5
  65. package/modules/teadsBidAdapter.js +1 -2
  66. package/modules/undertoneBidAdapter.js +17 -1
  67. package/modules/userId/eids.js +7 -1
  68. package/modules/userId/userId.md +8 -0
  69. package/modules/viewability.js +177 -0
  70. package/modules/viewability.md +87 -0
  71. package/modules/weboramaRtdProvider.js +264 -34
  72. package/modules/weboramaRtdProvider.md +110 -40
  73. package/modules/welectBidAdapter.js +106 -0
  74. package/modules/yahoosspBidAdapter.js +2 -0
  75. package/package.json +2 -1
  76. package/src/adRendering.js +38 -0
  77. package/src/adloader.js +2 -1
  78. package/src/auction.js +103 -73
  79. package/src/bidderSettings.js +69 -0
  80. package/src/hook.js +5 -1
  81. package/src/prebid.js +19 -21
  82. package/src/secureCreatives.js +131 -47
  83. package/src/targeting.js +3 -2
  84. package/src/utils.js +13 -10
  85. package/test/helpers/syncPromise.js +71 -0
  86. package/test/spec/auctionmanager_spec.js +179 -15
  87. package/test/spec/modules/adagioBidAdapter_spec.js +0 -10
  88. package/test/spec/modules/adbookpspBidAdapter_spec.js +17 -3
  89. package/test/spec/modules/adhashBidAdapter_spec.js +2 -2
  90. package/test/spec/modules/admanBidAdapter_spec.js +2 -2
  91. package/test/spec/modules/adomikAnalyticsAdapter_spec.js +3 -1
  92. package/test/spec/modules/adqueryIdSystem_spec.js +74 -0
  93. package/test/spec/modules/appnexusBidAdapter_spec.js +27 -0
  94. package/test/spec/modules/asealBidAdapter_spec.js +144 -0
  95. package/test/spec/modules/bliinkBidAdapter_spec.js +2 -0
  96. package/test/spec/modules/brandmetricsRtdProvider_spec.js +191 -0
  97. package/test/spec/modules/colossussspBidAdapter_spec.js +5 -2
  98. package/test/spec/modules/compassBidAdapter_spec.js +1 -0
  99. package/test/spec/modules/conversantBidAdapter_spec.js +54 -2
  100. package/test/spec/modules/criteoBidAdapter_spec.js +21 -0
  101. package/test/spec/modules/criteoIdSystem_spec.js +6 -3
  102. package/test/spec/modules/currency_spec.js +21 -6
  103. package/test/spec/modules/displayioBidAdapter_spec.js +239 -0
  104. package/test/spec/modules/e_volutionBidAdapter_spec.js +242 -0
  105. package/test/spec/modules/eids_spec.js +15 -0
  106. package/test/spec/modules/glimpseBidAdapter_spec.js +0 -18
  107. package/test/spec/modules/gnetBidAdapter_spec.js +6 -6
  108. package/test/spec/modules/gumgumBidAdapter_spec.js +46 -0
  109. package/test/spec/modules/idImportLibrary_spec.js +197 -10
  110. package/test/spec/modules/improvedigitalBidAdapter_spec.js +61 -0
  111. package/test/spec/modules/jwplayerRtdProvider_spec.js +195 -2
  112. package/test/spec/modules/kargoBidAdapter_spec.js +1 -1
  113. package/test/spec/modules/loglyliftBidAdapter_spec.js +1 -1
  114. package/test/spec/modules/lunamediahbBidAdapter_spec.js +27 -1
  115. package/test/spec/modules/nextMillenniumBidAdapter_spec.js +1 -1
  116. package/test/spec/modules/oguryBidAdapter_spec.js +69 -3
  117. package/test/spec/modules/pilotxBidAdapter_spec.js +244 -0
  118. package/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +13 -1
  119. package/test/spec/modules/realTimeDataModule_spec.js +67 -5
  120. package/test/spec/modules/richaudienceBidAdapter_spec.js +40 -0
  121. package/test/spec/modules/riseBidAdapter_spec.js +31 -5
  122. package/test/spec/modules/rtbhouseBidAdapter_spec.js +20 -0
  123. package/test/spec/modules/rubiconAnalyticsAdapter_spec.js +61 -1
  124. package/test/spec/modules/sharethroughBidAdapter_spec.js +91 -6
  125. package/test/spec/modules/showheroes-bsBidAdapter_spec.js +2 -0
  126. package/test/spec/modules/sortableAnalyticsAdapter_spec.js +2 -3
  127. package/test/spec/modules/sovrnBidAdapter_spec.js +413 -333
  128. package/test/spec/modules/synacormediaBidAdapter_spec.js +70 -0
  129. package/test/spec/modules/tappxBidAdapter_spec.js +0 -19
  130. package/test/spec/modules/teadsBidAdapter_spec.js +14 -59
  131. package/test/spec/modules/undertoneBidAdapter_spec.js +55 -2
  132. package/test/spec/modules/userId_spec.js +68 -19
  133. package/test/spec/modules/viewability_spec.js +280 -0
  134. package/test/spec/modules/weboramaRtdProvider_spec.js +536 -20
  135. package/test/spec/modules/welectBidAdapter_spec.js +211 -0
  136. package/test/spec/modules/yahoosspBidAdapter_spec.js +10 -0
  137. package/test/spec/unit/core/bidderSettings_spec.js +123 -0
  138. package/test/spec/unit/pbjs_api_spec.js +21 -8
  139. package/test/spec/unit/secureCreatives_spec.js +143 -24
@@ -152,7 +152,7 @@
152
152
 
153
153
  import {config} from '../../src/config.js';
154
154
  import {module} from '../../src/hook.js';
155
- import {logError, logWarn} from '../../src/utils.js';
155
+ import {logError, logInfo, logWarn} from '../../src/utils.js';
156
156
  import events from '../../src/events.js';
157
157
  import CONSTANTS from '../../src/constants.json';
158
158
  import {gdprDataHandler, uspDataHandler} from '../../src/adapterManager.js';
@@ -197,12 +197,13 @@ const setEventsListeners = (function () {
197
197
  return function setEventsListeners() {
198
198
  if (!registered) {
199
199
  Object.entries({
200
- [CONSTANTS.EVENTS.AUCTION_INIT]: 'onAuctionInitEvent',
201
- [CONSTANTS.EVENTS.AUCTION_END]: 'onAuctionEndEvent',
202
- [CONSTANTS.EVENTS.BID_RESPONSE]: 'onBidResponseEvent',
203
- [CONSTANTS.EVENTS.BID_REQUESTED]: 'onBidRequestEvent'
204
- }).forEach(([ev, handler]) => {
200
+ [CONSTANTS.EVENTS.AUCTION_INIT]: ['onAuctionInitEvent'],
201
+ [CONSTANTS.EVENTS.AUCTION_END]: ['onAuctionEndEvent', getAdUnitTargeting],
202
+ [CONSTANTS.EVENTS.BID_RESPONSE]: ['onBidResponseEvent'],
203
+ [CONSTANTS.EVENTS.BID_REQUESTED]: ['onBidRequestEvent']
204
+ }).forEach(([ev, [handler, preprocess]]) => {
205
205
  events.on(ev, (args) => {
206
+ preprocess && preprocess(args);
206
207
  subModules.forEach(sm => {
207
208
  try {
208
209
  sm[handler] && sm[handler](args, sm.config, _userConsent)
@@ -255,6 +256,7 @@ function initSubModules() {
255
256
  }
256
257
  });
257
258
  subModules = subModulesByOrder;
259
+ logInfo(`Real time data module enabled, using submodules: ${subModules.map((m) => m.name).join(', ')}`);
258
260
  }
259
261
 
260
262
  /**
@@ -289,18 +291,12 @@ export function setBidRequestsData(fn, reqBidsConfigObj) {
289
291
  return exitHook();
290
292
  }
291
293
 
292
- if (shouldDelayAuction) {
293
- waitTimeout = setTimeout(exitHook, _moduleConfig.auctionDelay);
294
- }
294
+ waitTimeout = setTimeout(exitHook, shouldDelayAuction ? _moduleConfig.auctionDelay : 0);
295
295
 
296
296
  relevantSubModules.forEach(sm => {
297
297
  sm.getBidRequestData(reqBidsConfigObj, onGetBidRequestDataCallback.bind(sm), sm.config, _userConsent)
298
298
  });
299
299
 
300
- if (!shouldDelayAuction) {
301
- return exitHook();
302
- }
303
-
304
300
  function onGetBidRequestDataCallback() {
305
301
  if (isDone) {
306
302
  return;
@@ -308,12 +304,15 @@ export function setBidRequestsData(fn, reqBidsConfigObj) {
308
304
  if (this.config && this.config.waitForIt) {
309
305
  callbacksExpected--;
310
306
  }
311
- if (callbacksExpected <= 0) {
312
- return exitHook();
307
+ if (callbacksExpected === 0) {
308
+ setTimeout(exitHook, 0);
313
309
  }
314
310
  }
315
311
 
316
312
  function exitHook() {
313
+ if (isDone) {
314
+ return;
315
+ }
317
316
  isDone = true;
318
317
  clearTimeout(waitTimeout);
319
318
  fn.call(this, reqBidsConfigObj);
@@ -234,6 +234,9 @@ function sendMessage(auctionId, bidWonId, trigger) {
234
234
 
235
235
  let auction = {
236
236
  clientTimeoutMillis: auctionCache.timeout,
237
+ auctionStart: auctionCache.timestamp,
238
+ auctionEnd: auctionCache.endTs,
239
+ bidderOrder: auctionCache.bidderOrder,
237
240
  samplingFactor,
238
241
  accountId,
239
242
  adUnits: Object.keys(adUnitMap).map(i => adUnitMap[i]),
@@ -384,8 +387,9 @@ export function parseBidResponse(bid, previousBidResponse, auctionFloorData) {
384
387
  'floorRuleValue', () => deepAccess(bid, 'floorData.floorRuleValue'),
385
388
  'floorRule', () => debugTurnedOn() ? deepAccess(bid, 'floorData.floorRule') : undefined,
386
389
  'adomains', () => {
387
- let adomains = deepAccess(bid, 'meta.advertiserDomains');
388
- return Array.isArray(adomains) && adomains.length > 0 ? adomains.slice(0, 10) : undefined
390
+ const adomains = deepAccess(bid, 'meta.advertiserDomains');
391
+ const validAdomains = Array.isArray(adomains) && adomains.filter(domain => typeof domain === 'string');
392
+ return validAdomains && validAdomains.length > 0 ? validAdomains.slice(0, 10) : undefined
389
393
  }
390
394
  ]);
391
395
  }
@@ -582,6 +586,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, {
582
586
  cacheEntry.bids = {};
583
587
  cacheEntry.bidsWon = {};
584
588
  cacheEntry.gamHasRendered = {};
589
+ cacheEntry.bidderOrder = [];
585
590
  cacheEntry.referrer = deepAccess(args, 'bidderRequests.0.refererInfo.referer');
586
591
  const floorData = deepAccess(args, 'bidderRequests.0.bids.0.floorData');
587
592
  if (floorData) {
@@ -607,6 +612,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, {
607
612
  }
608
613
  break;
609
614
  case BID_REQUESTED:
615
+ cache.auctions[args.auctionId].bidderOrder.push(args.bidderCode);
610
616
  Object.assign(cache.auctions[args.auctionId].bids, args.bids.reduce((memo, bid) => {
611
617
  // mark adUnits we expect bidWon events for
612
618
  cache.auctions[args.auctionId].bidsWon[bid.adUnitCode] = false;
@@ -152,10 +152,10 @@ export const spec = {
152
152
 
153
153
  const { seatbid, cur } = serverResponse.body;
154
154
 
155
- const bidResponses = flatten(seatbid.map(seat => seat.bid)).reduce((result, bid) => {
155
+ const bidResponses = (typeof seatbid != 'undefined') ? flatten(seatbid.map(seat => seat.bid)).reduce((result, bid) => {
156
156
  result[bid.impid - 1] = bid;
157
157
  return result;
158
- }, []);
158
+ }, []) : [];
159
159
 
160
160
  return bids
161
161
  .map((bid, id) => {
@@ -167,7 +167,7 @@ export const spec = {
167
167
  cpm: bidResponse.price,
168
168
  creativeId: bidResponse.crid,
169
169
  ttl: 1000,
170
- netRevenue: bid.netRevenue === 'net',
170
+ netRevenue: (!bid.netRevenue || bid.netRevenue === 'net'),
171
171
  currency: cur,
172
172
  mediaType: NATIVE,
173
173
  bidderCode: BIDDER_CODE,
@@ -1,10 +1,10 @@
1
- import { generateUUID, deepAccess, inIframe } from '../src/utils.js';
1
+ import { deepAccess, generateUUID, inIframe } from '../src/utils.js';
2
2
  import { registerBidder } from '../src/adapters/bidderFactory.js';
3
3
  import { config } from '../src/config.js';
4
4
  import { BANNER, VIDEO } from '../src/mediaTypes.js';
5
5
  import { createEidsArray } from './userId/eids.js';
6
6
 
7
- const VERSION = '4.0.1';
7
+ const VERSION = '4.1.0';
8
8
  const BIDDER_CODE = 'sharethrough';
9
9
  const SUPPLY_ID = 'WYu2BXv1';
10
10
 
@@ -23,6 +23,7 @@ export const sharethroughAdapterSpec = {
23
23
 
24
24
  buildRequests: (bidRequests, bidderRequest) => {
25
25
  const timeout = config.getConfig('bidderTimeout');
26
+ const firstPartyData = config.getConfig('ortb2') || {};
26
27
 
27
28
  const nonHttp = sharethroughInternal.getProtocol().indexOf('http') < 0;
28
29
  const secure = nonHttp || (sharethroughInternal.getProtocol().indexOf('https') > -1);
@@ -35,12 +36,8 @@ export const sharethroughAdapterSpec = {
35
36
  site: {
36
37
  domain: window.location.hostname,
37
38
  page: window.location.href,
38
- ref: bidderRequest.refererInfo ? bidderRequest.refererInfo.referer || null : null,
39
- },
40
- user: {
41
- ext: {
42
- eids: userIdAsEids(bidRequests[0]),
43
- },
39
+ ref: deepAccess(bidderRequest, 'refererInfo.referer'),
40
+ ...firstPartyData.site,
44
41
  },
45
42
  device: {
46
43
  ua: navigator.userAgent,
@@ -66,6 +63,10 @@ export const sharethroughAdapterSpec = {
66
63
  test: 0,
67
64
  };
68
65
 
66
+ req.user = nullish(firstPartyData.user, {});
67
+ if (!req.user.ext) req.user.ext = {};
68
+ req.user.ext.eids = userIdAsEids(bidRequests[0]);
69
+
69
70
  if (bidderRequest.gdprConsent) {
70
71
  const gdprApplies = bidderRequest.gdprConsent.gdprApplies === true;
71
72
  req.regs.ext.gdpr = gdprApplies ? 1 : 0;
@@ -86,15 +87,9 @@ export const sharethroughAdapterSpec = {
86
87
  impression.ext = { gpid: gpid };
87
88
  }
88
89
 
89
- // if request is for video, we only support instream
90
- if (bidReq.mediaTypes && bidReq.mediaTypes.video && bidReq.mediaTypes.video.context === 'outstream') {
91
- // return null so we can easily remove this imp from the array of imps that we send to adserver
92
- return null;
93
- }
94
-
95
- if (bidReq.mediaTypes && bidReq.mediaTypes.video) {
96
- const videoRequest = bidReq.mediaTypes.video;
90
+ const videoRequest = deepAccess(bidReq, 'mediaTypes.video');
97
91
 
92
+ if (videoRequest) {
98
93
  // default playerSize, only change this if we know width and height are properly defined in the request
99
94
  let [w, h] = [640, 360];
100
95
  if (videoRequest.playerSize && videoRequest.playerSize[0] && videoRequest.playerSize[1]) {
@@ -117,9 +112,9 @@ export const sharethroughAdapterSpec = {
117
112
  startdelay: nullish(videoRequest.startdelay, 0),
118
113
  skipmin: nullish(videoRequest.skipmin, 0),
119
114
  skipafter: nullish(videoRequest.skipafter, 0),
115
+ placement: videoRequest.context === 'instream' ? 1 : +deepAccess(videoRequest, 'placement', 4),
120
116
  };
121
117
 
122
- if (videoRequest.placement) impression.video.placement = videoRequest.placement;
123
118
  if (videoRequest.delivery) impression.video.delivery = videoRequest.delivery;
124
119
  if (videoRequest.companiontype) impression.video.companiontype = videoRequest.companiontype;
125
120
  if (videoRequest.companionad) impression.video.companionad = videoRequest.companionad;
@@ -61,15 +61,24 @@ export const spec = {
61
61
  }
62
62
  }
63
63
 
64
+ const consentData = bidderRequest.gdprConsent || {};
65
+
66
+ const gdprConsent = {
67
+ apiVersion: consentData.apiVersion || 2,
68
+ gdprApplies: consentData.gdprApplies || 0,
69
+ consentString: consentData.consentString || '',
70
+ }
71
+
64
72
  return {
65
73
  type: streamType,
74
+ adUnitCode: bid.adUnitCode,
66
75
  bidId: bid.bidId,
67
76
  mediaType: type,
68
77
  context: context,
69
78
  playerId: getBidIdParameter('playerId', bid.params),
70
79
  auctionId: bidderRequest.auctionId,
71
80
  bidderCode: BIDDER_CODE,
72
- gdprConsent: bidderRequest.gdprConsent,
81
+ gdprConsent: gdprConsent,
73
82
  start: +new Date(),
74
83
  timeout: 3000,
75
84
  size: {
@@ -159,6 +168,7 @@ function createBids(bidRes, reqData) {
159
168
  let bidUnit = {};
160
169
  bidUnit.cpm = bid.cpm;
161
170
  bidUnit.requestId = bid.bidId;
171
+ bidUnit.adUnitCode = reqBid.adUnitCode;
162
172
  bidUnit.currency = bid.currency;
163
173
  bidUnit.mediaType = bid.mediaType || VIDEO;
164
174
  bidUnit.ttl = TTL;
@@ -183,7 +193,8 @@ function createBids(bidRes, reqData) {
183
193
  } else if (bid.context === 'outstream') {
184
194
  const renderer = Renderer.install({
185
195
  id: bid.bidId,
186
- url: '//',
196
+ url: 'https://static.showheroes.com/renderer.js',
197
+ adUnitCode: reqBid.adUnitCode,
187
198
  config: {
188
199
  playerId: reqBid.playerId,
189
200
  width: bid.size.width,
@@ -5,6 +5,7 @@ import adapterManager from '../src/adapterManager.js';
5
5
  import {ajax} from '../src/ajax.js';
6
6
  import {getGlobal} from '../src/prebidGlobal.js';
7
7
  import { config } from '../src/config.js';
8
+ import {bidderSettings} from '../src/bidderSettings.js';
8
9
 
9
10
  const DEFAULT_PROTOCOL = 'https';
10
11
  const DEFAULT_HOST = 'pa.deployads.com';
@@ -182,11 +183,11 @@ function getFactor(bidder) {
182
183
  }
183
184
 
184
185
  function getBiddersFactors() {
185
- const pb = getGlobal();
186
186
  const result = {};
187
- if (pb && pb.bidderSettings) {
188
- Object.keys(pb.bidderSettings).forEach(bidderKey => {
189
- const bidder = pb.bidderSettings[bidderKey];
187
+ const settings = bidderSettings.getSettings();
188
+ if (settings) {
189
+ Object.keys(settings).forEach(bidderKey => {
190
+ const bidder = settings[bidderKey];
190
191
  const factor = getFactor(bidder);
191
192
  if (factor !== null) {
192
193
  result[bidderKey] = factor;
@@ -1,12 +1,38 @@
1
- import { _each, getBidIdParameter, isArray, deepClone, parseUrl, getUniqueIdentifierStr, deepSetValue, logError, deepAccess } from '../src/utils.js';
1
+ import { _each, getBidIdParameter, isArray, deepClone, parseUrl, getUniqueIdentifierStr, deepSetValue, logError, deepAccess, isInteger, logWarn } from '../src/utils.js';
2
2
  import { registerBidder } from '../src/adapters/bidderFactory.js'
3
- import { BANNER } from '../src/mediaTypes.js'
3
+ import { ADPOD, BANNER, VIDEO } from '../src/mediaTypes.js'
4
4
  import { createEidsArray } from './userId/eids.js';
5
5
  import {config} from '../src/config.js';
6
6
 
7
+ const ORTB_VIDEO_PARAMS = {
8
+ 'mimes': (value) => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string'),
9
+ 'minduration': (value) => isInteger(value),
10
+ 'maxduration': (value) => isInteger(value),
11
+ 'protocols': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 10),
12
+ 'w': (value) => isInteger(value),
13
+ 'h': (value) => isInteger(value),
14
+ 'startdelay': (value) => isInteger(value),
15
+ 'placement': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 5),
16
+ 'linearity': (value) => [1, 2].indexOf(value) !== -1,
17
+ 'skip': (value) => [0, 1].indexOf(value) !== -1,
18
+ 'skipmin': (value) => isInteger(value),
19
+ 'skipafter': (value) => isInteger(value),
20
+ 'sequence': (value) => isInteger(value),
21
+ 'battr': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 17),
22
+ 'maxextended': (value) => isInteger(value),
23
+ 'minbitrate': (value) => isInteger(value),
24
+ 'maxbitrate': (value) => isInteger(value),
25
+ 'boxingallowed': (value) => [0, 1].indexOf(value) !== -1,
26
+ 'playbackmethod': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 6),
27
+ 'playbackend': (value) => [1, 2, 3].indexOf(value) !== -1,
28
+ 'delivery': (value) => [1, 2, 3].indexOf(value) !== -1,
29
+ 'pos': (value) => Array.isArray(value) && value.every(v => v >= 0 && v <= 7),
30
+ 'api': (value) => Array.isArray(value) && value.every(v => v >= 1 && v <= 6)
31
+ }
32
+
7
33
  export const spec = {
8
34
  code: 'sovrn',
9
- supportedMediaTypes: [BANNER],
35
+ supportedMediaTypes: [BANNER, VIDEO],
10
36
  gvlid: 13,
11
37
 
12
38
  /**
@@ -15,7 +41,12 @@ export const spec = {
15
41
  * @return boolean for whether or not a bid is valid
16
42
  */
17
43
  isBidRequestValid: function(bid) {
18
- return !!(bid.params.tagid && !isNaN(parseFloat(bid.params.tagid)) && isFinite(bid.params.tagid))
44
+ return !!(
45
+ bid.params.tagid &&
46
+ !isNaN(parseFloat(bid.params.tagid)) &&
47
+ isFinite(bid.params.tagid) &&
48
+ deepAccess(bid, 'mediaTypes.video.context') !== ADPOD
49
+ )
19
50
  },
20
51
 
21
52
  /**
@@ -50,13 +81,9 @@ export const spec = {
50
81
  }
51
82
  iv = iv || getBidIdParameter('iv', bid.params)
52
83
 
53
- let bidSizes = (bid.mediaTypes && bid.mediaTypes.banner && bid.mediaTypes.banner.sizes) || bid.sizes
54
- bidSizes = ((isArray(bidSizes) && isArray(bidSizes[0])) ? bidSizes : [bidSizes])
55
- bidSizes = bidSizes.filter(size => isArray(size))
56
- const processedSizes = bidSizes.map(size => ({w: parseInt(size[0], 10), h: parseInt(size[1], 10)}))
57
84
  const floorInfo = (bid.getFloor && typeof bid.getFloor === 'function') ? bid.getFloor({
58
85
  currency: 'USD',
59
- mediaType: 'banner',
86
+ mediaType: bid.mediaTypes && bid.mediaTypes.banner ? 'banner' : 'video',
60
87
  size: '*'
61
88
  }) : {}
62
89
  floorInfo.floor = floorInfo.floor || getBidIdParameter('bidfloor', bid.params)
@@ -64,13 +91,24 @@ export const spec = {
64
91
  const imp = {
65
92
  adunitcode: bid.adUnitCode,
66
93
  id: bid.bidId,
67
- banner: {
94
+ tagid: String(getBidIdParameter('tagid', bid.params)),
95
+ bidfloor: floorInfo.floor
96
+ }
97
+
98
+ if (deepAccess(bid, 'mediaTypes.banner')) {
99
+ let bidSizes = deepAccess(bid, 'mediaTypes.banner.sizes') || bid.sizes
100
+ bidSizes = (isArray(bidSizes) && isArray(bidSizes[0])) ? bidSizes : [bidSizes]
101
+ bidSizes = bidSizes.filter(size => isArray(size))
102
+ const processedSizes = bidSizes.map(size => ({w: parseInt(size[0], 10), h: parseInt(size[1], 10)}))
103
+
104
+ imp.banner = {
68
105
  format: processedSizes,
69
106
  w: 1,
70
107
  h: 1,
71
- },
72
- tagid: String(getBidIdParameter('tagid', bid.params)),
73
- bidfloor: floorInfo.floor
108
+ };
109
+ }
110
+ if (deepAccess(bid, 'mediaTypes.video')) {
111
+ imp.video = _buildVideoRequestObj(bid);
74
112
  }
75
113
 
76
114
  imp.ext = getBidIdParameter('ext', bid.ortb2Imp) || undefined
@@ -149,7 +187,7 @@ export const spec = {
149
187
  seatbid[0].bid &&
150
188
  seatbid[0].bid.length > 0) {
151
189
  seatbid[0].bid.map(sovrnBid => {
152
- sovrnBidResponses.push({
190
+ const bid = {
153
191
  requestId: sovrnBid.impid,
154
192
  cpm: parseFloat(sovrnBid.price),
155
193
  width: parseInt(sovrnBid.w),
@@ -158,11 +196,18 @@ export const spec = {
158
196
  dealId: sovrnBid.dealid || null,
159
197
  currency: 'USD',
160
198
  netRevenue: true,
161
- mediaType: BANNER,
162
- ad: decodeURIComponent(`${sovrnBid.adm}<img src="${sovrnBid.nurl}">`),
163
199
  ttl: sovrnBid.ext ? (sovrnBid.ext.ttl || 90) : 90,
164
200
  meta: { advertiserDomains: sovrnBid && sovrnBid.adomain ? sovrnBid.adomain : [] }
165
- });
201
+ }
202
+
203
+ if (!sovrnBid.nurl) {
204
+ bid.mediaType = VIDEO
205
+ bid.vastXml = decodeURIComponent(sovrnBid.adm)
206
+ } else {
207
+ bid.mediaType = BANNER
208
+ bid.ad = decodeURIComponent(`${sovrnBid.adm}<img src="${sovrnBid.nurl}">`)
209
+ }
210
+ sovrnBidResponses.push(bid);
166
211
  });
167
212
  }
168
213
  return sovrnBidResponses
@@ -209,4 +254,34 @@ export const spec = {
209
254
  },
210
255
  }
211
256
 
212
- registerBidder(spec);
257
+ function _buildVideoRequestObj(bid) {
258
+ const videoObj = {}
259
+ const videoAdUnitParams = deepAccess(bid, 'mediaTypes.video', {})
260
+ const videoBidderParams = deepAccess(bid, 'params.video', {})
261
+ const computedParams = {}
262
+
263
+ if (Array.isArray(videoAdUnitParams.playerSize)) {
264
+ const sizes = (Array.isArray(videoAdUnitParams.playerSize[0])) ? videoAdUnitParams.playerSize[0] : videoAdUnitParams.playerSize
265
+ computedParams.w = sizes[0]
266
+ computedParams.h = sizes[1]
267
+ }
268
+
269
+ const videoParams = {
270
+ ...computedParams,
271
+ ...videoAdUnitParams,
272
+ ...videoBidderParams
273
+ };
274
+
275
+ Object.keys(ORTB_VIDEO_PARAMS).forEach(paramName => {
276
+ if (videoParams.hasOwnProperty(paramName)) {
277
+ if (ORTB_VIDEO_PARAMS[paramName](videoParams[paramName])) {
278
+ videoObj[paramName] = videoParams[paramName]
279
+ } else {
280
+ logWarn(`The OpenRTB video param ${paramName} has been skipped due to misformating. Please refer to OpenRTB 2.5 spec.`);
281
+ }
282
+ }
283
+ })
284
+ return videoObj
285
+ }
286
+
287
+ registerBidder(spec)
@@ -10,9 +10,9 @@ Maintainer: jrosendahl@sovrn.com
10
10
 
11
11
  Sovrn's adapter integration to the Prebid library. Posts plain-text JSON to the /rtb/bid endpoint.
12
12
 
13
- # Test Parameters
13
+ # Banner Test Parameters
14
14
 
15
- ```
15
+ ```js
16
16
  var adUnits = [
17
17
  {
18
18
  code: 'test-leaderboard',
@@ -45,3 +45,81 @@ var adUnits = [
45
45
  }
46
46
  ]
47
47
  ```
48
+
49
+ # Video Test Parameters
50
+ ### Instream
51
+ ```js
52
+ var videoAdUnit = {
53
+ code: 'video1',
54
+ sizes: [640, 480],
55
+ mediaTypes: {
56
+ video: {
57
+ context: 'instream',
58
+ playerSize: [640, 480],
59
+ mimes: ['video/mp4'],
60
+ protocols: [1, 2, 3, 4, 5, 6, 7, 8],
61
+ playbackmethod: [2],
62
+ skip: 1,
63
+ },
64
+ },
65
+ bids: [
66
+ {
67
+ bidder: 'sovrn',
68
+ // Prebid Server Bidder Params https://docs.prebid.org/dev-docs/pbs-bidders.html#sovrn
69
+ params: {
70
+ tagid: '315045',
71
+ bidfloor: '0.04',
72
+ },
73
+ },
74
+ ],
75
+ }
76
+ ```
77
+ ### Outstream
78
+ ```js
79
+ var adUnits = [
80
+ {
81
+ code: videoId,
82
+ mediaTypes: {
83
+ video: {
84
+ context: 'outstream',
85
+ playerSize: [640, 360],
86
+ mimes: ['video/mp4'],
87
+ protocols: [1, 2, 3, 4, 5, 6, 7, 8],
88
+ playbackmethod: [2],
89
+ skip: 1,
90
+ },
91
+ },
92
+ bids: [
93
+ {
94
+ bidder: 'sovrn',
95
+ // Prebid Server Bidder Params https://docs.prebid.org/dev-docs/pbs-bidders.html#sovrn
96
+ params: {
97
+ tagid: '315045',
98
+ bidfloor: '0.04',
99
+ },
100
+ },
101
+ ],
102
+ renderer: {
103
+ url: 'https://acdn.adnxs.com/video/outstream/ANOutstreamVideo.js',
104
+ render: function (bid) {
105
+ adResponse = {
106
+ ad: {
107
+ video: {
108
+ content: bid.vastXml,
109
+ player_height: bid.height,
110
+ player_width: bid.width,
111
+ },
112
+ },
113
+ }
114
+ // push to render queue because ANOutstreamVideo may not be loaded yet.
115
+ bid.renderer.push(() => {
116
+ ANOutstreamVideo.renderAd({
117
+ targetId: bid.adUnitCode,
118
+ adResponse: adResponse,
119
+ })
120
+ })
121
+ },
122
+ },
123
+ },
124
+ ]
125
+ ```
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- import { getAdUnitSizes, logWarn, deepSetValue } from '../src/utils.js';
3
+ import { getAdUnitSizes, logWarn, deepSetValue, isFn, isPlainObject } from '../src/utils.js';
4
4
  import { registerBidder } from '../src/adapters/bidderFactory.js';
5
5
  import { BANNER, VIDEO } from '../src/mediaTypes.js';
6
6
  import includes from 'core-js-pure/features/array/includes.js';
@@ -67,11 +67,7 @@ export const spec = {
67
67
  } else {
68
68
  seatId = bid.params.seatId;
69
69
  }
70
- const tagIdOrplacementId = bid.params.tagId || bid.params.placementId;
71
- const bidFloor = bid.params.bidfloor ? parseFloat(bid.params.bidfloor) : null;
72
- if (isNaN(bidFloor)) {
73
- logWarn(`Synacormedia: there is an invalid bid floor: ${bid.params.bidfloor}`);
74
- }
70
+ const tagIdOrPlacementId = bid.params.tagId || bid.params.placementId;
75
71
  let pos = parseInt(bid.params.pos, 10);
76
72
  if (isNaN(pos)) {
77
73
  logWarn(`Synacormedia: there is an invalid POS: ${bid.params.pos}`);
@@ -83,9 +79,9 @@ export const spec = {
83
79
 
84
80
  let imps = [];
85
81
  if (videoOrBannerKey === 'banner') {
86
- imps = this.buildBannerImpressions(adSizes, bid, tagIdOrplacementId, pos, bidFloor, videoOrBannerKey);
82
+ imps = this.buildBannerImpressions(adSizes, bid, tagIdOrPlacementId, pos, videoOrBannerKey);
87
83
  } else if (videoOrBannerKey === 'video') {
88
- imps = this.buildVideoImpressions(adSizes, bid, tagIdOrplacementId, pos, bidFloor, videoOrBannerKey);
84
+ imps = this.buildVideoImpressions(adSizes, bid, tagIdOrPlacementId, pos, videoOrBannerKey);
89
85
  }
90
86
  if (imps.length > 0) {
91
87
  imps.forEach(i => openRtbBidRequest.imp.push(i));
@@ -128,7 +124,7 @@ export const spec = {
128
124
  return eids;
129
125
  },
130
126
 
131
- buildBannerImpressions: function (adSizes, bid, tagIdOrPlacementId, pos, bidFloor, videoOrBannerKey) {
127
+ buildBannerImpressions: function (adSizes, bid, tagIdOrPlacementId, pos, videoOrBannerKey) {
132
128
  let format = [];
133
129
  let imps = [];
134
130
  adSizes.forEach((size, i) => {
@@ -151,6 +147,10 @@ export const spec = {
151
147
  },
152
148
  tagid: tagIdOrPlacementId,
153
149
  };
150
+ const bidFloor = getBidFloor(bid, 'banner', '*');
151
+ if (isNaN(bidFloor)) {
152
+ logWarn(`Synacormedia: there is an invalid bid floor: ${bid.params.bidfloor}`);
153
+ }
154
154
  if (bidFloor !== null && !isNaN(bidFloor)) {
155
155
  imp.bidfloor = bidFloor;
156
156
  }
@@ -159,7 +159,7 @@ export const spec = {
159
159
  return imps;
160
160
  },
161
161
 
162
- buildVideoImpressions: function(adSizes, bid, tagIdOrPlacementId, pos, bidFloor, videoOrBannerKey) {
162
+ buildVideoImpressions: function(adSizes, bid, tagIdOrPlacementId, pos, videoOrBannerKey) {
163
163
  let imps = [];
164
164
  adSizes.forEach((size, i) => {
165
165
  if (!size || size.length != 2) {
@@ -171,6 +171,11 @@ export const spec = {
171
171
  id: `${videoOrBannerKey.substring(0, 1)}${bid.bidId}-${size0}x${size1}`,
172
172
  tagid: tagIdOrPlacementId
173
173
  };
174
+ const bidFloor = getBidFloor(bid, 'video', size);
175
+ if (isNaN(bidFloor)) {
176
+ logWarn(`Synacormedia: there is an invalid bid floor: ${bid.params.bidfloor}`);
177
+ }
178
+
174
179
  if (bidFloor !== null && !isNaN(bidFloor)) {
175
180
  imp.bidfloor = bidFloor;
176
181
  }
@@ -287,4 +292,20 @@ export const spec = {
287
292
  }
288
293
  };
289
294
 
295
+ function getBidFloor(bid, mediaType, size) {
296
+ if (!isFn(bid.getFloor)) {
297
+ return bid.params.bidfloor ? parseFloat(bid.params.bidfloor) : null;
298
+ }
299
+ let floor = bid.getFloor({
300
+ currency: 'USD',
301
+ mediaType,
302
+ size
303
+ });
304
+
305
+ if (isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') {
306
+ return floor.floor;
307
+ }
308
+ return null;
309
+ }
310
+
290
311
  registerBidder(spec);
@@ -53,7 +53,14 @@ export const spec = {
53
53
  * @return boolean True if this is a valid bid, and false otherwise.
54
54
  */
55
55
  isBidRequestValid: function(bid) {
56
- return validBasic(bid) && validMediaType(bid)
56
+ // bid.params.host
57
+ if ((new RegExp(`^(vz.*|zz.*)\\.*$`, 'i')).test(bid.params.host)) { // New endpoint
58
+ if ((new RegExp(`^(zz.*)\\.*$`, 'i')).test(bid.params.host)) return validBasic(bid)
59
+ else return validBasic(bid) && validMediaType(bid)
60
+ } else { // This is backward compatible feature. It will be remove in the future
61
+ if ((new RegExp(`^(ZZ.*)\\.*$`, 'i')).test(bid.params.endpoint)) return validBasic(bid)
62
+ else return validBasic(bid) && validMediaType(bid)
63
+ }
57
64
  },
58
65
 
59
66
  /**
@@ -169,10 +176,6 @@ function validMediaType(bid) {
169
176
  logWarn(LOG_PREFIX, 'Please review the mandatory Tappx parameters for Video. Video context not supported.');
170
177
  return false;
171
178
  }
172
- if (typeof video.mimes == 'undefined') {
173
- logWarn(LOG_PREFIX, 'Please review the mandatory Tappx parameters for Video. Mimes param is mandatory.');
174
- return false;
175
- }
176
179
  }
177
180
 
178
181
  return true;
@@ -190,8 +190,7 @@ function buildRequestObject(bid) {
190
190
  const reqObj = {};
191
191
  let placementId = getValue(bid.params, 'placementId');
192
192
  let pageId = getValue(bid.params, 'pageId');
193
- const impressionData = deepAccess(bid, 'ortb2Imp.ext.data');
194
- const gpid = deepAccess(impressionData, 'pbadslot') || deepAccess(impressionData, 'adserver.adslot');
193
+ const gpid = deepAccess(bid, 'ortb2Imp.ext.gpid');
195
194
 
196
195
  reqObj.sizes = getSizes(bid);
197
196
  reqObj.bidId = getBidIdParameter('bidId', bid);