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,370 @@
1
+ import {registerBidder} from '../src/adapters/bidderFactory.js';
2
+ import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
3
+ import {convertCamelToUnderscore, isStr, isArray, isNumber, isPlainObject, replaceAuctionPrice} from '../src/utils.js';
4
+ import find from 'core-js-pure/features/array/find.js';
5
+ import includes from 'core-js-pure/features/array/includes.js';
6
+
7
+ const BID_METHOD = 'POST';
8
+ const BIDDER_URL = 'http://13.234.201.146:8088/va/ad';
9
+ const FIRST_PRICE = 1;
10
+ const NET_REVENUE = true;
11
+ const TTL = 10;
12
+ const USER_PARAMS = ['age', 'externalUid', 'segments', 'gender', 'dnt', 'language'];
13
+ const DEVICE_PARAMS = ['ua', 'geo', 'dnt', 'lmt', 'ip', 'ipv6', 'devicetype'];
14
+ const APP_DEVICE_PARAMS = ['geo', 'device_id']; // appid is collected separately
15
+ const DOMAIN_REGEX = new RegExp('//([^/]*)');
16
+
17
+ function groupBy(values, key) {
18
+ const groups = values.reduce((acc, value) => {
19
+ const groupId = value[key];
20
+
21
+ if (!acc[groupId]) acc[groupId] = [];
22
+ acc[groupId].push(value);
23
+
24
+ return acc;
25
+ }, {});
26
+
27
+ return Object
28
+ .keys(groups)
29
+ .map(id => ({id, key, values: groups[id]}));
30
+ }
31
+
32
+ function validateMediaTypes(mediaTypes, allowedMediaTypes) {
33
+ if (!isPlainObject(mediaTypes)) return false;
34
+ if (!allowedMediaTypes.some(mediaType => mediaType in mediaTypes)) return false;
35
+
36
+ if (isBanner(mediaTypes)) {
37
+ if (!validateBanner(mediaTypes.banner)) return false;
38
+ }
39
+ return true;
40
+ }
41
+
42
+ function isBanner(mediaTypes) {
43
+ return isPlainObject(mediaTypes) && isPlainObject(mediaTypes.banner);
44
+ }
45
+
46
+ function validateBanner(banner) {
47
+ return isPlainObject(banner) &&
48
+ isArray(banner.sizes) &&
49
+ (banner.sizes.length > 0) &&
50
+ banner.sizes.every(validateMediaSizes);
51
+ }
52
+
53
+ function validateMediaSizes(mediaSize) {
54
+ return isArray(mediaSize) &&
55
+ (mediaSize.length === 2) &&
56
+ mediaSize.every(size => (isNumber(size) && size >= 0));
57
+ }
58
+
59
+ function hasUserInfo(bid) {
60
+ return !!bid.params.user;
61
+ }
62
+
63
+ function validateParameters(parameters, adUnit) {
64
+ if (!(parameters.placementId)) {
65
+ return false;
66
+ }
67
+ if (!(parameters.publisherId)) {
68
+ return false;
69
+ }
70
+
71
+ return true;
72
+ }
73
+
74
+ function extractSiteDomainFromURL(url) {
75
+ if (!url || !isStr(url)) return null;
76
+
77
+ const domain = url.match(DOMAIN_REGEX);
78
+
79
+ if (isArray(domain) && domain.length === 2) return domain[1];
80
+
81
+ return null;
82
+ }
83
+
84
+ function generateSiteFromAdUnitContext(bidRequests, adUnitContext) {
85
+ if (!adUnitContext || !adUnitContext.refererInfo) return null;
86
+
87
+ const domain = extractSiteDomainFromURL(adUnitContext.refererInfo.referer);
88
+ const publisherId = bidRequests[0].params.publisherId;
89
+
90
+ if (!domain) return null;
91
+
92
+ return {
93
+ page: adUnitContext.refererInfo.referer,
94
+ domain: domain,
95
+ name: domain,
96
+ publisher: {
97
+ id: publisherId
98
+ }
99
+ };
100
+ }
101
+
102
+ function validateServerRequest(serverRequest) {
103
+ return isPlainObject(serverRequest) &&
104
+ isPlainObject(serverRequest.data) &&
105
+ isArray(serverRequest.data.imp)
106
+ }
107
+
108
+ function createServerRequestFromAdUnits(adUnits, bidRequestId, adUnitContext) {
109
+ return {
110
+ method: BID_METHOD,
111
+ url: BIDDER_URL,
112
+ data: generateBidRequestsFromAdUnits(adUnits, bidRequestId, adUnitContext),
113
+ options: {
114
+ contentType: 'application/json',
115
+ withCredentials: false,
116
+ }
117
+ }
118
+ }
119
+
120
+ function generateBidRequestsFromAdUnits(bidRequests, bidRequestId, adUnitContext) {
121
+ const userObjBid = find(bidRequests, hasUserInfo);
122
+ let userObj = {};
123
+ if (userObjBid) {
124
+ Object.keys(userObjBid.params.user)
125
+ .filter(param => includes(USER_PARAMS, param))
126
+ .forEach((param) => {
127
+ let uparam = convertCamelToUnderscore(param);
128
+ if (param === 'segments' && isArray(userObjBid.params.user[param])) {
129
+ let segs = [];
130
+ userObjBid.params.user[param].forEach(val => {
131
+ if (isNumber(val)) {
132
+ segs.push({'id': val});
133
+ } else if (isPlainObject(val)) {
134
+ segs.push(val);
135
+ }
136
+ });
137
+ userObj[uparam] = segs;
138
+ } else if (param !== 'segments') {
139
+ userObj[uparam] = userObjBid.params.user[param];
140
+ }
141
+ });
142
+ }
143
+
144
+ const deviceObjBid = find(bidRequests, hasDeviceInfo);
145
+ let deviceObj;
146
+ if (deviceObjBid && deviceObjBid.params && deviceObjBid.params.device) {
147
+ deviceObj = {};
148
+ Object.keys(deviceObjBid.params.device)
149
+ .filter(param => includes(DEVICE_PARAMS, param))
150
+ .forEach(param => deviceObj[param] = deviceObjBid.params.device[param]);
151
+ if (!deviceObjBid.hasOwnProperty('ua')) {
152
+ deviceObj.ua = navigator.userAgent;
153
+ }
154
+ if (!deviceObjBid.hasOwnProperty('language')) {
155
+ deviceObj.language = navigator.language;
156
+ }
157
+ } else {
158
+ deviceObj = {};
159
+ deviceObj.ua = navigator.userAgent;
160
+ deviceObj.language = navigator.language;
161
+ }
162
+ const appDeviceObjBid = find(bidRequests, hasAppInfo);
163
+ let appIdObj;
164
+ if (appDeviceObjBid && appDeviceObjBid.params && appDeviceObjBid.params.app && appDeviceObjBid.params.app.id) {
165
+ Object.keys(appDeviceObjBid.params.app)
166
+ .filter(param => includes(APP_DEVICE_PARAMS, param))
167
+ .forEach(param => appDeviceObjBid[param] = appDeviceObjBid.params.app[param]);
168
+ }
169
+
170
+ const payload = {}
171
+ payload.id = bidRequestId
172
+ payload.at = FIRST_PRICE
173
+ payload.cur = ['USD']
174
+ payload.imp = bidRequests.reduce(generateImpressionsFromAdUnit, [])
175
+ payload.site = generateSiteFromAdUnitContext(bidRequests, adUnitContext)
176
+ payload.device = deviceObj
177
+ if (appDeviceObjBid && payload.site != null) {
178
+ payload.app = appIdObj;
179
+ }
180
+ payload.user = userObj
181
+ // payload.regs = getRegulationFromAdUnitContext(adUnitContext)
182
+ // payload.ext = generateBidRequestExtension()
183
+
184
+ return payload
185
+ }
186
+
187
+ function generateImpressionsFromAdUnit(acc, adUnit) {
188
+ const {bidId, mediaTypes, params} = adUnit;
189
+ const {placementId} = params;
190
+ const pmp = {};
191
+
192
+ if (placementId) pmp.deals = [{id: placementId}]
193
+
194
+ const imps = Object
195
+ .keys(mediaTypes)
196
+ .reduce((acc, mediaType) => {
197
+ const data = mediaTypes[mediaType];
198
+ const impId = `${bidId}`;
199
+
200
+ if (mediaType === 'banner') return acc.concat(generateBannerFromAdUnit(impId, data, params));
201
+ }, []);
202
+
203
+ return acc.concat(imps);
204
+ }
205
+
206
+ function generateBannerFromAdUnit(impId, data, params) {
207
+ const {position, placementId} = params;
208
+ const pos = position || 0;
209
+ const pmp = {};
210
+ const ext = {placementId};
211
+
212
+ if (placementId) pmp.deals = [{id: placementId}]
213
+
214
+ return data.sizes.map(([w, h]) => ({id: `${impId}`, banner: {format: [{w, h}], w, h, pos}, pmp, ext, tagid: placementId}));
215
+ }
216
+
217
+ function validateServerResponse(serverResponse) {
218
+ return isPlainObject(serverResponse) &&
219
+ isPlainObject(serverResponse.body) &&
220
+ isStr(serverResponse.body.cur) &&
221
+ isArray(serverResponse.body.seatbid);
222
+ }
223
+
224
+ function seatBidsToAds(seatBid, bidResponse, serverRequest) {
225
+ return seatBid.bid
226
+ .filter(bid => validateBids(bid))
227
+ .map(bid => generateAdFromBid(bid, bidResponse));
228
+ }
229
+
230
+ function validateBids(bid) {
231
+ if (!isPlainObject(bid)) return false;
232
+ if (!isStr(bid.impid)) return false;
233
+ if (!isStr(bid.crid)) return false;
234
+ if (!isNumber(bid.price)) return false;
235
+ if (!isNumber(bid.w)) return false;
236
+ if (!isNumber(bid.h)) return false;
237
+ if (!bid.adm) return false;
238
+ if (bid.adm) {
239
+ if (!isStr(bid.adm)) return false;
240
+ }
241
+ return true;
242
+ }
243
+
244
+ const VAST_REGEXP = /VAST\s+version/;
245
+
246
+ function getMediaType(adm) {
247
+ const videoRegex = new RegExp(VAST_REGEXP);
248
+
249
+ if (videoRegex.test(adm)) {
250
+ return VIDEO;
251
+ }
252
+
253
+ const markup = safeJSONparse(adm.replace(/\\/g, ''));
254
+
255
+ if (markup && isPlainObject(markup.native)) {
256
+ return NATIVE;
257
+ }
258
+
259
+ return BANNER;
260
+ }
261
+
262
+ function safeJSONparse(...args) {
263
+ try {
264
+ return JSON.parse(...args);
265
+ } catch (_) {
266
+ return undefined;
267
+ }
268
+ }
269
+
270
+ function generateAdFromBid(bid, bidResponse) {
271
+ const mediaType = getMediaType(bid.adm);
272
+ const base = {
273
+ requestId: bid.impid,
274
+ cpm: bid.price,
275
+ currency: bidResponse.cur,
276
+ ttl: TTL,
277
+ creativeId: bid.crid,
278
+ mediaType: mediaType,
279
+ netRevenue: NET_REVENUE
280
+ };
281
+
282
+ if (bid.adomain) {
283
+ base.meta = { advertiserDomains: bid.adomain };
284
+ }
285
+
286
+ const size = getSizeFromBid(bid);
287
+ const creative = getCreativeFromBid(bid);
288
+
289
+ return {
290
+ ...base,
291
+ height: size.height,
292
+ width: size.width,
293
+ ad: creative.markup,
294
+ adUrl: creative.markupUrl,
295
+ // vastXml: isVideo && !isStr(creative.markupUrl) ? creative.markup : null,
296
+ // vastUrl: isVideo && isStr(creative.markupUrl) ? creative.markupUrl : null,
297
+ renderer: creative.renderer
298
+ };
299
+ }
300
+
301
+ function getSizeFromBid(bid) {
302
+ if (isNumber(bid.w) && isNumber(bid.h)) {
303
+ return { width: bid.w, height: bid.h };
304
+ }
305
+ return { width: null, height: null };
306
+ }
307
+
308
+ function getCreativeFromBid(bid) {
309
+ const shouldUseAdMarkup = !!bid.adm;
310
+ const price = bid.price;
311
+ return {
312
+ markup: shouldUseAdMarkup ? replaceAuctionPrice(bid.adm, price) : null,
313
+ markupUrl: !shouldUseAdMarkup ? replaceAuctionPrice(bid.nurl, price) : null
314
+ };
315
+ }
316
+
317
+ function hasDeviceInfo(bid) {
318
+ if (bid.params) {
319
+ return !!bid.params.device
320
+ }
321
+ }
322
+
323
+ function hasAppInfo(bid) {
324
+ if (bid.params) {
325
+ return !!bid.params.app
326
+ }
327
+ }
328
+
329
+ const venavenBidderSpec = {
330
+ code: 'ventes',
331
+ supportedMediaTypes: [BANNER],
332
+ isBidRequestValid(adUnit) {
333
+ const allowedBidderCodes = [this.code];
334
+
335
+ return isPlainObject(adUnit) &&
336
+ allowedBidderCodes.indexOf(adUnit.bidder) !== -1 &&
337
+ isStr(adUnit.adUnitCode) &&
338
+ isStr(adUnit.bidderRequestId) &&
339
+ isStr(adUnit.bidId) &&
340
+ validateMediaTypes(adUnit.mediaTypes, this.supportedMediaTypes) &&
341
+ validateParameters(adUnit.params, adUnit);
342
+ },
343
+ buildRequests(bidRequests, bidderRequest) {
344
+ if (!bidRequests) return null;
345
+
346
+ return groupBy(bidRequests, 'bidderRequestId').map(group => {
347
+ const bidRequestId = group.id;
348
+ const adUnits = groupBy(group.values, 'bidId').map((group) => {
349
+ const length = group.values.length;
350
+ return length > 0 && group.values[length - 1]
351
+ });
352
+
353
+ return createServerRequestFromAdUnits(adUnits, bidRequestId, bidderRequest)
354
+ });
355
+ },
356
+ interpretResponse(serverResponse, serverRequest) {
357
+ if (!validateServerRequest(serverRequest)) return [];
358
+ if (!validateServerResponse(serverResponse)) return [];
359
+
360
+ const bidResponse = serverResponse.body;
361
+
362
+ return bidResponse.seatbid
363
+ .filter(seatBid => isPlainObject(seatBid) && isArray(seatBid.bid))
364
+ .reduce((acc, seatBid) => acc.concat(seatBidsToAds(seatBid, bidResponse, serverRequest)), []);
365
+ }
366
+ };
367
+
368
+ registerBidder(venavenBidderSpec);
369
+
370
+ export {venavenBidderSpec as spec};
@@ -0,0 +1,94 @@
1
+ ---
2
+ layout: bidder
3
+ title: ventes
4
+ description: Prebid ventes Bidder Adapter
5
+ pbjs: false
6
+ biddercode: ventes
7
+ gdpr_supported: false
8
+ usp_supported: false
9
+ media_types: banner
10
+ coppa_supported: false
11
+ schain_supported: false
12
+ dchain_supported: false
13
+ prebid_member: false
14
+ ---
15
+
16
+ ### BidParams
17
+ {: .table .table-bordered .table-striped }
18
+ | Name | Scope | Description | Example | Type |
19
+ |-----------------|----------|-----------------------------------------------------------|----------------------------------------------|---------------|
20
+ | `placementId` | required | Placement ID from Ventes Avenues | `'VA-062-0013-0183'` | `string` |
21
+ | `publisherId` | required | Publisher ID from Ventes Avenues | `'VA-062'` | `string` |
22
+ | `user` | optional | Object that specifies information about an external user. | `user: { age: 25, gender: 0, dnt: true}` | `object` |
23
+ | `app` | required | Object containing mobile app parameters. | `app : { id: 'app-id'}` | `object` |
24
+ | `device` | required | Object containing device info mandatory for mobile devices| `device : { ifa: 'device-id'}` | `object` |
25
+
26
+ #### User Object
27
+
28
+ {: .table .table-bordered .table-striped }
29
+ | Name | Description | Example | Type |
30
+ |-------------------|-------------------------------------------------------------------------------------------|-----------------------|-----------------------|
31
+ | `age` | The age of the user. | `35` | `integer` |
32
+ | `externalUid` | Specifies a string that corresponds to an external user ID for this user. | `'1234567890abcdefg'` | `string` |
33
+ | `segments` | Specifies the segments to which the user belongs. | `[1, 2]` | `Array<integer>` |
34
+ | `gender` | Specifies the gender of the user. Allowed values: Unknown: `0`; Male: `1`; Female: `2` | `1` | `integer` |
35
+ | `dnt` | Do not track flag. Indicates if tracking cookies should be disabled for this auction | `true` | `boolean` |
36
+ | `language` | Two-letter ANSI code for this user's language. | `EN` | `string` |
37
+
38
+
39
+ ### Ad Unit Setup for Banner through mobile devices
40
+ ```javascript
41
+ var adUnits = [
42
+ {
43
+ code: 'test-hb-ad-11111-1',
44
+ mediaTypes: {
45
+ banner: {
46
+ sizes: [
47
+ [300, 250]
48
+ ]
49
+ }
50
+ },
51
+ bids: [{
52
+ bidder: 'ventes',
53
+ params: {
54
+ placementId: 'VA-062-0013-0183',
55
+ publisherId: '5cebea3c9eea646c7b623d5e',
56
+ IABCategories: "['IAB1', 'IAB5']",
57
+ device:{
58
+ ip: '123.145.167.189',
59
+ ifa:"AEBE52E7-03EE-455A-B3C4-E57283966239",
60
+ },
61
+ app: {
62
+ id: "agltb3B1Yi1pbmNyDAsSA0FwcBiJkfIUDA",
63
+ name: "Yahoo Weather",
64
+ bundle: 'com.kiloo.subwaysurf',
65
+ storeurl: 'https://play.google.com/store/apps/details?id=com.kiloo.subwaysurf&hl=en',
66
+ domain: 'somoaudience.com',
67
+ }
68
+ }
69
+ }]
70
+ }
71
+ ]
72
+ ```
73
+
74
+ ### Ad Unit Setup for Banner through Websites
75
+ ```javascript
76
+ var adUnits = [
77
+ {
78
+ code: 'test-hb-ad-11111-1',
79
+ mediaTypes: {
80
+ banner: {
81
+ sizes: [
82
+ [300, 250]
83
+ ]
84
+ }
85
+ },
86
+ bids: [{
87
+ bidder: 'ventes',
88
+ params: {
89
+ placementId: 'VA-002-0007-0799',
90
+ publisherId: '5cebea3c9eea646c7b623d5e',
91
+ }
92
+ }]
93
+ }
94
+ ]
@@ -144,9 +144,9 @@ function getAdapterMode() {
144
144
 
145
145
  function getResponseFormat(bid) {
146
146
  const adm = bid.adm;
147
- if (adm.includes('o2playerSettings') || adm.includes('YAHOO.VideoPlatform.VideoPlayer') || adm.includes('AdPlacement')) {
147
+ if (adm.indexOf('o2playerSettings') !== -1 || adm.indexOf('YAHOO.VideoPlatform.VideoPlayer') !== -1 || adm.indexOf('AdPlacement') !== -1) {
148
148
  return BANNER;
149
- } else if (adm.includes('VAST')) {
149
+ } else if (adm.indexOf('VAST') !== -1) {
150
150
  return VIDEO;
151
151
  }
152
152
  };
@@ -188,23 +188,23 @@ function validateAppendObject(validationType, allowedKeys, inputObject, appendTo
188
188
  for (const objectKey in inputObject) {
189
189
  switch (validationType) {
190
190
  case 'string':
191
- if (allowedKeys.includes(objectKey) && isStr(inputObject[objectKey])) {
191
+ if (allowedKeys.indexOf(objectKey) !== -1 && isStr(inputObject[objectKey])) {
192
192
  outputObject[objectKey] = inputObject[objectKey];
193
193
  };
194
194
  break;
195
195
  case 'number':
196
- if (allowedKeys.includes(objectKey) && isNumber(inputObject[objectKey])) {
196
+ if (allowedKeys.indexOf(objectKey) !== -1 && isNumber(inputObject[objectKey])) {
197
197
  outputObject[objectKey] = inputObject[objectKey];
198
198
  };
199
199
  break;
200
200
 
201
201
  case 'array':
202
- if (allowedKeys.includes(objectKey) && isArray(inputObject[objectKey])) {
202
+ if (allowedKeys.indexOf(objectKey) !== -1 && isArray(inputObject[objectKey])) {
203
203
  outputObject[objectKey] = inputObject[objectKey];
204
204
  };
205
205
  break;
206
206
  case 'object':
207
- if (allowedKeys.includes(objectKey) && isPlainObject(inputObject[objectKey])) {
207
+ if (allowedKeys.indexOf(objectKey) !== -1 && isPlainObject(inputObject[objectKey])) {
208
208
  outputObject[objectKey] = inputObject[objectKey];
209
209
  };
210
210
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prebid.js",
3
- "version": "5.19.0",
3
+ "version": "5.20.3",
4
4
  "description": "Header Bidding Management Library",
5
5
  "main": "src/prebid.js",
6
6
  "scripts": {
package/src/auction.js CHANGED
@@ -613,7 +613,8 @@ export const getPriceGranularity = (mediaType, bidReq) => {
613
613
  * @returns {function}
614
614
  */
615
615
  export const getPriceByGranularity = (granularity) => {
616
- return (bid) => {
616
+ return (bid, bidReq) => {
617
+ granularity = granularity || getPriceGranularity(bid.mediaType, bidReq);
617
618
  if (granularity === CONSTANTS.GRANULARITY_OPTIONS.AUTO) {
618
619
  return bid.pbAg;
619
620
  } else if (granularity === CONSTANTS.GRANULARITY_OPTIONS.DENSE) {
@@ -646,14 +647,14 @@ export const getAdvertiserDomain = () => {
646
647
  * @param {BidRequest} bidReq
647
648
  * @returns {*}
648
649
  */
649
- export function getStandardBidderSettings(mediaType, bidderCode, bidReq) {
650
+ export function getStandardBidderSettings(mediaType, bidderCode) {
650
651
  // factory for key value objs
651
652
  function createKeyVal(key, value) {
652
653
  return {
653
654
  key,
654
655
  val: (typeof value === 'function')
655
- ? function (bidResponse) {
656
- return value(bidResponse);
656
+ ? function (bidResponse, bidReq) {
657
+ return value(bidResponse, bidReq);
657
658
  }
658
659
  : function (bidResponse) {
659
660
  return getValue(bidResponse, value);
@@ -661,7 +662,6 @@ export function getStandardBidderSettings(mediaType, bidderCode, bidReq) {
661
662
  };
662
663
  }
663
664
  const TARGETING_KEYS = CONSTANTS.TARGETING_KEYS;
664
- const granularity = getPriceGranularity(mediaType, bidReq);
665
665
 
666
666
  let bidderSettings = $$PREBID_GLOBAL$$.bidderSettings;
667
667
  if (!bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD]) {
@@ -671,7 +671,7 @@ export function getStandardBidderSettings(mediaType, bidderCode, bidReq) {
671
671
  bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD][CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING] = [
672
672
  createKeyVal(TARGETING_KEYS.BIDDER, 'bidderCode'),
673
673
  createKeyVal(TARGETING_KEYS.AD_ID, 'adId'),
674
- createKeyVal(TARGETING_KEYS.PRICE_BUCKET, getPriceByGranularity(granularity)),
674
+ createKeyVal(TARGETING_KEYS.PRICE_BUCKET, getPriceByGranularity()),
675
675
  createKeyVal(TARGETING_KEYS.SIZE, 'size'),
676
676
  createKeyVal(TARGETING_KEYS.DEAL, 'dealId'),
677
677
  createKeyVal(TARGETING_KEYS.SOURCE, 'source'),
@@ -716,12 +716,12 @@ export function getKeyValueTargetingPairs(bidderCode, custBidObj, bidReq) {
716
716
  // 1) set the keys from "standard" setting or from prebid defaults
717
717
  if (bidderSettings) {
718
718
  // initialize default if not set
719
- const standardSettings = getStandardBidderSettings(custBidObj.mediaType, bidderCode, bidReq);
720
- setKeys(keyValues, standardSettings, custBidObj);
719
+ const standardSettings = getStandardBidderSettings(custBidObj.mediaType, bidderCode);
720
+ setKeys(keyValues, standardSettings, custBidObj, bidReq);
721
721
 
722
722
  // 2) set keys from specific bidder setting override if they exist
723
723
  if (bidderCode && bidderSettings[bidderCode] && bidderSettings[bidderCode][CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING]) {
724
- setKeys(keyValues, bidderSettings[bidderCode], custBidObj);
724
+ setKeys(keyValues, bidderSettings[bidderCode], custBidObj, bidReq);
725
725
  custBidObj.sendStandardTargeting = bidderSettings[bidderCode].sendStandardTargeting;
726
726
  }
727
727
  }
@@ -734,7 +734,7 @@ export function getKeyValueTargetingPairs(bidderCode, custBidObj, bidReq) {
734
734
  return keyValues;
735
735
  }
736
736
 
737
- function setKeys(keyValues, bidderSettings, custBidObj) {
737
+ function setKeys(keyValues, bidderSettings, custBidObj, bidReq) {
738
738
  var targeting = bidderSettings[CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING];
739
739
  custBidObj.size = custBidObj.getSize();
740
740
 
@@ -748,7 +748,7 @@ function setKeys(keyValues, bidderSettings, custBidObj) {
748
748
 
749
749
  if (isFn(value)) {
750
750
  try {
751
- value = value(custBidObj);
751
+ value = value(custBidObj, bidReq);
752
752
  } catch (e) {
753
753
  logError('bidmanager', 'ERROR', e);
754
754
  }
package/src/prebid.js CHANGED
@@ -403,10 +403,23 @@ function emitAdRenderSucceeded({ doc, bid, id }) {
403
403
  events.emit(AD_RENDER_SUCCEEDED, data);
404
404
  }
405
405
 
406
+ /**
407
+ * This function will check for presence of given node in given parent. If not present - will inject it.
408
+ * @param {Node} node node, whose existance is in question
409
+ * @param {Document} doc document element do look in
410
+ * @param {string} tagName tag name to look in
411
+ */
412
+ function reinjectNodeIfRemoved(node, doc, tagName) {
413
+ const injectionNode = doc.querySelector(tagName);
414
+ if (!node.parentNode || node.parentNode !== injectionNode) {
415
+ insertElement(node, doc, tagName);
416
+ }
417
+ }
418
+
406
419
  /**
407
420
  * This function will render the ad (based on params) in the given iframe document passed through.
408
421
  * Note that doc SHOULD NOT be the parent document page as we can't doc.write() asynchronously
409
- * @param {HTMLDocument} doc document
422
+ * @param {Document} doc document
410
423
  * @param {string} id bid id to locate the ad
411
424
  * @alias module:pbjs.renderAd
412
425
  */
@@ -450,10 +463,13 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) {
450
463
  const {height, width, ad, mediaType, adUrl, renderer} = bid;
451
464
 
452
465
  const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`);
