prebid.js 5.19.0 → 5.20.3

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 (67) hide show
  1. package/.circleci/config.yml +4 -5
  2. package/modules/adxcgBidAdapter.js +311 -359
  3. package/modules/airgridRtdProvider.js +1 -1
  4. package/modules/appnexusBidAdapter.js +9 -3
  5. package/modules/atsAnalyticsAdapter.js +67 -46
  6. package/modules/atsAnalyticsAdapter.md +1 -0
  7. package/modules/betweenBidAdapter.js +2 -1
  8. package/modules/browsiRtdProvider.js +106 -18
  9. package/modules/cleanioRtdProvider.js +192 -0
  10. package/modules/cleanioRtdProvider.md +59 -0
  11. package/modules/deltaprojectsBidAdapter.js +252 -0
  12. package/modules/deltaprojectsBidAdapter.md +32 -0
  13. package/modules/gridBidAdapter.js +1 -0
  14. package/modules/ipromBidAdapter.js +79 -0
  15. package/modules/ixBidAdapter.js +7 -1
  16. package/modules/jixieBidAdapter.js +8 -2
  17. package/modules/justpremiumBidAdapter.js +6 -1
  18. package/modules/kinessoIdSystem.js +1 -1
  19. package/modules/livewrappedAnalyticsAdapter.js +5 -0
  20. package/modules/multibid/index.js +3 -3
  21. package/modules/nativoBidAdapter.js +5 -1
  22. package/modules/openxBidAdapter.js +1 -1
  23. package/modules/operaadsBidAdapter.js +21 -1
  24. package/modules/otmBidAdapter.js +146 -0
  25. package/modules/otmBidAdapter.md +27 -26
  26. package/modules/outbrainBidAdapter.js +5 -0
  27. package/modules/playwireBidAdapter.md +61 -0
  28. package/modules/pubmaticBidAdapter.js +1 -1
  29. package/modules/rtdModule/index.js +2 -2
  30. package/modules/sonobiBidAdapter.js +7 -0
  31. package/modules/sortableBidAdapter.js +1 -0
  32. package/modules/teadsBidAdapter.js +3 -0
  33. package/modules/trustxBidAdapter.js +8 -6
  34. package/modules/ventesBidAdapter.js +370 -0
  35. package/modules/ventesBidAdapter.md +94 -0
  36. package/modules/yahoosspBidAdapter.js +6 -6
  37. package/package.json +1 -1
  38. package/src/auction.js +11 -11
  39. package/src/prebid.js +20 -4
  40. package/src/targeting.js +8 -0
  41. package/test/fixtures/fixtures.js +2 -1
  42. package/test/spec/modules/adxcgBidAdapter_spec.js +827 -571
  43. package/test/spec/modules/appnexusBidAdapter_spec.js +16 -1
  44. package/test/spec/modules/atsAnalyticsAdapter_spec.js +42 -9
  45. package/test/spec/modules/browsiRtdProvider_spec.js +62 -7
  46. package/test/spec/modules/cleanioRtdProvider_spec.js +188 -0
  47. package/test/spec/modules/deltaprojectsBidAdapter_spec.js +399 -0
  48. package/test/spec/modules/eplanningBidAdapter_spec.js +8 -8
  49. package/test/spec/modules/ipromBidAdapter_spec.js +195 -0
  50. package/test/spec/modules/ixBidAdapter_spec.js +3 -3
  51. package/test/spec/modules/jixieBidAdapter_spec.js +13 -11
  52. package/test/spec/modules/justpremiumBidAdapter_spec.js +9 -2
  53. package/test/spec/modules/livewrappedAnalyticsAdapter_spec.js +23 -4
  54. package/test/spec/modules/multibid_spec.js +31 -31
  55. package/test/spec/modules/openxBidAdapter_spec.js +0 -26
  56. package/test/spec/modules/operaadsBidAdapter_spec.js +38 -6
  57. package/test/spec/modules/otmBidAdapter_spec.js +67 -0
  58. package/test/spec/modules/outbrainBidAdapter_spec.js +18 -0
  59. package/test/spec/modules/pubmaticBidAdapter_spec.js +40 -0
  60. package/test/spec/modules/sonobiBidAdapter_spec.js +34 -1
  61. package/test/spec/modules/sortableBidAdapter_spec.js +11 -0
  62. package/test/spec/modules/teadsBidAdapter_spec.js +132 -0
  63. package/test/spec/modules/trustxBidAdapter_spec.js +3 -3
  64. package/test/spec/modules/ventesBidAdapter_spec.js +845 -0
  65. package/test/spec/unit/core/adapterManager_spec.js +7 -3
  66. package/test/spec/unit/core/targeting_spec.js +93 -0
  67. package/test/spec/unit/pbjs_api_spec.js +3 -1
