prebid.js 6.7.0 → 6.8.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 (38) hide show
  1. package/integrationExamples/gpt/x-domain/creative.html +53 -26
  2. package/modules/adagioBidAdapter.js +0 -8
  3. package/modules/adagioBidAdapter.md +1 -1
  4. package/modules/appnexusBidAdapter.js +11 -0
  5. package/modules/brandmetricsRtdProvider.js +168 -0
  6. package/modules/brandmetricsRtdProvider.md +40 -0
  7. package/modules/criteoBidAdapter.js +9 -0
  8. package/modules/currency.js +26 -1
  9. package/modules/displayioBidAdapter.js +157 -0
  10. package/modules/displayioBidAdapter.md +148 -0
  11. package/modules/e_volutionBidAdapter.js +158 -0
  12. package/modules/gumgumBidAdapter.js +52 -38
  13. package/modules/interactiveOffersBidAdapter.js +9 -6
  14. package/modules/sovrnBidAdapter.js +93 -18
  15. package/modules/sovrnBidAdapter.md +80 -2
  16. package/modules/undertoneBidAdapter.js +17 -1
  17. package/modules/yahoosspBidAdapter.js +2 -0
  18. package/package.json +1 -1
  19. package/src/adRendering.js +38 -0
  20. package/src/auction.js +44 -9
  21. package/src/prebid.js +3 -19
  22. package/src/secureCreatives.js +111 -42
  23. package/src/utils.js +13 -3
  24. package/test/helpers/syncPromise.js +71 -0
  25. package/test/spec/auctionmanager_spec.js +148 -16
  26. package/test/spec/modules/adagioBidAdapter_spec.js +0 -10
  27. package/test/spec/modules/appnexusBidAdapter_spec.js +27 -0
  28. package/test/spec/modules/brandmetricsRtdProvider_spec.js +191 -0
  29. package/test/spec/modules/criteoBidAdapter_spec.js +21 -0
  30. package/test/spec/modules/currency_spec.js +21 -6
  31. package/test/spec/modules/displayioBidAdapter_spec.js +239 -0
  32. package/test/spec/modules/e_volutionBidAdapter_spec.js +242 -0
  33. package/test/spec/modules/gumgumBidAdapter_spec.js +46 -0
  34. package/test/spec/modules/sovrnBidAdapter_spec.js +413 -333
  35. package/test/spec/modules/undertoneBidAdapter_spec.js +55 -2
  36. package/test/spec/modules/yahoosspBidAdapter_spec.js +10 -0
  37. package/test/spec/unit/pbjs_api_spec.js +17 -1
  38. package/test/spec/unit/secureCreatives_spec.js +85 -0