466
+ // It is important that the comment with metadata is injected before the ad is actually rendered,
467
+ // so the creativeId can be used by e.g. ad quality scanners to check against blocking rules
468
+ insertElement(creativeComment, doc, 'html');
453
469
 
454
470
  if (isRendererRequired(renderer)) {
455
471
  executeRenderer(renderer, bid);
456
- insertElement(creativeComment, doc, 'html');
472
+ reinjectNodeIfRemoved(creativeComment, doc, 'html');
457
473
  emitAdRenderSucceeded({ doc, bid, id });
458
474
  } else if ((doc === document && !inIframe()) || mediaType === 'video') {
459
475
  const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`;
@@ -472,7 +488,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) {
472
488
  doc.write(ad);
473
489
  doc.close();
474
490
  setRenderSize(doc, width, height);
475
- insertElement(creativeComment, doc, 'html');
491
+ reinjectNodeIfRemoved(creativeComment, doc, 'html');
476
492
  callBurl(bid);
477
493
  emitAdRenderSucceeded({ doc, bid, id });
478
494
  } else if (adUrl) {
@@ -485,7 +501,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) {
485
501
 
486
502
  insertElement(iframe, doc, 'body');
487
503
  setRenderSize(doc, width, height);
488
- insertElement(creativeComment, doc, 'html');
504
+ reinjectNodeIfRemoved(creativeComment, doc, 'html');
489
505
  callBurl(bid);
490
506
  emitAdRenderSucceeded({ doc, bid, id });
491
507
  } else {
package/src/targeting.js CHANGED
@@ -416,7 +416,15 @@ export function newTargeting(auctionManager) {
416
416
  let bidsReceived = auctionManager.getBidsReceived();
417
417
 
418
418
  if (!config.getConfig('useBidCache')) {
419
+ // don't use bid cache (i.e. filter out bids not in the latest auction)
419
420
  bidsReceived = bidsReceived.filter(bid => latestAuctionForAdUnit[bid.adUnitCode] === bid.auctionId)
421
+ } else {
422
+ // if custom bid cache filter function exists, run for each bid from
423
+ // previous auctions. If it returns true, include bid in bid pool
424
+ const filterFunction = config.getConfig('bidCacheFilterFunction');
425
+ if (typeof filterFunction === 'function') {
426
+ bidsReceived = bidsReceived.filter(bid => latestAuctionForAdUnit[bid.adUnitCode] === bid.auctionId || !!filterFunction(bid))
427
+ }
420
428
  }
421
429
 
422
430
  bidsReceived = bidsReceived
@@ -1231,7 +1231,7 @@ export function getCurrencyRates() {
1231
1231
  };
1232
1232
  }
1233
1233
 
1234
- export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, adUnitCode, adId, status, ttl, requestId}) {
1234
+ export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, adUnitCode, adId, status, ttl, requestId, mediaType}) {
1235
1235
  let bid = {
1236
1236
  'bidderCode': bidder,
1237
1237
  'width': '300',
@@ -1259,6 +1259,7 @@ export function createBidReceived({bidder, cpm, auctionId, responseTimestamp, ad
1259
1259
  'hb_pb': cpm,
1260
1260
  'foobar': '300x250'
1261
1261
  }),
1262
+ 'mediaType': mediaType,
1262
1263
  'netRevenue': true,
1263
1264
  'currency': 'USD',
1264
1265
  'ttl': (!ttl) ? 300 : ttl