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
@@ -0,0 +1,106 @@
1
+ import { deepAccess } from '../src/utils.js';
2
+ import { registerBidder } from '../src/adapters/bidderFactory.js';
3
+
4
+ const BIDDER_CODE = 'welect';
5
+ const DEFAULT_DOMAIN = 'www.welect.de';
6
+
7
+ export const spec = {
8
+ code: BIDDER_CODE,
9
+ aliases: ['wlt'],
10
+ gvlid: 282,
11
+ supportedMediaTypes: ['video'],
12
+
13
+ // short code
14
+ /**
15
+ * Determines whether or not the given bid request is valid.
16
+ *
17
+ * @param {BidRequest} bid The bid params to validate.
18
+ * @return boolean True if this is a valid bid, and false otherwise.
19
+ */
20
+ isBidRequestValid: function (bid) {
21
+ return (
22
+ deepAccess(bid, 'mediaTypes.video.context') === 'instream' &&
23
+ !!bid.params.placementId
24
+ );
25
+ },
26
+ /**
27
+ * Make a server request from the list of BidRequests.
28
+ *
29
+ * @param {validBidRequests[]} - an array of bids
30
+ * @return ServerRequest Info describing the request to the server.
31
+ */
32
+ buildRequests: function (validBidRequests) {
33
+ return validBidRequests.map((bidRequest) => {
34
+ let rawSizes =
35
+ deepAccess(bidRequest, 'mediaTypes.video.playerSize') ||
36
+ bidRequest.sizes;
37
+ let size = rawSizes[0];
38
+
39
+ let domain = bidRequest.params.domain || DEFAULT_DOMAIN;
40
+
41
+ let url = `https://${domain}/api/v2/preflight/${bidRequest.params.placementId}`;
42
+
43
+ let gdprConsent = null;
44
+
45
+ if (bidRequest && bidRequest.gdprConsent) {
46
+ gdprConsent = {
47
+ gdpr_consent: {
48
+ gdprApplies: bidRequest.gdprConsent.gdprApplies,
49
+ tcString: bidRequest.gdprConsent.gdprConsent,
50
+ },
51
+ };
52
+ }
53
+
54
+ const data = {
55
+ width: size[0],
56
+ height: size[1],
57
+ bid_id: bidRequest.bidId,
58
+ ...gdprConsent,
59
+ };
60
+
61
+ return {
62
+ method: 'POST',
63
+ url: url,
64
+ data: data,
65
+ options: {
66
+ contentType: 'application/json',
67
+ withCredentials: false,
68
+ crossOrigin: true,
69
+ },
70
+ };
71
+ });
72
+ },
73
+ /**
74
+ * Unpack the response from the server into a list of bids.
75
+ *
76
+ * @param {ServerResponse} serverResponse A successful response from the server.
77
+ * @return {Bid[]} An array of bids which were nested inside the server.
78
+ */
79
+ interpretResponse: function (serverResponse, bidRequest) {
80
+ const responseBody = serverResponse.body;
81
+
82
+ if (typeof responseBody !== 'object' || responseBody.available !== true) {
83
+ return [];
84
+ }
85
+
86
+ const bidResponses = [];
87
+ const bidResponse = {
88
+ requestId: responseBody.bidResponse.requestId,
89
+ cpm: responseBody.bidResponse.cpm,
90
+ width: responseBody.bidResponse.width,
91
+ height: responseBody.bidResponse.height,
92
+ creativeId: responseBody.bidResponse.creativeId,
93
+ currency: responseBody.bidResponse.currency,
94
+ netRevenue: responseBody.bidResponse.netRevenue,
95
+ ttl: responseBody.bidResponse.ttl,
96
+ ad: responseBody.bidResponse.ad,
97
+ vastUrl: responseBody.bidResponse.vastUrl,
98
+ meta: {
99
+ advertiserDomains: responseBody.bidResponse.meta.advertiserDomains
100
+ }
101
+ };
102
+ bidResponses.push(bidResponse);
103
+ return bidResponses;
104
+ },
105
+ };
106
+ registerBidder(spec);
@@ -6,6 +6,7 @@ import { Renderer } from '../src/Renderer.js';
6
6
 