@@ -0,0 +1,148 @@
1
+ # Overview
2
+
3
+ ```
4
+ Module Name: DisplayIO Bidder Adapter
5
+ Module Type: Bidder Adapter
6
+ ```
7
+
8
+ # Description
9
+
10
+ Module that connects to display.io's demand sources.
11
+ Web mobile (not relevant for web desktop).
12
+
13
+
14
+ #Features
15
+ | Feature | | Feature | |
16
+ |---------------|---------------------------------------------------------|-----------------------|-----|
17
+ | Bidder Code | displayio | Prebid member | no |
18
+ | Media Types | Banner, video. <br/>Sizes (display 320x480 / vertical video) | GVL ID | no |
19
+ | GDPR Support | yes | Prebid.js Adapter | yes |
20
+ | USP Support | yes | Prebid Server Adapter | no |
21
+
22
+
23
+ #Global configuration
24
+ ```javascript
25
+ <head>
26
+ <script src="https://cdn.display.io/webis/webis.min.js"></script>
27
+ </head>
28
+ <script>
29
+ ......................................
30
+ var CMP_TIMEOUT = 8000;
31
+ var consentManagement = {
32
+ gdpr: {
33
+ cmpApi: 'iab',
34
+ timeout: CMP_TIMEOUT,
35
+ defaultGdprScope: true
36
+ },
37
+ usp: {
38
+ cmpApi: 'iab',
39
+ timeout: CMP_TIMEOUT
40
+ }
41
+ }
42
+
43
+ if (typeof __tcfapi !== 'function') {
44
+ delete consentManagement.gdpr;
45
+ }
46
+
47
+ pbjs.que.push(function() {
48
+ pbjs.setConfig({consentManagement})
49
+ });
50
+
51
+
52
+ ......................................
53
+
54
+ function initAdserver(bidResponses) {
55
+ if (pbjs.initAdserverSet) return;
56
+ pbjs.initAdserverSet = true;
57
+
58
+ ...........................
59
+
60
+ const displayioBids = getDisplayioBid(bidResponses)
61
+ displayioBids.forEach(b => {
62
+ const {adData, placement} = b[1].bids[0];
63
+ webis.init(adData, b[0], {placement})
64
+ })
65
+
66
+ }
67
+
68
+ function getDisplayioBid(bidResponses) {
69
+ const codes = adUnits.map(u => u.code);
70
+ const bids = Object.entries(bidResponses);
71
+ bids.filter(([key, value]) => codes.includes(key) && value.bids[0].bidderCode === 'displayio');
72
+ return bids;
73
+ }
74
+
75
+ ......................................
76
+
77
+ ```
78
+
79
+
80
+ # Bid Parameters
81
+
82
+ | Name | Scope | Type | Description | Example |
83
+ |----------------| ----- | ---- |----------------------------------------|-------------------------------|
84
+ | `siteId` | required | Number | SiteId and PlacementID are your inventory IDs on the display.io platform (please ask your Account Manager for your site and placement IDs). | 7753 |
85
+ | `placementId` | required | Number | SiteId and PlacementID are your inventory IDs on the display.io platform (please ask your Account Manager for your site and placement IDs). | 5375 |
86
+ | `adsSrvDomain` | required | String | | "appsrv.display.io" |
87
+ | `cdnDomain` | required | String | | "cdn.display.io" |
88
+ | `pageCategory` | optional | String | Comma-separated list of IAB content categories that describe the current page or view of the site, list of available values. | "pageCategory1, pageCategory2" |
89
+ | `keywords` | optional | String | Comma-separated list of keywords describing the content. | "keyword1, keyword2, keyword3" |
90
+ | `custom` | optional | Object | User-defined targeting key-value pairs. custom applies to a specific unit. | `{headerTextColor: "red", fixedHeaderSelector: '.site-header'}` |
91
+ | `custom.headerText`| optional | String | Ad container header text. By default, text is "Scroll to continue with content". Limited to 50 characters. | "Our awesome advertisement"|
92
+ | `custom.headerTextColor`| optional | String | Ad container header text color, "white" by default | "#2196f3"|
93
+ | `custom.headerBackgroundColor`| optional | String | Ad container header background color, "black" by default | "#fff" |
94
+ | `custom.adContainerBackgroundColor`| optional | String | Ad container body background color, "transparent" by default | "#000"|
95
+ | `custom.fixedHeaderSelector`| optional | String | In case your webpage has a fixed header – the header Id attribute or header class attribute should be defined as a value for parameter fixedHeaderSelector. | ".site-header"|
96
+
97
+ # adUnit configuration example
98
+ ```javascript
99
+ var adUnits = [
100
+ {
101
+ code: 'ad-tag-1',
102
+ mediaTypes: {
103
+ banner: {
104
+ sizes: [[320, 480]]
105
+ },
106
+ video: {
107
+ sizes: [[360, 640]]
108
+ },
109
+ },
110
+ bids: [
111
+ {
112
+ bidder: 'displayio',
113
+ params: {
114
+ siteId: 1,
115
+ placementId: 1,
116
+ adsSrvDomain: 'appsrv.display.io',
117
+ cdnDomain: 'cdn.display.io',
118
+ pageCategory: 'pageCategory1, pageCategory2', //comma separated
119
+ keywords: 'keyword1, keyword2, keyword3', //comma separated
120
+ custom: {
121
+ headerText: 'Our awesome advertisement',
122
+ headerTextColor: '#2196f3',
123
+ headerBackgroundColor: 'black',
124
+ adContainerBackgroundColor: 'transparent',
125
+ fixedHeaderSelector: '.site-header',
126
+ },
127
+ }
128
+ }
129
+ ]
130
+ },
131
+ // minimal required options
132
+ {
133
+ code: 'ad-tag-2',
134
+ bids: [{
135
+ bidder: 'displayio',
136
+ params: {
137
+ siteId: 1,
138
+ placementId: 1,
139
+ adsSrvDomain: 'appsrv.display.io',
140
+ cdnDomain: 'cdn.display.io',
141
+ }
142
+ }]
143
+ }
144
+ ];
145
+ ```
146
+
147
+ # Additional Details
148
+ [Mobile web prebid.js integration](https://www.display.io/documentation/mobile-web-prebid-js-integration/)
@@ -0,0 +1,158 @@
1
+ import { registerBidder } from '../src/adapters/bidderFactory.js';
2
+ import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
3
+ import { isFn, deepAccess, logMessage } from '../src/utils.js';
4
+
5
+ const BIDDER_CODE = 'e_volution';
6
+ const AD_URL = 'https://service.e-volution.ai/?c=o&m=multi';
7
+ const URL_SYNC = 'https://service.e-volution.ai/?c=o&m=sync';
8
+ const NO_SYNC = true;
9
+
10
+ function isBidResponseValid(bid) {
11
+ if (!bid.requestId || !bid.cpm || !bid.creativeId ||
12
+ !bid.ttl || !bid.currency) {
13
+ return false;
14
+ }
15
+ switch (bid.mediaType) {
16
+ case BANNER:
17
+ return Boolean(bid.width && bid.height && bid.ad);
18
+ case VIDEO:
19
+ return Boolean(bid.vastUrl);
20
+ case NATIVE:
21
+ return Boolean(bid.native && bid.native.title && bid.native.image && bid.native.impressionTrackers);
22
+ default:
23
+ return false;
24
+ }
25
+ }
26
+
27
+ function getBidFloor(bid) {
28
+ if (!isFn(bid.getFloor)) {
29
+ return deepAccess(bid, 'params.bidfloor', 0);
30
+ }
31
+
32
+ try {
33
+ const bidFloor = bid.getFloor({
34
+ currency: 'USD',
35
+ mediaType: '*',
36
+ size: '*',
37
+ });
38
+ return bidFloor.floor;
39
+ } catch (_) {
40
+ return 0
41
+ }
42
+ }
43
+
44
+ export const spec = {
45
+ code: BIDDER_CODE,
46
+ supportedMediaTypes: [BANNER, VIDEO, NATIVE],
47
+ noSync: NO_SYNC,
48
+
49
+ isBidRequestValid: (bid) => {
50
+ return Boolean(bid.bidId && bid.params && !isNaN(parseInt(bid.params.placementId)));
51
+ },
52
+
53
+ buildRequests: (validBidRequests = [], bidderRequest) => {
54
+ let winTop = window;
55
+ let location;
56
+ try {
57
+ location = new URL(bidderRequest.refererInfo.referer)
58
+ winTop = window.top;
59
+ } catch (e) {
60
+ location = winTop.location;
61
+ logMessage(e);
62
+ };
63
+ let placements = [];
64
+ let request = {
65
+ 'deviceWidth': winTop.screen.width,
66
+ 'deviceHeight': winTop.screen.height,
67
+ 'language': (navigator && navigator.language) ? navigator.language.split('-')[0] : '',
68
+ 'secure': 1,
69
+ 'host': location.host,
70
+ 'page': location.pathname,
71
+ 'placements': placements
72
+ };
73
+ if (bidderRequest) {
74
+ if (bidderRequest.uspConsent) {
75
+ request.ccpa = bidderRequest.uspConsent;
76
+ }
77
+ if (bidderRequest.gdprConsent) {
78
+ request.gdpr = bidderRequest.gdprConsent
79
+ }
80
+ }
81
+ const len = validBidRequests.length;
82
+
83
+ for (let i = 0; i < len; i++) {
84
+ let bid = validBidRequests[i];
85
+
86
+ const placement = {
87
+ placementId: bid.params.placementId,
88
+ bidId: bid.bidId,
89
+ bidfloor: getBidFloor(bid)
90
+ }
91
+
92
+ if (bid.mediaTypes && bid.mediaTypes[BANNER] && bid.mediaTypes[BANNER].sizes) {
93
+ placement.traffic = BANNER;
94
+ placement.sizes = bid.mediaTypes[BANNER].sizes;
95
+ } else if (bid.mediaTypes && bid.mediaTypes[VIDEO] && bid.mediaTypes[VIDEO].playerSize) {
96
+ placement.traffic = VIDEO;
97
+ placement.wPlayer = bid.mediaTypes[VIDEO].playerSize[0];
98
+ placement.hPlayer = bid.mediaTypes[VIDEO].playerSize[1];
99
+ placement.minduration = bid.mediaTypes[VIDEO].minduration;
100
+ placement.maxduration = bid.mediaTypes[VIDEO].maxduration;
101
+ placement.mimes = bid.mediaTypes[VIDEO].mimes;
102
+ placement.protocols = bid.mediaTypes[VIDEO].protocols;
103
+ placement.startdelay = bid.mediaTypes[VIDEO].startdelay;
104
+ placement.placement = bid.mediaTypes[VIDEO].placement;
105
+ placement.skip = bid.mediaTypes[VIDEO].skip;
106
+ placement.skipafter = bid.mediaTypes[VIDEO].skipafter;
107
+ placement.minbitrate = bid.mediaTypes[VIDEO].minbitrate;
108
+ placement.maxbitrate = bid.mediaTypes[VIDEO].maxbitrate;
109
+ placement.delivery = bid.mediaTypes[VIDEO].delivery;
110
+ placement.playbackmethod = bid.mediaTypes[VIDEO].playbackmethod;
111
+ placement.api = bid.mediaTypes[VIDEO].api;
112
+ placement.linearity = bid.mediaTypes[VIDEO].linearity;
113
+ } else if (bid.mediaTypes && bid.mediaTypes[NATIVE]) {
114
+ placement.traffic = NATIVE;
115
+ placement.native = bid.mediaTypes[NATIVE];
116
+ }
117
+
118
+ if (bid.schain) {
119
+ placements.schain = bid.schain;
120
+ }
121
+
122
+ placements.push(placement);
123
+ }
124
+ return {
125
+ method: 'POST',
126
+ url: AD_URL,
127
+ data: request
128
+ };
129
+ },
130
+
131
+ interpretResponse: (serverResponse) => {
132
+ let response = [];
133
+ for (let i = 0; i < serverResponse.body.length; i++) {
134
+ let resItem = serverResponse.body[i];
135
+ if (isBidResponseValid(resItem)) {
136
+ const advertiserDomains = resItem.adomain && resItem.adomain.length ? resItem.adomain : [];
137
+ resItem.meta = { ...resItem.meta, advertiserDomains };
138
+
139
+ response.push(resItem);
140
+ }
141
+ }
142
+ return response;
143
+ },
144
+
145
+ getUserSyncs: (syncOptions, serverResponses) => {
146
+ if (NO_SYNC) {
147
+ return false
148
+ } else {
149
+ return [{
150
+ type: 'image',
151
+ url: URL_SYNC
152
+ }];
153
+ }
154
+ }
155
+
156
+ };
157
+
158
+ registerBidder(spec);
@@ -17,32 +17,48 @@ const TIME_TO_LIVE = 60
17
17
  const DELAY_REQUEST_TIME = 1800000; // setting to 30 mins
18
18
 
19
19
  let invalidRequestIds = {};
20
- let browserParams = {};
21
20
  let pageViewId = null;
22
21
 
23
22
  // TODO: potential 0 values for browserParams sent to ad server
24
23
  function _getBrowserParams(topWindowUrl) {
25
- let topWindow
26
- let topScreen
27
- let topUrl
28
- let ggad
29
- let ns
30
- function getNetworkSpeed() {
31
- const connection = window.navigator && (window.navigator.connection || window.navigator.mozConnection || window.navigator.webkitConnection)
32
- const Mbps = connection && (connection.downlink || connection.bandwidth)
33
- return Mbps ? Math.round(Mbps * 1024) : null
24
+ const paramRegex = paramName => new RegExp(`[?#&](${paramName}=(.*?))($|&)`, 'i');
25
+
26
+ let browserParams = {};
27
+ let topWindow;
28
+ let topScreen;
29
+ let topUrl;
30
+ let ggad;
31
+ let ggdeal;
32
+ let ns;
33
+
34
+ function getNetworkSpeed () {
35
+ const connection = window.navigator && (window.navigator.connection || window.navigator.mozConnection || window.navigator.webkitConnection);
36
+ const Mbps = connection && (connection.downlink || connection.bandwidth);
37
+ return Mbps ? Math.round(Mbps * 1024) : null;
34
38
  }
35
- function getOgURL() {
36
- let ogURL = ''
37
- const ogURLSelector = "meta[property='og:url']"
38
- const head = document && document.getElementsByTagName('head')[0]
39
- const ogURLElement = head.querySelector(ogURLSelector)
40
- ogURL = ogURLElement ? ogURLElement.content : null
41
- return ogURL
39
+
40
+ function getOgURL () {
41
+ let ogURL = '';
42
+ const ogURLSelector = "meta[property='og:url']";
43
+ const head = document && document.getElementsByTagName('head')[0];
44
+ const ogURLElement = head.querySelector(ogURLSelector);
45
+ ogURL = ogURLElement ? ogURLElement.content : null;
46
+ return ogURL;
42
47
  }
43
- if (browserParams.vw) {
44
- // we've already initialized browserParams, just return it.
45
- return browserParams
48
+
49
+ function stripGGParams (url) {
50
+ const params = [
51
+ 'ggad',
52
+ 'ggdeal'
53
+ ];
54
+
55
+ return params.reduce((result, param) => {
56
+ const matches = url.match(paramRegex(param));
57
+ if (!matches) return result;
58
+ matches[1] && (result = result.replace(matches[1], ''));
59
+ matches[3] && (result = result.replace(matches[3], ''));
60
+ return result;
61
+ }, url);
46
62
  }
47
63
 
48
64
  try {
@@ -51,7 +67,7 @@ function _getBrowserParams(topWindowUrl) {
51
67
  topUrl = topWindowUrl || '';
52
68
  } catch (error) {
53
69
  logError(error);
54
- return browserParams
70
+ return browserParams;
55
71
  }
56
72
 
57
73
  browserParams = {
@@ -59,23 +75,25 @@ function _getBrowserParams(topWindowUrl) {
59
75
  vh: topWindow.innerHeight,
60
76
  sw: topScreen.width,
61
77
  sh: topScreen.height,
62
- pu: topUrl,
78
+ pu: stripGGParams(topUrl),
63
79
  ce: storage.cookiesAreEnabled(),
64
80
  dpr: topWindow.devicePixelRatio || 1,
65
81
  jcsi: JSON.stringify(JCSI),
66
82
  ogu: getOgURL()
67
- }
83
+ };
68
84
 
69
- ns = getNetworkSpeed()
85
+ ns = getNetworkSpeed();
70
86
  if (ns) {
71
- browserParams.ns = ns
87
+ browserParams.ns = ns;
72
88
  }
73
89
 
74
- ggad = (topUrl.match(/#ggad=(\w+)$/) || [0, 0])[1]
75
- if (ggad) {
76
- browserParams[isNaN(ggad) ? 'eAdBuyId' : 'adBuyId'] = ggad
77
- }
78
- return browserParams
90
+ ggad = (topUrl.match(paramRegex('ggad')) || [0, 0, 0])[2];
91
+ if (ggad) browserParams[isNaN(ggad) ? 'eAdBuyId' : 'adBuyId'] = ggad;
92
+
93
+ ggdeal = (topUrl.match(paramRegex('ggdeal')) || [0, 0, 0])[2];
94
+ if (ggdeal) browserParams.ggdeal = ggdeal;
95
+
96
+ return browserParams;
79
97
  }
80
98
 
81
99
  function getWrapperCode(wrapper, data) {
@@ -291,9 +309,9 @@ function buildRequests(validBidRequests, bidderRequest) {
291
309
  } = bidRequest;
292
310
  const { currency, floor } = _getFloor(mediaTypes, params.bidfloor, bidRequest);
293
311
  const eids = getEids(userId);
312
+ const gpid = deepAccess(ortb2Imp, 'ext.data.pbadslot') || deepAccess(ortb2Imp, 'ext.data.adserver.adslot');
294
313
  let sizes = [1, 1];
295
314
  let data = {};
296
- let gpid = '';
297
315
 
298
316
  const date = new Date();
299
317
  const lt = date.getTime();
@@ -309,12 +327,8 @@ function buildRequests(validBidRequests, bidderRequest) {
309
327
  // ADTS-134 Retrieve ID envelopes
310
328
  for (const eid in eids) data[eid] = eids[eid];
311
329
 
312
- // ADJS-1024 & ADSS-1297
313
- if (deepAccess(ortb2Imp, 'ext.data.pbadslot')) {
314
- gpid = deepAccess(ortb2Imp, 'ext.data.pbadslot')
315
- } else if (deepAccess(ortb2Imp, 'ext.data.adserver.name')) {
316
- gpid = ortb2Imp.ext.data.adserver.adslot
317
- }
330
+ // ADJS-1024 & ADSS-1297 & ADTS-175
331
+ gpid && (data.gpid = gpid);
318
332
 
319
333
  if (mediaTypes.banner) {
320
334
  sizes = mediaTypes.banner.sizes;
@@ -384,7 +398,7 @@ function buildRequests(validBidRequests, bidderRequest) {
384
398
  sizes,
385
399
  url: BID_ENDPOINT,
386
400
  method: 'GET',
387
- data: Object.assign(data, _getBrowserParams(topWindowUrl), _getDigiTrustQueryParams(userId), { gpid })
401
+ data: Object.assign(data, _getBrowserParams(topWindowUrl), _getDigiTrustQueryParams(userId))
388
402
  })
389
403
  });
390
404
  return bids;
@@ -83,7 +83,8 @@ function parseRequestPrebidjsToOpenRTB(prebidRequest) {
83
83
  let openRTBRequest = JSON.parse(JSON.stringify(DEFAULT['OpenRTBBidRequest']));
84
84
  openRTBRequest.id = prebidRequest.auctionId;
85
85
  openRTBRequest.ext = {
86
- auctionstart: Date.now()
86
+ refererInfo: prebidRequest.refererInfo,
87
+ auctionId: prebidRequest.auctionId
87
88
  };
88
89
 
89
90
  openRTBRequest.site = JSON.parse(JSON.stringify(DEFAULT['OpenRTBBidRequestSite']));
@@ -111,15 +112,17 @@ function parseRequestPrebidjsToOpenRTB(prebidRequest) {
111
112
  openRTBRequest.user = JSON.parse(JSON.stringify(DEFAULT['OpenRTBBidRequestUser']));
112
113
 
113
114
  openRTBRequest.imp = [];
114
- prebidRequest.bids.forEach(function(bid, impId) {
115
- impId++;
115
+ prebidRequest.bids.forEach(function(bid) {
116
116
  if (!ret.partnerId) {
117
117
  ret.partnerId = bid.params.partnerId;
118
118
  }
119
119
  let imp = JSON.parse(JSON.stringify(DEFAULT['OpenRTBBidRequestImp']));
120
- imp.id = impId;
120
+ imp.id = bid.bidId;
121
121
  imp.secure = secure;
122
- imp.tagid = bid.bidId;
122
+ imp.tagid = bid.adUnitCode;
123
+ imp.ext = {
124
+ rawdata: bid
125
+ };
123
126
 
124
127
  openRTBRequest.site.publisher.id = openRTBRequest.site.publisher.id || 0;
125
128
  openRTBRequest.tmax = openRTBRequest.tmax || bid.params.tmax || 0;
@@ -152,7 +155,7 @@ function parseResponseOpenRTBToPrebidjs(openRTBResponse) {
152
155
  if (seatbid.bid && seatbid.bid.forEach) {
153
156
  seatbid.bid.forEach(function(bid) {
154
157
  let prebid = JSON.parse(JSON.stringify(DEFAULT['PrebidBid']));
155
- prebid.requestId = bid.ext.tagid;
158
+ prebid.requestId = bid.impid;
156
159
  prebid.ad = bid.adm;
157
160
  prebid.creativeId = bid.crid;
158
161
  prebid.cpm = bid.price;
@@ -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)