@@ -0,0 +1,252 @@
1
+ import { registerBidder } from '../src/adapters/bidderFactory.js';
2
+ import { BANNER } from '../src/mediaTypes.js';
3
+ import {
4
+ _each, _map, isFn, isNumber, createTrackPixelHtml, deepAccess, parseUrl, logWarn, logError
5
+ } from '../src/utils.js';
6
+ import {config} from '../src/config.js';
7
+
8
+ export const BIDDER_CODE = 'deltaprojects';
9
+ export const BIDDER_ENDPOINT_URL = 'https://d5p.de17a.com/dogfight/prebid';
10
+ export const USERSYNC_URL = 'https://userservice.de17a.com/getuid/prebid';
11
+
12
+ /** -- isBidRequestValid --**/
13
+ function isBidRequestValid(bid) {
14
+ if (!bid) return false;
15
+
16
+ if (bid.bidder !== BIDDER_CODE) return false;
17
+
18
+ // publisher id is required
19
+ const publisherId = deepAccess(bid, 'params.publisherId')
20
+ if (!publisherId) {
21
+ logError('Invalid bid request, missing publisher id in params');
22
+ return false;
23
+ }
24
+
25
+ return true;
26
+ }
27
+
28
+ /** -- Build requests --**/
29
+ function buildRequests(validBidRequests, bidderRequest) {
30
+ /** == shared ==**/
31
+ // -- build id
32
+ const id = bidderRequest.auctionId;
33
+
34
+ // -- build site
35
+ const loc = parseUrl(bidderRequest.refererInfo.referer);
36
+ const publisherId = setOnAny(validBidRequests, 'params.publisherId');
37
+ const siteId = setOnAny(validBidRequests, 'params.siteId');
38
+ const site = {
39
+ id: siteId,
40
+ domain: loc.hostname,
41
+ page: loc.href,
42
+ ref: loc.href,
43
+ publisher: { id: publisherId },
44
+ };
45
+
46
+ // -- build device
47
+ const ua = navigator.userAgent;
48
+ const device = {
49
+ ua,
50
+ w: screen.width,
51
+ h: screen.height
52
+ }
53
+
54
+ // -- build user, reg
55
+ let user = { ext: {} };
56
+ const regs = { ext: {} };
57
+ const gdprConsent = bidderRequest && bidderRequest.gdprConsent;
58
+ if (gdprConsent) {
59
+ user.ext = { consent: gdprConsent.consentString };
60
+ if (typeof gdprConsent.gdprApplies == 'boolean') {
61
+ regs.ext.gdpr = gdprConsent.gdprApplies ? 1 : 0
62
+ }
63
+ }
64
+
65
+ // -- build tmax
66
+ let tmax = (bidderRequest && bidderRequest.timeout > 0) ? bidderRequest.timeout : undefined;
67
+
68
+ // build bid specific
69
+ return validBidRequests.map(validBidRequest => {
70
+ const openRTBRequest = buildOpenRTBRequest(validBidRequest, id, site, device, user, tmax, regs);
71
+ return {
72
+ method: 'POST',
73
+ url: BIDDER_ENDPOINT_URL,
74
+ data: openRTBRequest,
75
+ options: { contentType: 'application/json' },
76
+ bids: [validBidRequest],
77
+ };
78
+ });
79
+ }
80
+
81
+ function buildOpenRTBRequest(validBidRequest, id, site, device, user, tmax, regs) {
82
+ // build cur
83
+ const currency = config.getConfig('currency.adServerCurrency') || deepAccess(validBidRequest, 'params.currency');
84
+ const cur = currency && [currency];
85
+
86
+ // build impression
87
+ const impression = buildImpression(validBidRequest, currency);
88
+
89
+ // build test
90
+ const test = deepAccess(validBidRequest, 'params.test') ? 1 : 0
91
+
92
+ const at = 1
93
+
94
+ // build source
95
+ const source = {
96
+ tid: validBidRequest.transactionId,
97
+ fd: 1,
98
+ }
99
+
100
+ return {
101
+ id,
102
+ at,
103
+ imp: [impression],
104
+ site,
105
+ device,
106
+ user,
107
+ test,
108
+ tmax,
109
+ cur,
110
+ source,
111
+ regs,
112
+ ext: {},
113
+ };
114
+ }
115
+
116
+ function buildImpression(bid, currency) {
117
+ const impression = {
118
+ id: bid.bidId,
119
+ tagid: bid.params.tagId,
120
+ ext: {},
121
+ };
122
+
123
+ const bannerMediaType = deepAccess(bid, `mediaTypes.${BANNER}`);
124
+ impression.banner = buildImpressionBanner(bid, bannerMediaType);
125
+
126
+ // bid floor
127
+ const bidFloor = getBidFloor(bid, BANNER, '*', currency);
128
+ if (bidFloor) {
129
+ impression.bidfloor = bidFloor.floor;
130
+ impression.bidfloorcur = bidFloor.currency;
131
+ }
132
+
133
+ return impression;
134
+ }
135
+
136
+ function buildImpressionBanner(bid, bannerMediaType) {
137
+ const bannerSizes = (bannerMediaType && bannerMediaType.sizes) || bid.sizes;
138
+ return {
139
+ format: _map(bannerSizes, ([width, height]) => ({ w: width, h: height })),
140
+ };
141
+ }
142
+
143
+ /** -- Interpret response --**/
144
+ function interpretResponse(serverResponse) {
145
+ if (!serverResponse.body) {
146
+ logWarn('Response body is invalid, return !!');
147
+ return [];
148
+ }
149
+
150
+ const { body: { id, seatbid, cur } } = serverResponse;
151
+ if (!id || !seatbid) {
152
+ logWarn('Id / seatbid of response is invalid, return !!');
153
+ return [];
154
+ }
155
+
156
+ const bidResponses = [];
157
+
158
+ _each(seatbid, seatbid => {
159
+ _each(seatbid.bid, bid => {
160
+ const bidObj = {
161
+ requestId: bid.impid,
162
+ cpm: parseFloat(bid.price),
163
+ width: parseInt(bid.w),
164
+ height: parseInt(bid.h),
165
+ creativeId: bid.crid || bid.id,
166
+ dealId: bid.dealid || null,
167
+ currency: cur,
168
+ netRevenue: true,
169
+ ttl: 60,
170
+ };
171
+
172
+ bidObj.mediaType = BANNER;
173
+ bidObj.ad = bid.adm;
174
+ if (bid.nurl) {
175
+ bidObj.ad += createTrackPixelHtml(decodeURIComponent(bid.nurl));
176
+ }
177
+ if (bid.ext) {
178
+ bidObj[BIDDER_CODE] = bid.ext;
179
+ }
180
+ bidResponses.push(bidObj);
181
+ });
182
+ });
183
+ return bidResponses;
184
+ }
185
+
186
+ /** -- On Bid Won -- **/
187
+ function onBidWon(bid) {
188
+ let cpm = bid.cpm;
189
+ if (bid.currency && bid.currency !== bid.originalCurrency && typeof bid.getCpmInNewCurrency === 'function') {
190
+ cpm = bid.getCpmInNewCurrency(bid.originalCurrency);
191
+ }
192
+ const wonPrice = Math.round(cpm * 1000000);
193
+ const wonPriceMacroPatten = /\$\{AUCTION_PRICE:B64\}/g;
194
+ bid.ad = bid.ad.replace(wonPriceMacroPatten, wonPrice);
195
+ }
196
+
197
+ /** -- Get user syncs --**/
198
+ function getUserSyncs(syncOptions, serverResponses, gdprConsent) {
199
+ const syncs = []
200
+
201
+ if (syncOptions.pixelEnabled) {
202
+ let gdprParams;
203
+ if (gdprConsent) {
204
+ if (typeof gdprConsent.gdprApplies === 'boolean') {
205
+ gdprParams = `?gdpr=${Number(gdprConsent.gdprApplies)}&gdpr_consent=${gdprConsent.consentString}`;
206
+ } else {
207
+ gdprParams = `?gdpr_consent=${gdprConsent.consentString}`;
208
+ }
209
+ } else {
210
+ gdprParams = '';
211
+ }
212
+ syncs.push({
213
+ type: 'image',
214
+ url: USERSYNC_URL + gdprParams
215
+ });
216
+ }
217
+ return syncs;
218
+ }
219
+
220
+ /** -- Get bid floor --**/
221
+ export function getBidFloor(bid, mediaType, size, currency) {
222
+ if (isFn(bid.getFloor)) {
223
+ const bidFloorCurrency = currency || 'USD';
224
+ const bidFloor = bid.getFloor({currency: bidFloorCurrency, mediaType: mediaType, size: size});
225
+ if (isNumber(bidFloor.floor)) {
226
+ return bidFloor;
227
+ }
228
+ }
229
+ }
230
+
231
+ /** -- Helper methods --**/
232
+ function setOnAny(collection, key) {
233
+ for (let i = 0, result; i < collection.length; i++) {
234
+ result = deepAccess(collection[i], key);
235
+ if (result) {
236
+ return result;
237
+ }
238
+ }
239
+ }
240
+
241
+ /** -- Register -- */
242
+ export const spec = {
243
+ code: BIDDER_CODE,
244
+ supportedMediaTypes: [BANNER],
245
+ isBidRequestValid,
246
+ buildRequests,
247
+ interpretResponse,
248
+ onBidWon,
249
+ getUserSyncs,
250
+ };
251
+
252
+ registerBidder(spec);
@@ -0,0 +1,32 @@
1
+ # Overview
2
+
3
+ ```
4
+ Module Name: Delta Projects Bid Adapter
5
+ Module Type: Bidder Adapter
6
+ Maintainer: dev@deltaprojects.com
7
+ ```
8
+
9
+ # Description
10
+
11
+ Connects to Delta Projects DSP for bids.
12
+
13
+ # Test Parameters
14
+ ```
15
+ // define banner unit
16
+ var bannerUnit = {
17
+ code: 'div-gpt-ad-1460505748561-0',
18
+ mediaTypes: {
19
+ banner: {
20
+ sizes: [[300, 250], [300,600]],
21
+ }
22
+ },
23
+ // Replace this object to test a new Adapter!
24
+ bids: [{
25
+ bidder: 'deltaprojects',
26
+ params: {
27
+ publisherId: '4' //required
28
+ }
29
+ }]
30
+ };
31
+ ```
32
+
@@ -29,6 +29,7 @@ let hasSynced = false;
29
29
 