7
7
  const INTEGRATION_METHOD = 'prebid.js';
8
8
  const BIDDER_CODE = 'yahoossp';
9
+ const GVLID = 25;
9
10
  const ADAPTER_VERSION = '1.0.2';
10
11
  const PREBID_VERSION = '$prebid.version$';
11
12
  const DEFAULT_BID_TTL = 300;
@@ -515,6 +516,7 @@ function createRenderer(bidderRequest, bidResponse) {
515
516
 
516
517
  export const spec = {
517
518
  code: BIDDER_CODE,
519
+ gvlid: GVLID,
518
520
  aliases: [],
519
521
  supportedMediaTypes: [BANNER, VIDEO],
520
522
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prebid.js",
3
- "version": "6.5.0",
3
+ "version": "6.9.0",
4
4
  "description": "Header Bidding Management Library",
5
5
  "main": "src/prebid.js",
6
6
  "scripts": {
@@ -30,6 +30,7 @@
30
30
  },
31
31
  "devDependencies": {
32
32
  "@babel/core": "^7.8.4",
33
+ "@babel/eslint-parser": "^7.16.5",
33
34
  "@babel/preset-env": "^7.8.4",
34
35
  "@jsdevtools/coverage-istanbul-loader": "^3.0.3",
35
36
  "@wdio/browserstack-service": "^6.1.4",
@@ -0,0 +1,38 @@
1
+ import {logError} from './utils.js';
2
+ import events from './events.js';
3
+ import CONSTANTS from './constants.json';
4
+
5
+ const {AD_RENDER_FAILED, AD_RENDER_SUCCEEDED} = CONSTANTS.EVENTS;
6
+
7
+ /**
8
+ * Emit the AD_RENDER_FAILED event.
9
+ *
10
+ * @param reason one of the values in CONSTANTS.AD_RENDER_FAILED_REASON
11
+ * @param message failure description
12
+ * @param bid? bid response object that failed to render
13
+ * @param id? adId that failed to render
14
+ */
15
+ export function emitAdRenderFail({ reason, message, bid, id }) {
16
+ const data = { reason, message };
17
+ if (bid) data.bid = bid;
18
+ if (id) data.adId = id;
19
+
20
+ logError(message);
21
+ events.emit(AD_RENDER_FAILED, data);
22
+ }
23
+
24
+ /**
25
+ * Emit the AD_RENDER_SUCCEEDED event.
26
+ *
27
+ * @param doc document object that was used to `.write` the ad. Should be `null` if unavailable (e.g. for documents in
28
+ * a cross-origin frame).
29
+ * @param bid bid response object for the ad that was rendered
30
+ * @param id adId that was rendered.
31
+ */
32
+ export function emitAdRenderSucceeded({ doc, bid, id }) {
33
+ const data = { doc };
34
+ if (bid) data.bid = bid;
35
+ if (id) data.adId = id;
36
+
37
+ events.emit(AD_RENDER_SUCCEEDED, data);
38
+ }
package/src/adloader.js CHANGED
@@ -8,7 +8,8 @@ const _approvedLoadExternalJSList = [
8
8
  'criteo',
9
9
  'outstream',
10
10
  'adagio',
11
- 'browsi'
11
+ 'browsi',
12
+ 'brandmetrics'
12
13
  ]
13
14
 
14
15
  /**
package/src/auction.js CHANGED
@@ -59,7 +59,7 @@
59
59
 
60
60
  import {
61
61
  flatten, timestamp, adUnitsFilter, deepAccess, getBidRequest, getValue, parseUrl, generateUUID,
62
- logMessage, bind, logError, logInfo, logWarn, isEmpty, _each, isFn, isEmptyStr, isAllowZeroCpmBidsEnabled
62
+ logMessage, bind, logError, logInfo, logWarn, isEmpty, _each, isFn, isEmptyStr
63
63
  } from './utils.js';
64
64
  import { getPriceBucketString } from './cpmBucketManager.js';
65
65
  import { getNativeTargeting } from './native.js';
@@ -72,6 +72,7 @@ import find from 'core-js-pure/features/array/find.js';
72
72
  import includes from 'core-js-pure/features/array/includes.js';
73
73
  import { OUTSTREAM } from './video.js';
74
74
  import { VIDEO } from './mediaTypes.js';
75
+ import {bidderSettings} from './bidderSettings.js';
75
76
 
76
77
  const { syncUsers } = userSync;
77
78
 
@@ -250,10 +251,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
250
251
 
251
252
  let callbacks = auctionCallbacks(auctionDone, this);
252
253
  adapterManager.callBids(_adUnits, bidRequests, function(...args) {
253
- addBidResponse.apply({
254
- dispatch: callbacks.addBidResponse,
255
- bidderRequest: this
256
- }, args)
254
+ callbacks.addBidResponse.apply(this, args);
257
255
  }, callbacks.adapterDone, {
258
256
  request(source, origin) {
259
257
  increment(outstandingRequests, origin);
@@ -344,6 +342,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
344
342
  addWinningBid,
345
343
  setBidTargeting,
346
344
  getWinningBids: () => _winningBids,
345
+ getAuctionStart: () => _auctionStart,
347
346
  getTimeout: () => _timeout,
348
347
  getAuctionId: () => _auctionId,
349
348
  getAuctionStatus: () => _auctionStatus,
@@ -355,8 +354,12 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
355
354
  }
356
355
  }
357
356
 
358
- export const addBidResponse = hook('async', function(adUnitCode, bid) {
359
- this.dispatch.call(this.bidderRequest, adUnitCode, bid);
357
+ /**
358
+ * addBidResponse may return a Promise; if it does, the auction will attempt to
359
+ * wait for it to complete (successfully or not) before closing.
360
+ */
361
+ export const addBidResponse = hook('sync', function(adUnitCode, bid) {
362
+ return this.dispatch.call(this.bidderRequest, adUnitCode, bid);
360
363
  }, 'addBidResponse');
361
364
 
362
365
  export const addBidderRequests = hook('sync', function(bidderRequests) {
@@ -374,6 +377,32 @@ export function auctionCallbacks(auctionDone, auctionInstance) {
374
377
  let allAdapterCalledDone = false;
375
378
  let bidderRequestsDone = new Set();
376
379
  let bidResponseMap = {};
380
+ const ready = {};
381
+
382
+ function waitFor(bidderRequest, result) {
383
+ const id = bidderRequest.bidderRequestId;
384
+ if (ready[id] == null) {
385
+ ready[id] = Promise.resolve();
386
+ }
387
+ ready[id] = ready[id].then(() => Promise.resolve(result).catch(() => {}))
388
+ }
389
+
390
+ function guard(bidderRequest, fn) {
391
+ let timeout = bidderRequest.timeout;
392
+ if (timeout == null || timeout > auctionInstance.getTimeout()) {
393
+ timeout = auctionInstance.getTimeout();
394
+ }
395
+ const timeRemaining = auctionInstance.getAuctionStart() + timeout - Date.now();
396
+ const wait = ready[bidderRequest.bidderRequestId];
397
+ if (wait != null && timeRemaining > 0) {
398
+ Promise.race([
399
+ new Promise((resolve) => setTimeout(resolve, timeRemaining)),
400
+ wait
401
+ ]).then(fn);
402
+ } else {
403
+ fn();
404
+ }
405
+ }
377
406
 
378
407
  function afterBidAdded() {
379
408
  outstandingBidsAdded--;
@@ -382,7 +411,7 @@ export function auctionCallbacks(auctionDone, auctionInstance) {
382
411
  }
383
412
  }
384
413
 
385
- function addBidResponse(adUnitCode, bid) {
414
+ function handleBidResponse(adUnitCode, bid) {
386
415
  let bidderRequest = this;
387
416
 
388
417
  bidResponseMap[bid.requestId] = true;
@@ -429,8 +458,15 @@ export function auctionCallbacks(auctionDone, auctionInstance) {
429
458
  }
430
459
 
431
460
  return {
432
- addBidResponse,
433
- adapterDone
461
+ addBidResponse: function (...args) {
462
+ waitFor(this, addBidResponse.apply({
463
+ dispatch: handleBidResponse,
464
+ bidderRequest: this
465
+ }, args));
466
+ },
467
+ adapterDone: function () {
468
+ guard(this, adapterDone.bind(this))
469
+ }
434
470
  }
435
471
  }
436
472
 
@@ -567,7 +603,7 @@ function getPreparedBidForAuction({adUnitCode, bid, bidderRequest, auctionId}) {
567
603
 
568
604
  function setupBidTargeting(bidObject, bidderRequest) {
569
605
  let keyValues;
570
- const cpmCheck = (isAllowZeroCpmBidsEnabled(bidObject.bidderCode)) ? bidObject.cpm >= 0 : bidObject.cpm > 0;
606
+ const cpmCheck = (bidderSettings.get(bidObject.bidderCode, 'allowZeroCpmBids') === true) ? bidObject.cpm >= 0 : bidObject.cpm > 0;
571
607
  if (bidObject.bidderCode && (cpmCheck || bidObject.dealId)) {
572
608
  let bidReq = find(bidderRequest.bids, bid => bid.adUnitCode === bidObject.adUnitCode && bid.bidId === bidObject.requestId);
573
609
  keyValues = getKeyValueTargetingPairs(bidObject.bidderCode, bidObject, bidReq);
@@ -615,18 +651,18 @@ export const getPriceGranularity = (mediaType, bidReq) => {
615
651
  */
616
652
  export const getPriceByGranularity = (granularity) => {
617
653
  return (bid, bidReq) => {
618
- granularity = granularity || getPriceGranularity(bid.mediaType, bidReq);
619
- if (granularity === CONSTANTS.GRANULARITY_OPTIONS.AUTO) {
654
+ const bidGranularity = granularity || getPriceGranularity(bid.mediaType, bidReq);
655
+ if (bidGranularity === CONSTANTS.GRANULARITY_OPTIONS.AUTO) {
620
656
  return bid.pbAg;
621
- } else if (granularity === CONSTANTS.GRANULARITY_OPTIONS.DENSE) {
657
+ } else if (bidGranularity === CONSTANTS.GRANULARITY_OPTIONS.DENSE) {
622
658
  return bid.pbDg;
623
- } else if (granularity === CONSTANTS.GRANULARITY_OPTIONS.LOW) {
659
+ } else if (bidGranularity === CONSTANTS.GRANULARITY_OPTIONS.LOW) {
624
660
  return bid.pbLg;
625
- } else if (granularity === CONSTANTS.GRANULARITY_OPTIONS.MEDIUM) {
661
+ } else if (bidGranularity === CONSTANTS.GRANULARITY_OPTIONS.MEDIUM) {
626
662
  return bid.pbMg;
627
- } else if (granularity === CONSTANTS.GRANULARITY_OPTIONS.HIGH) {
663
+ } else if (bidGranularity === CONSTANTS.GRANULARITY_OPTIONS.HIGH) {
628
664
  return bid.pbHg;
629
- } else if (granularity === CONSTANTS.GRANULARITY_OPTIONS.CUSTOM) {
665
+ } else if (bidGranularity === CONSTANTS.GRANULARITY_OPTIONS.CUSTOM) {
630
666
  return bid.pbCg;
631
667
  }
632
668
  }
@@ -642,6 +678,34 @@ export const getAdvertiserDomain = () => {
642
678
  }
643
679
  }
644
680
 
681
+ // factory for key value objs
682
+ function createKeyVal(key, value) {
683
+ return {
684
+ key,
685
+ val: (typeof value === 'function')
686
+ ? function (bidResponse, bidReq) {
687
+ return value(bidResponse, bidReq);
688
+ }
689
+ : function (bidResponse) {
690
+ return getValue(bidResponse, value);
691
+ }
692
+ };
693
+ }
694
+
695
+ function defaultAdserverTargeting() {
696
+ const TARGETING_KEYS = CONSTANTS.TARGETING_KEYS;
697
+ return [
698
+ createKeyVal(TARGETING_KEYS.BIDDER, 'bidderCode'),
699
+ createKeyVal(TARGETING_KEYS.AD_ID, 'adId'),
700
+ createKeyVal(TARGETING_KEYS.PRICE_BUCKET, getPriceByGranularity()),
701
+ createKeyVal(TARGETING_KEYS.SIZE, 'size'),
702
+ createKeyVal(TARGETING_KEYS.DEAL, 'dealId'),
703
+ createKeyVal(TARGETING_KEYS.SOURCE, 'source'),
704
+ createKeyVal(TARGETING_KEYS.FORMAT, 'mediaType'),
705
+ createKeyVal(TARGETING_KEYS.ADOMAIN, getAdvertiserDomain()),
706
+ ]
707
+ }
708
+
645
709
  /**
646
710
  * @param {string} mediaType
647
711
  * @param {string} bidderCode
@@ -649,40 +713,16 @@ export const getAdvertiserDomain = () => {
649
713
  * @returns {*}
650
714
  */
651
715
  export function getStandardBidderSettings(mediaType, bidderCode) {
652
- // factory for key value objs
653
- function createKeyVal(key, value) {
654
- return {
655
- key,
656
- val: (typeof value === 'function')
657
- ? function (bidResponse, bidReq) {
658
- return value(bidResponse, bidReq);
659
- }
660
- : function (bidResponse) {
661
- return getValue(bidResponse, value);
662
- }
663
- };
664
- }
665
716
  const TARGETING_KEYS = CONSTANTS.TARGETING_KEYS;
717
+ const standardSettings = Object.assign({}, bidderSettings.settingsFor(null));
666
718
 
667
- let bidderSettings = $$PREBID_GLOBAL$$.bidderSettings;
668
- if (!bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD]) {
669
- bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD] = {};
670
- }
671
- if (!bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD][CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING]) {
672
- bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD][CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING] = [
673
- createKeyVal(TARGETING_KEYS.BIDDER, 'bidderCode'),
674
- createKeyVal(TARGETING_KEYS.AD_ID, 'adId'),
675
- createKeyVal(TARGETING_KEYS.PRICE_BUCKET, getPriceByGranularity()),
676
- createKeyVal(TARGETING_KEYS.SIZE, 'size'),
677
- createKeyVal(TARGETING_KEYS.DEAL, 'dealId'),
678
- createKeyVal(TARGETING_KEYS.SOURCE, 'source'),
679
- createKeyVal(TARGETING_KEYS.FORMAT, 'mediaType'),
680
- createKeyVal(TARGETING_KEYS.ADOMAIN, getAdvertiserDomain()),
681
- ]
719
+ if (!standardSettings[CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING]) {
720
+ standardSettings[CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING] = defaultAdserverTargeting();
682
721
  }
683
722
 
684
723
  if (mediaType === 'video') {
685
- const adserverTargeting = bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD][CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING];
724
+ const adserverTargeting = standardSettings[CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING].slice();
725
+ standardSettings[CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING] = adserverTargeting;
686
726
 
687
727
  // Adding hb_uuid + hb_cache_id
688
728
  [TARGETING_KEYS.UUID, TARGETING_KEYS.CACHE_ID].forEach(targetingKeyVal => {
@@ -692,7 +732,7 @@ export function getStandardBidderSettings(mediaType, bidderCode) {
692
732
  });
693
733
 
694
734
  // Adding hb_cache_host
695
- if (config.getConfig('cache.url') && (!bidderCode || deepAccess(bidderSettings, `${bidderCode}.sendStandardTargeting`) !== false)) {
735
+ if (config.getConfig('cache.url') && (!bidderCode || bidderSettings.get(bidderCode, 'sendStandardTargeting') !== false)) {
696
736
  const urlInfo = parseUrl(config.getConfig('cache.url'));
697
737
 
698
738
  if (typeof find(adserverTargeting, targetingKeyVal => targetingKeyVal.key === TARGETING_KEYS.CACHE_HOST) === 'undefined') {
@@ -703,7 +743,7 @@ export function getStandardBidderSettings(mediaType, bidderCode) {
703
743
  }
704
744
  }
705
745
  }
706
- return bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD];
746
+ return standardSettings;
707
747
  }
708
748
 
709
749
  export function getKeyValueTargetingPairs(bidderCode, custBidObj, bidReq) {
@@ -712,19 +752,15 @@ export function getKeyValueTargetingPairs(bidderCode, custBidObj, bidReq) {
712
752
  }
713
753
 
714
754
  var keyValues = {};
715
- var bidderSettings = $$PREBID_GLOBAL$$.bidderSettings;
716
755
 
717
756
  // 1) set the keys from "standard" setting or from prebid defaults
718
- if (bidderSettings) {
719
- // initialize default if not set
720
- const standardSettings = getStandardBidderSettings(custBidObj.mediaType, bidderCode);
721
- setKeys(keyValues, standardSettings, custBidObj, bidReq);
722
-
723
- // 2) set keys from specific bidder setting override if they exist
724
- if (bidderCode && bidderSettings[bidderCode] && bidderSettings[bidderCode][CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING]) {
725
- setKeys(keyValues, bidderSettings[bidderCode], custBidObj, bidReq);
726
- custBidObj.sendStandardTargeting = bidderSettings[bidderCode].sendStandardTargeting;
727
- }
757
+ const standardSettings = getStandardBidderSettings(custBidObj.mediaType, bidderCode);
758
+ setKeys(keyValues, standardSettings, custBidObj, bidReq);
759
+
760
+ // 2) set keys from specific bidder setting override if they exist
761
+ if (bidderCode && bidderSettings.getOwn(bidderCode, CONSTANTS.JSON_MAPPING.ADSERVER_TARGETING)) {
762
+ setKeys(keyValues, bidderSettings.ownSettingsFor(bidderCode), custBidObj, bidReq);
763
+ custBidObj.sendStandardTargeting = bidderSettings.get(bidderCode, 'sendStandardTargeting');
728
764
  }
729
765
 
730
766
  // set native key value targeting
@@ -776,19 +812,13 @@ function setKeys(keyValues, bidderSettings, custBidObj, bidReq) {
776
812
  export function adjustBids(bid) {
777
813
  let code = bid.bidderCode;
778
814
  let bidPriceAdjusted = bid.cpm;
779
- let bidCpmAdjustment;
780
- if ($$PREBID_GLOBAL$$.bidderSettings) {
781
- if (code && $$PREBID_GLOBAL$$.bidderSettings[code] && typeof $$PREBID_GLOBAL$$.bidderSettings[code].bidCpmAdjustment === 'function') {
782
- bidCpmAdjustment = $$PREBID_GLOBAL$$.bidderSettings[code].bidCpmAdjustment;
783
- } else if ($$PREBID_GLOBAL$$.bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD] && typeof $$PREBID_GLOBAL$$.bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD].bidCpmAdjustment === 'function') {
784
- bidCpmAdjustment = $$PREBID_GLOBAL$$.bidderSettings[CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD].bidCpmAdjustment;
785
- }
786
- if (bidCpmAdjustment) {
787
- try {
788
- bidPriceAdjusted = bidCpmAdjustment(bid.cpm, Object.assign({}, bid));
789
- } catch (e) {
790
- logError('Error during bid adjustment', 'bidmanager.js', e);
791
- }
815
+ const bidCpmAdjustment = bidderSettings.get(code || null, 'bidCpmAdjustment');
816
+
817
+ if (bidCpmAdjustment && typeof bidCpmAdjustment === 'function') {
818
+ try {
819
+ bidPriceAdjusted = bidCpmAdjustment(bid.cpm, Object.assign({}, bid));
820
+ } catch (e) {
821
+ logError('Error during bid adjustment', 'bidmanager.js', e);
792
822
  }
793
823
  }
794
824
 
@@ -0,0 +1,69 @@
1
+ import {deepAccess, mergeDeep} from './utils.js';
2
+ import {getGlobal} from './prebidGlobal.js';
3
+
4
+ const CONSTANTS = require('./constants.json');
5
+
6
+ export class ScopedSettings {
7
+ constructor(getSettings, defaultScope) {
8
+ this.getSettings = getSettings;
9
+ this.defaultScope = defaultScope;
10
+ }
11
+
12
+ /**
13
+ * Get setting value at `path` under the given scope, falling back to the default scope if needed.
14
+ * If `scope` is `null`, get the setting's default value.
15
+ * @param scope {String|null}
16
+ * @param path {String}
17
+ * @returns {*}
18
+ */
19
+ get(scope, path) {
20
+ let value = this.getOwn(scope, path);
21
+ if (typeof value === 'undefined') {
22
+ value = this.getOwn(null, path);
23
+ }
24
+ return value;
25
+ }
26
+
27
+ /**
28
+ * Get the setting value at `path` *without* falling back to the default value.
29
+ * @param scope {String}
30
+ * @param path {String}
31
+ * @returns {*}
32
+ */
33
+ getOwn(scope, path) {
34
+ scope = this.#resolveScope(scope);
35
+ return deepAccess(this.getSettings(), `${scope}.${path}`)
36
+ }
37
+
38
+ /**
39
+ * @returns {string[]} all existing scopes except the default one.
40
+ */
41
+ getScopes() {
42
+ return Object.keys(this.getSettings()).filter((scope) => scope !== this.defaultScope);
43
+ }
44
+
45
+ /**
46
+ * @returns all settings in the given scope, merged with the settings for the default scope.
47
+ */
48
+ settingsFor(scope) {
49
+ return mergeDeep({}, this.ownSettingsFor(null), this.ownSettingsFor(scope));
50
+ }
51
+
52
+ /**
53
+ * @returns all settings in the given scope, *without* any of the default settings.
54
+ */
55
+ ownSettingsFor(scope) {
56
+ scope = this.#resolveScope(scope);
57
+ return this.getSettings()[scope] || {};
58
+ }
59
+
60
+ #resolveScope(scope) {
61
+ if (scope == null) {
62
+ return this.defaultScope;
63
+ } else {
64
+ return scope;
65
+ }
66
+ }
67
+ }
68
+
69
+ export const bidderSettings = new ScopedSettings(() => getGlobal().bidderSettings || {}, CONSTANTS.JSON_MAPPING.BD_SETTING_STANDARD);
package/src/hook.js CHANGED
@@ -13,14 +13,18 @@ export function setupBeforeHookFnOnce(baseFn, hookFn, priority = 15) {
13
13
  baseFn.before(hookFn, priority);
14
14
  }
15
15
  }
16
+ const submoduleInstallMap = {};
16
17
 
17
- export function module(name, install) {
18
+ export function module(name, install, {postInstallAllowed = false} = {}) {
18
19
  hook('async', function (submodules) {
19
20
  submodules.forEach(args => install(...args));
21
+ if (postInstallAllowed) submoduleInstallMap[name] = install;
20
22
  }, name)([]); // will be queued until hook.ready() called in pbjs.processQueue();
21
23
  }
22
24
 
23
25
  export function submodule(name, ...args) {
26
+ const install = submoduleInstallMap[name];
27
+ if (install) return install(...args);
24
28
  getHook(name).before((next, modules) => {
25
29
  modules.push(args);
26
30
  next(modules);
package/src/prebid.js CHANGED
@@ -19,6 +19,7 @@ import { adunitCounter } from './adUnits.js';
19
19
  import { executeRenderer, isRendererRequired } from './Renderer.js';
20
20
  import { createBid } from './bidfactory.js';
21
21
  import { storageCallbacks } from './storageManager.js';
22
+ import { emitAdRenderSucceeded, emitAdRenderFail } from './adRendering.js';
22
23
 
23
24
  const $$PREBID_GLOBAL$$ = getGlobal();
24
25
  const CONSTANTS = require('./constants.json');
@@ -27,7 +28,7 @@ const events = require('./events.js');
27
28
  const { triggerUserSyncs } = userSync;
28
29
 
29
30
  /* private variables */
30
- const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, AD_RENDER_FAILED, AD_RENDER_SUCCEEDED, STALE_RENDER } = CONSTANTS.EVENTS;
31
+ const { ADD_AD_UNITS, BID_WON, REQUEST_BIDS, SET_TARGETING, STALE_RENDER } = CONSTANTS.EVENTS;
31
32
  const { PREVENT_WRITING_ON_MAIN_DOCUMENT, NO_AD, EXCEPTION, CANNOT_FIND_AD, MISSING_DOC_OR_ADID } = CONSTANTS.AD_RENDER_FAILED_REASON;
32
33
 
33
34
  const eventValidators = {
@@ -146,7 +147,7 @@ function validateNativeMediaType(adUnit) {
146
147
  function validateAdUnitPos(adUnit, mediaType) {
147
148
  let pos = deepAccess(adUnit, `mediaTypes.${mediaType}.pos`);
148
149
 
149
- if (!pos || !isNumber(pos) || !isFinite(pos)) {
150
+ if (!isNumber(pos) || isNaN(pos) || !isFinite(pos)) {
150
151
  let warning = `Value of property 'pos' on ad unit ${adUnit.code} should be of type: Number`;
151
152
 
152
153
  logWarn(warning);
@@ -385,27 +386,23 @@ $$PREBID_GLOBAL$$.setTargetingForAst = function (adUnitCodes) {
385
386
  events.emit(SET_TARGETING, targeting.getAllTargeting());
386
387
  };
387
388
 
388
- function emitAdRenderFail({ reason, message, bid, id }) {
389
- const data = { reason, message };
390
- if (bid) data.bid = bid;
391
- if (id) data.adId = id;
392
-
393
- logError(message);
394
- events.emit(AD_RENDER_FAILED, data);
395
- }
396
-
397
- function emitAdRenderSucceeded({ doc, bid, id }) {
398
- const data = { doc };
399
- if (bid) data.bid = bid;
400
- if (id) data.adId = id;
401
-
402
- events.emit(AD_RENDER_SUCCEEDED, data);
389
+ /**
390
+ * This function will check for presence of given node in given parent. If not present - will inject it.
391
+ * @param {Node} node node, whose existance is in question
392
+ * @param {Document} doc document element do look in
393
+ * @param {string} tagName tag name to look in
394
+ */
395
+ function reinjectNodeIfRemoved(node, doc, tagName) {
396
+ const injectionNode = doc.querySelector(tagName);
397
+ if (!node.parentNode || node.parentNode !== injectionNode) {
398
+ insertElement(node, doc, tagName);
399
+ }
403
400
  }
404
401
 
405
402
  /**
406
403
  * This function will render the ad (based on params) in the given iframe document passed through.
407
404
  * Note that doc SHOULD NOT be the parent document page as we can't doc.write() asynchronously
408
- * @param {HTMLDocument} doc document
405
+ * @param {Document} doc document
409
406
  * @param {string} id bid id to locate the ad
410
407
  * @alias module:pbjs.renderAd
411
408
  */
@@ -449,10 +446,11 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) {
449
446
  const {height, width, ad, mediaType, adUrl, renderer} = bid;
450
447
 
451
448
  const creativeComment = document.createComment(`Creative ${bid.creativeId} served by ${bid.bidder} Prebid.js Header Bidding`);
449
+ insertElement(creativeComment, doc, 'html');
452
450
 
453
451
  if (isRendererRequired(renderer)) {
454
452
  executeRenderer(renderer, bid);
455
- insertElement(creativeComment, doc, 'html');
453
+ reinjectNodeIfRemoved(creativeComment, doc, 'html');
456
454
  emitAdRenderSucceeded({ doc, bid, id });
457
455
  } else if ((doc === document && !inIframe()) || mediaType === 'video') {
458
456
  const message = `Error trying to write ad. Ad render call ad id ${id} was prevented from writing to the main document.`;
@@ -471,7 +469,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) {
471
469
  doc.write(ad);
472
470
  doc.close();
473
471
  setRenderSize(doc, width, height);
474
- insertElement(creativeComment, doc, 'html');
472
+ reinjectNodeIfRemoved(creativeComment, doc, 'html');
475
473
  callBurl(bid);
476
474
  emitAdRenderSucceeded({ doc, bid, id });
477
475
  } else if (adUrl) {
@@ -484,7 +482,7 @@ $$PREBID_GLOBAL$$.renderAd = hook('async', function (doc, id, options) {
484
482
 
485
483
  insertElement(iframe, doc, 'body');
486
484
  setRenderSize(doc, width, height);
487
- insertElement(creativeComment, doc, 'html');
485
+ reinjectNodeIfRemoved(creativeComment, doc, 'html');
488
486
  callBurl(bid);
489
487
  emitAdRenderSucceeded({ doc, bid, id });
490
488
  } else {