30
30
  export const spec = {
31
31
  code: BIDDER_CODE,
32
+ aliases: ['playwire'],
32
33
  supportedMediaTypes: [ BANNER, VIDEO ],
33
34
  /**
34
35
  * Determines whether or not the given bid request is valid.
@@ -0,0 +1,79 @@
1
+ import { logError } from '../src/utils.js';
2
+ import { registerBidder } from '../src/adapters/bidderFactory.js';
3
+
4
+ const BIDDER_CODE = 'iprom';
5
+ const ENDPOINT_URL = 'https://core.iprom.net/programmatic';
6
+ const VERSION = 'v1.0.2';
7
+ const DEFAULT_CURRENCY = 'EUR';
8
+ const DEFAULT_NETREVENUE = true;
9
+ const DEFAULT_TTL = 360;
10
+
11
+ export const spec = {
12
+ code: BIDDER_CODE,
13
+ isBidRequestValid: function ({ bidder, params = {} } = {}) {
14
+ // id parameter checks
15
+ if (!params.id) {
16
+ logError(`${bidder}: Parameter 'id' missing`);
17
+ return false;
18
+ } else if (typeof params.id !== 'string') {
19
+ logError(`${bidder}: Parameter 'id' needs to be a string`);
20
+ return false;
21
+ }
22
+ // dimension parameter checks
23
+ if (!params.dimension) {
24
+ logError(`${bidder}: Required parameter 'dimension' missing`);
25
+ return false;
26
+ } else if (typeof params.dimension !== 'string') {
27
+ logError(`${bidder}: Parameter 'dimension' needs to be a string`);
28
+ return false;
29
+ }
30
+
31
+ return true;
32
+ },
33
+
34
+ buildRequests: function (validBidRequests, bidderRequest) {
35
+ const payload = {
36
+ bids: validBidRequests,
37
+ referer: bidderRequest.refererInfo,
38
+ version: VERSION
39
+ };
40
+ const payloadString = JSON.stringify(payload);
41
+
42
+ return {
43
+ method: 'POST',
44
+ url: ENDPOINT_URL,
45
+ data: payloadString
46
+ };
47
+ },
48
+
49
+ interpretResponse: function (serverResponse, request) {
50
+ let bids = serverResponse.body;
51
+
52
+ const bidResponses = [];
53
+
54
+ bids.forEach(bid => {
55
+ const b = {
56
+ ad: bid.ad,
57
+ requestId: bid.requestId,
58
+ cpm: bid.cpm,
59
+ width: bid.width,
60
+ height: bid.height,
61
+ creativeId: bid.creativeId,
62
+ currency: bid.currency || DEFAULT_CURRENCY,
63
+ netRevenue: bid.netRevenue || DEFAULT_NETREVENUE,
64
+ ttl: bid.ttl || DEFAULT_TTL,
65
+ meta: {},
66
+ };
67
+
68
+ if (bid.aDomains && bid.aDomains.length) {
69
+ b.meta.advertiserDomains = bid.aDomains;
70
+ }
71
+
72
+ bidResponses.push(b);
73
+ });
74
+
75
+ return bidResponses;
76
+ },
77
+ }
78
+
79
+ registerBidder(spec);
@@ -646,7 +646,7 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) {
646
646
  }
647
647
 
648
648
  if (impressionObjects.length && BANNER in impressionObjects[0]) {
649
- const { id, banner: { topframe } } = impressionObjects[0];
649
+ const { id, banner: { topframe }, ext } = impressionObjects[0];
650
650
  const _bannerImpression = {
651
651
  id,
652
652
  banner: {
@@ -655,6 +655,12 @@ function buildRequest(validBidRequests, bidderRequest, impressions, version) {
655
655
  },
656
656
  }
657
657
 
658
+ if (ext.dfp_ad_unit_code) {
659
+ _bannerImpression.ext = {
660
+ dfp_ad_unit_code: ext.dfp_ad_unit_code
661
+ }
662
+ }
663
+
658
664
  if ('bidfloor' in impressionObjects[0]) {
659
665
  _bannerImpression.bidfloor = impressionObjects[0].bidfloor;
660
666
  }
@@ -10,7 +10,7 @@ export const storage = getStorageManager();
10
10
 
11
11
  const BIDDER_CODE = 'jixie';
12
12
  const EVENTS_URL = 'https://hbtra.jixie.io/sync/hb?';
13
- const JX_OUTSTREAM_RENDERER_URL = 'https://scripts.jixie.io/jxhboutstream.js';
13
+ const JX_OUTSTREAM_RENDERER_URL = 'https://scripts.jixie.media/jxhbrenderer.1.1.min.js';
14
14
  const REQUESTS_URL = 'https://hb.jixie.io/v2/hbpost';
15
15
  const sidTTLMins_ = 30;
16
16
 
@@ -104,7 +104,8 @@ function getMiscDims_() {
104
104
  let ret = {
105
105
  pageurl: '',
106
106
  domain: '',
107
- device: 'unknown'
107
+ device: 'unknown',
108
+ mkeywords: ''
108
109
  }
109
110
  try {
110
111
  let refererInfo_ = getRefererInfo();
@@ -112,6 +113,10 @@ function getMiscDims_() {
112
113
  ret.pageurl = url_;
113
114
  ret.domain = parseUrl(url_).host;
114
115
  ret.device = getDevice_();
116
+ let keywords = document.getElementsByTagName('meta')['keywords'];
117
+ if (keywords && keywords.content) {
118
+ ret.mkeywords = keywords.content;
119
+ }
115
120
  } catch (error) {}
116
121
  return ret;
117
122
  }
@@ -167,6 +172,7 @@ export const spec = {
167
172
  device: miscDims.device,
168
173
  domain: miscDims.domain,
169
174
  pageurl: miscDims.pageurl,
175
+ mkeywords: miscDims.mkeywords,
170
176
  bids: bids,
171
177
  cfg: jixieCfgBlob
172
178
  }, ids);
@@ -4,7 +4,7 @@ import { deepAccess } from '../src/utils.js';
4
4
  const BIDDER_CODE = 'justpremium'
5
5
  const GVLID = 62
6
6
  const ENDPOINT_URL = 'https://pre.ads.justpremium.com/v/2.0/t/xhr'
7
- const JP_ADAPTER_VERSION = '1.8'
7
+ const JP_ADAPTER_VERSION = '1.8.1'
8
8
  const pixels = []
9
9
 
10
10
  export const spec = {
@@ -101,6 +101,11 @@ export const spec = {
101
101
  advertiserDomains: bid.adomain && bid.adomain.length > 0 ? bid.adomain : []
102
102
  }
103
103
  }
104
+ if (bid.ext && bid.ext.pg) {
105
+ bidResponse.adserverTargeting = {
106
+ 'hb_deal_justpremium': 'jp_pg'
107
+ }
108
+ }
104
109
  bidResponses.push(bidResponse)
105
110
  }
106
111
  })
@@ -180,7 +180,7 @@ function kinessoSyncUrl(accountId, consentData) {
180
180
  const usPrivacyString = uspDataHandler.getConsentData();
181
181
  let kinessoSyncUrl = `${ID_SVC}?accountid=${accountId}`;
182
182
  if (usPrivacyString) {
183
- kinessoSyncUrl = `${kinessoSyncUrl}?us_privacy=${usPrivacyString}`;
183
+ kinessoSyncUrl = `${kinessoSyncUrl}&us_privacy=${usPrivacyString}`;
184
184
  }
185
185
  if (!consentData || typeof consentData.gdprApplies !== 'boolean' || !consentData.gdprApplies) return kinessoSyncUrl;
186
186
 
@@ -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';
@@ -14,6 +15,7 @@ const TIMEOUTSENT = 8;
14
15
  const ADRENDERFAILEDSENT = 16;
15
16
 
16
17
  let initOptions;
18
+ let prebidGlobal = getGlobal();
17
19
  export const BID_WON_TIMEOUT = 500;
18
20
 
19
21
  const cache = {
@@ -79,6 +81,7 @@ let livewrappedAnalyticsAdapter = Object.assign(adapter({EMPTYURL, ANALYTICSTYPE
79
81
  bidResponse.width = args.width;
80
82
  bidResponse.height = args.height;
81
83
  bidResponse.cpm = args.cpm;
84
+ bidResponse.originalCpm = prebidGlobal.convertCurrency(args.originalCpm, args.originalCurrency, args.currency);
82
85
  bidResponse.ttr = args.timeToRespond;
83
86
  bidResponse.readyToSend = 1;
84
87
  bidResponse.mediaType = args.mediaType == 'native' ? 2 : (args.mediaType == 'video' ? 4 : 1);
@@ -237,6 +240,7 @@ function getResponses(gdpr, auctionIds) {
237
240
  width: bid.width,
238
241
  height: bid.height,
239
242
  cpm: bid.cpm,
243
+ orgCpm: bid.originalCpm,
240
244
  ttr: bid.ttr,
241
245
  IsBid: bid.isBid,
242
246
  mediaType: bid.mediaType,
@@ -276,6 +280,7 @@ function getWins(gdpr, auctionIds) {
276
280
  width: bid.width,
277
281
  height: bid.height,
278
282
  cpm: bid.cpm,
283
+ orgCpm: bid.originalCpm,
279
284
  mediaType: bid.mediaType,
280
285
  gdpr: gdprPos,
281
286
  floor: bid.lwFloor ? bid.lwFloor : (bid.floorData ? bid.floorData.floorValue : undefined),
@@ -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) {
@@ -322,7 +322,7 @@ function buildFpdQueryParams(fpdPath) {
322
322
  return acc;
323
323
  }, {})
324
324
  return Object.keys(fpd)
325
- .map((name, _) => name + ':' + [...new Set(fpd[name])].join('|'))
325
+ .map((name, _) => name + ':' + fpd[name].join('|'))
326
326
  .join(',')
327
327
  }
328
328
 
@@ -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