prebid.js 6.4.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 (147) hide show
  1. package/integrationExamples/gpt/amp/creative.html +11 -33
  2. package/integrationExamples/gpt/x-domain/creative.html +53 -26
  3. package/modules/.submodules.json +2 -1
  4. package/modules/adagioBidAdapter.js +0 -8
  5. package/modules/adagioBidAdapter.md +1 -1
  6. package/modules/adbookpspBidAdapter.js +27 -10
  7. package/modules/adhashBidAdapter.js +3 -3
  8. package/modules/adkernelBidAdapter.js +2 -1
  9. package/modules/admanBidAdapter.js +10 -4
  10. package/modules/adomikAnalyticsAdapter.js +23 -11
  11. package/modules/adqueryIdSystem.js +103 -0
  12. package/modules/adqueryIdSystem.md +35 -0
  13. package/modules/adyoulikeBidAdapter.js +13 -9
  14. package/modules/appnexusBidAdapter.js +11 -0
  15. package/modules/bliinkBidAdapter.js +3 -2
  16. package/modules/brandmetricsRtdProvider.js +168 -0
  17. package/modules/brandmetricsRtdProvider.md +40 -0
  18. package/modules/colossussspBidAdapter.js +12 -8
  19. package/modules/colossussspBidAdapter.md +15 -1
  20. package/modules/compassBidAdapter.js +10 -3
  21. package/modules/consentManagement.js +7 -1
  22. package/modules/consumableBidAdapter.md +1 -1
  23. package/modules/criteoBidAdapter.js +10 -1
  24. package/modules/criteoIdSystem.js +29 -7
  25. package/modules/currency.js +26 -1
  26. package/modules/displayioBidAdapter.js +157 -0
  27. package/modules/displayioBidAdapter.md +148 -0
  28. package/modules/docereeBidAdapter.js +10 -1
  29. package/modules/docereeBidAdapter.md +2 -0
  30. package/modules/e_volutionBidAdapter.js +158 -0
  31. package/modules/glimpseBidAdapter.js +66 -44
  32. package/modules/gnetBidAdapter.js +3 -3
  33. package/modules/gnetBidAdapter.md +4 -4
  34. package/modules/gptPreAuction.js +55 -7
  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 +24 -2
  39. package/modules/interactiveOffersBidAdapter.js +9 -6
  40. package/modules/ixBidAdapter.js +29 -12
  41. package/modules/jwplayerRtdProvider.js +71 -6
  42. package/modules/jwplayerRtdProvider.md +27 -11
  43. package/modules/kargoBidAdapter.js +2 -2
  44. package/modules/limelightDigitalBidAdapter.js +2 -1
  45. package/modules/livewrappedAnalyticsAdapter.js +3 -1
  46. package/modules/loglyliftBidAdapter.js +79 -0
  47. package/modules/loglyliftBidAdapter.md +55 -0
  48. package/modules/nextMillenniumBidAdapter.js +3 -1
  49. package/modules/oguryBidAdapter.js +9 -2
  50. package/modules/onetagBidAdapter.js +4 -2
  51. package/modules/optimeraRtdProvider.js +8 -1
  52. package/modules/ozoneBidAdapter.js +21 -64
  53. package/modules/pilotxBidAdapter.js +147 -0
  54. package/modules/pilotxBidAdapter.md +50 -0
  55. package/modules/proxistoreBidAdapter.js +0 -2
  56. package/modules/pubmaticAnalyticsAdapter.js +16 -0
  57. package/modules/richaudienceBidAdapter.js +3 -2
  58. package/modules/riseBidAdapter.js +1 -1
  59. package/modules/rtbhouseBidAdapter.js +14 -4
  60. package/modules/rtdModule/index.js +14 -15
  61. package/modules/rubiconAnalyticsAdapter.js +3 -2
  62. package/modules/seedingAllianceBidAdapter.js +3 -3
  63. package/modules/sharethroughBidAdapter.js +12 -17
  64. package/modules/showheroes-bsBidAdapter.js +13 -2
  65. package/modules/sovrnBidAdapter.js +93 -18
  66. package/modules/sovrnBidAdapter.md +80 -2
  67. package/modules/synacormediaBidAdapter.js +31 -10
  68. package/modules/tappxBidAdapter.js +8 -5
  69. package/modules/teadsBidAdapter.js +1 -2
  70. package/modules/undertoneBidAdapter.js +17 -1
  71. package/modules/userId/eids.js +7 -1
  72. package/modules/userId/userId.md +8 -0
  73. package/modules/viewability.js +177 -0
  74. package/modules/viewability.md +87 -0
  75. package/modules/welectBidAdapter.js +106 -0
  76. package/modules/yahoosspBidAdapter.js +2 -0
  77. package/modules/yieldmoBidAdapter.js +23 -5
  78. package/modules/zeta_global_sspAnalyticsAdapter.js +97 -0
  79. package/modules/zeta_global_sspAnalyticsAdapter.md +24 -0
  80. package/package.json +1 -1
  81. package/src/adRendering.js +38 -0
  82. package/src/auction.js +44 -9
  83. package/src/config.js +27 -3
  84. package/src/hook.js +5 -1
  85. package/src/prebid.js +21 -21
  86. package/src/secureCreatives.js +114 -44
  87. package/src/utils.js +25 -4
  88. package/test/helpers/syncPromise.js +71 -0
  89. package/test/spec/auctionmanager_spec.js +148 -16
  90. package/test/spec/config_spec.js +279 -0
  91. package/test/spec/modules/adagioBidAdapter_spec.js +0 -10
  92. package/test/spec/modules/adbookpspBidAdapter_spec.js +17 -3
  93. package/test/spec/modules/adhashBidAdapter_spec.js +2 -2
  94. package/test/spec/modules/admanBidAdapter_spec.js +2 -2
  95. package/test/spec/modules/adomikAnalyticsAdapter_spec.js +3 -1
  96. package/test/spec/modules/adqueryIdSystem_spec.js +74 -0
  97. package/test/spec/modules/adyoulikeBidAdapter_spec.js +49 -0
  98. package/test/spec/modules/appnexusBidAdapter_spec.js +27 -0
  99. package/test/spec/modules/bliinkBidAdapter_spec.js +2 -0
  100. package/test/spec/modules/brandmetricsRtdProvider_spec.js +191 -0
  101. package/test/spec/modules/colossussspBidAdapter_spec.js +5 -2
  102. package/test/spec/modules/compassBidAdapter_spec.js +1 -0
  103. package/test/spec/modules/consentManagement_spec.js +20 -0
  104. package/test/spec/modules/criteoBidAdapter_spec.js +21 -0
  105. package/test/spec/modules/criteoIdSystem_spec.js +6 -3
  106. package/test/spec/modules/currency_spec.js +21 -6
  107. package/test/spec/modules/displayioBidAdapter_spec.js +239 -0
  108. package/test/spec/modules/docereeBidAdapter_spec.js +9 -1
  109. package/test/spec/modules/e_volutionBidAdapter_spec.js +242 -0
  110. package/test/spec/modules/eids_spec.js +15 -0
  111. package/test/spec/modules/glimpseBidAdapter_spec.js +0 -18
  112. package/test/spec/modules/gnetBidAdapter_spec.js +6 -6
  113. package/test/spec/modules/gptPreAuction_spec.js +177 -2
  114. package/test/spec/modules/gumgumBidAdapter_spec.js +46 -0
  115. package/test/spec/modules/idImportLibrary_spec.js +197 -10
  116. package/test/spec/modules/improvedigitalBidAdapter_spec.js +42 -0
  117. package/test/spec/modules/ixBidAdapter_spec.js +104 -62
  118. package/test/spec/modules/jwplayerRtdProvider_spec.js +195 -2
  119. package/test/spec/modules/kargoBidAdapter_spec.js +1 -1
  120. package/test/spec/modules/limelightDigitalBidAdapter_spec.js +75 -17
  121. package/test/spec/modules/livewrappedAnalyticsAdapter_spec.js +22 -0
  122. package/test/spec/modules/loglyliftBidAdapter_spec.js +172 -0
  123. package/test/spec/modules/nextMillenniumBidAdapter_spec.js +1 -1
  124. package/test/spec/modules/oguryBidAdapter_spec.js +10 -2
  125. package/test/spec/modules/optimeraRtdProvider_spec.js +14 -1
  126. package/test/spec/modules/ozoneBidAdapter_spec.js +43 -31
  127. package/test/spec/modules/pilotxBidAdapter_spec.js +244 -0
  128. package/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +13 -1
  129. package/test/spec/modules/realTimeDataModule_spec.js +67 -5
  130. package/test/spec/modules/richaudienceBidAdapter_spec.js +40 -0
  131. package/test/spec/modules/riseBidAdapter_spec.js +1 -1
  132. package/test/spec/modules/rtbhouseBidAdapter_spec.js +20 -0
  133. package/test/spec/modules/rubiconAnalyticsAdapter_spec.js +30 -0
  134. package/test/spec/modules/sharethroughBidAdapter_spec.js +91 -6
  135. package/test/spec/modules/showheroes-bsBidAdapter_spec.js +2 -0
  136. package/test/spec/modules/sovrnBidAdapter_spec.js +413 -333
  137. package/test/spec/modules/synacormediaBidAdapter_spec.js +70 -0
  138. package/test/spec/modules/tappxBidAdapter_spec.js +0 -19
  139. package/test/spec/modules/teadsBidAdapter_spec.js +14 -59
  140. package/test/spec/modules/undertoneBidAdapter_spec.js +55 -2
  141. package/test/spec/modules/userId_spec.js +68 -19
  142. package/test/spec/modules/viewability_spec.js +280 -0
  143. package/test/spec/modules/welectBidAdapter_spec.js +211 -0
  144. package/test/spec/modules/yahoosspBidAdapter_spec.js +10 -0
  145. package/test/spec/modules/zeta_global_sspAnalyticsAdapter_spec.js +427 -0
  146. package/test/spec/unit/pbjs_api_spec.js +20 -2
  147. package/test/spec/unit/secureCreatives_spec.js +85 -0
@@ -0,0 +1,87 @@
1
+ # Overview
2
+
3
+ Module Name: Viewability
4
+
5
+ Purpose: Track when a given HTML element becomes viewable
6
+
7
+ Maintainer: atrajkovic@magnite.com
8
+
9
+ # Configuration
10
+
11
+ Module does not need any configuration, as long as you include it in your PBJS bundle.
12
+ Viewability module has only two functions `startMeasurement` and `stopMeasurement` which can be used to enable more complex viewability measurements. Since it allows tracking from within creative (possibly inside a safe frame) this module registers a message listener, for messages with a format that is described bellow.
13
+
14
+ ## `startMeasurement`
15
+
16
+ | startMeasurement Arg Object | Scope | Type | Description | Example |
17
+ | --------------------- | -------- | ------------ | -------------------------------------------------------------------------------- | --------- |
18
+ | vid | Required | String | Unique viewability identifier, used to reference particular observer | `"ae0f9"` |
19
+ | element | Required | HTMLElement | Reference to an HTML element that needs to be tracked | `document.getElementById('test_div')` |
20
+ | tracker | Required | ViewabilityTracker | How viewaility event is communicated back to the parties of interest | `{ method: 'img', value: 'http://my.tracker/123' }` |
21
+ | criteria | Required | ViewabilityCriteria| Defines custom viewability criteria using the threshold and duration provided | `{ inViewThreshold: 0.5, timeInView: 1000 }` |
22
+
23
+ | ViewabilityTracker | Scope | Type | Description | Example |
24
+ | --------------------- | -------- | ------------ | -------------------------------------------------------------------------------- | --------- |
25
+ | method | Required | String | Type of method for Tracker | `'img' OR 'js' OR 'callback'` |
26
+ | value | Required | String | URL string for 'img' and 'js' Trackers, or a function for 'callback' Tracker | `'http://my.tracker/123'` |
27
+
28
+ | ViewabilityCriteria | Scope | Type | Description | Example |
29
+ | --------------------- | -------- | ------------ | -------------------------------------------------------------------------------- | --------- |
30
+ | inViewThreshold | Required | Number | Represents a percentage threshold for the Element to be registered as in view | `0.5` |
31
+ | timeInView | Required | Number | Number of milliseconds that a given element needs to be in view continuously, above the threshold | `1000` |
32
+
33
+ ## Please Note:
34
+ - `vid` allows for multiple trackers, with different criteria to be registered for a given HTML element, independently. It's not autogenerated by `startMeasurement()`, it needs to be provided by the caller so that it doesn't have to be posted back to the source iframe (in case viewability is started from within the creative).
35
+ - In case of 'callback' method, HTML element is being passed back to the callback function.
36
+ - When a tracker needs to be started, without direct access to pbjs, postMessage mechanism can be used to invoke `startMeasurement`, with a following payload: `vid`, `tracker` and `criteria` as described above, but also with `message: 'Prebid Viewability'` and `action: 'startMeasurement'`. Optionally payload can provide `elementId`, if available at that time (for ad servers where name of the iframe is known, or adservers that render outside an iframe). If `elementId` is not provided, viewability module will try to find the iframe that corresponds to the message source.
37
+
38
+
39
+ ## `stopMeasurement`
40
+
41
+ | stopMeasurement Arg Object | Scope | Type | Description | Example |
42
+ | --------------------- | -------- | ------------ | -------------------------------------------------------------------------------- | --------- |
43
+ | vid | Required | String | Unique viewability identifier, referencing an already started viewability tracker. | `"ae0f9"` |
44
+
45
+ ## Please Note:
46
+ - When a tracker needs to be stopped, without direct access to pbjs, postMessage mechanism can be used here as well. To invoke `stopMeasurement`, you provide the payload with `vid`, `message: 'Prebid Viewability'` and `action: 'stopMeasurement`. Check the example bellow.
47
+
48
+ # Examples
49
+
50
+ ## Example of starting a viewability measurement, when you have direct access to pbjs
51
+ ```
52
+ pbjs.viewability.startMeasurement(
53
+ 'ae0f9',
54
+ document.getElementById('test_div'),
55
+ { method: 'img', value: 'http://my.tracker/123' },
56
+ { inViewThreshold: 0.5, timeInView: 1000 }
57
+ );
58
+ ```
59
+
60
+ ## Example of starting a viewability measurement from within a rendered creative
61
+ ```
62
+ let viewabilityRecord = {
63
+ vid: 'ae0f9',
64
+ tracker: { method: 'img', value: 'http://my.tracker/123'},
65
+ criteria: { inViewThreshold: 0.5, timeInView: 1000 },
66
+ message: 'Prebid Viewability',
67
+ action: 'startMeasurement'
68
+ }
69
+
70
+ window.parent.postMessage(JSON.stringify(viewabilityRecord), '*');
71
+ ```
72
+
73
+ ## Example of stopping the viewability measurement, when you have direct access to pbjs
74
+ ```
75
+ pbjs.viewability.stopMeasurement('ae0f9');
76
+ ```
77
+
78
+ ## Example of stopping the viewability measurement from within a rendered creative
79
+ ```
80
+ let viewabilityRecord = {
81
+ vid: 'ae0f9',
82
+ message: 'Prebid Viewability',
83
+ action: 'stopMeasurement'
84
+ }
85
+
86
+ window.parent.postMessage(JSON.stringify(viewabilityRecord), '*');
87
+ ```
@@ -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
 
@@ -55,13 +55,8 @@ export const spec = {
55
55
  p: [],
56
56
  page_url: bidderRequest.refererInfo.referer,
57
57
  bust: new Date().getTime().toString(),
58
- pr: (LOCAL_WINDOW.document && LOCAL_WINDOW.document.referrer) || '',
59
- scrd: LOCAL_WINDOW.devicePixelRatio || 0,
60
58
  dnt: getDNT(),
61
59
  description: getPageDescription(),
62
- title: LOCAL_WINDOW.document.title || '',
63
- w: LOCAL_WINDOW.innerWidth,
64
- h: LOCAL_WINDOW.innerHeight,
65
60
  userConsent: JSON.stringify({
66
61
  // case of undefined, stringify will remove param
67
62
  gdprApplies: deepAccess(bidderRequest, 'gdprConsent.gdprApplies') || '',
@@ -70,6 +65,14 @@ export const spec = {
70
65
  us_privacy: deepAccess(bidderRequest, 'uspConsent') || ''
71
66
  };
72
67
 
68
+ if (canAccessTopWindow()) {
69
+ serverRequest.pr = (LOCAL_WINDOW.document && LOCAL_WINDOW.document.referrer) || '';
70
+ serverRequest.scrd = LOCAL_WINDOW.devicePixelRatio || 0;
71
+ serverRequest.title = LOCAL_WINDOW.document.title || '';
72
+ serverRequest.w = LOCAL_WINDOW.innerWidth;
73
+ serverRequest.h = LOCAL_WINDOW.innerHeight;
74
+ }
75
+
73
76
  const mtp = window.navigator.maxTouchPoints;
74
77
  if (mtp) {
75
78
  serverRequest.mtp = mtp;
@@ -609,3 +612,18 @@ function getEids(bidRequest) {
609
612
  return createEidsArray(bidRequest.userId) || [];
610
613
  }
611
614
  };
615
+
616
+ /**
617
+ * Check if top window can be accessed
618
+ *
619
+ * @return {boolean} true if can access top window otherwise false
620
+ */
621
+ function canAccessTopWindow() {
622
+ try {
623
+ if (getWindowTop().location.href) {
624
+ return true;
625
+ }
626
+ } catch (error) {
627
+ return false;
628
+ }
629
+ }
@@ -0,0 +1,97 @@
1
+ import { logInfo, logError } from '../src/utils.js';
2
+ import { ajax } from '../src/ajax.js';
3
+ import adapterManager from '../src/adapterManager.js';
4
+ import CONSTANTS from '../src/constants.json';
5
+
6
+ import adapter from '../src/AnalyticsAdapter.js';
7
+
8
+ const ZETA_GVL_ID = 833;
9
+ const ADAPTER_CODE = 'zeta_global_ssp';
10
+ const BASE_URL = 'https://ssp.disqus.com/prebid/event';
11
+ const LOG_PREFIX = 'ZetaGlobalSsp-Analytics: ';
12
+
13
+ /// /////////// VARIABLES ////////////////////////////////////
14
+
15
+ let publisherId; // int
16
+
17
+ /// /////////// HELPER FUNCTIONS /////////////////////////////
18
+
19
+ function sendEvent(eventType, event) {
20
+ ajax(
21
+ BASE_URL + '/' + eventType,
22
+ null,
23
+ JSON.stringify(event)
24
+ );
25
+ }
26
+
27
+ /// /////////// ADAPTER EVENT HANDLER FUNCTIONS //////////////
28
+
29
+ function adRenderSucceededHandler(args) {
30
+ let eventType = CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED
31
+ logInfo(LOG_PREFIX + 'handle ' + eventType + ' event');
32
+
33
+ sendEvent(eventType, args);
34
+ }
35
+
36
+ function auctionEndHandler(args) {
37
+ let eventType = CONSTANTS.EVENTS.AUCTION_END;
38
+ logInfo(LOG_PREFIX + 'handle ' + eventType + ' event');
39
+
40
+ sendEvent(eventType, args);
41
+ }
42
+
43
+ /// /////////// ADAPTER DEFINITION ///////////////////////////
44
+
45
+ let baseAdapter = adapter({ analyticsType: 'endpoint' });
46
+ let zetaAdapter = Object.assign({}, baseAdapter, {
47
+
48
+ enableAnalytics(config = {}) {
49
+ let error = false;
50
+
51
+ if (typeof config.options === 'object') {
52
+ if (config.options.sid) {
53
+ publisherId = Number(config.options.sid);
54
+ }
55
+ } else {
56
+ logError(LOG_PREFIX + 'Config not found');
57
+ error = true;
58
+ }
59
+
60
+ if (!publisherId) {
61
+ logError(LOG_PREFIX + 'Missing sid (publisher id)');
62
+ error = true;
63
+ }
64
+
65
+ if (error) {
66
+ logError(LOG_PREFIX + 'Analytics is disabled due to error(s)');
67
+ } else {
68
+ baseAdapter.enableAnalytics.call(this, config);
69
+ }
70
+ },
71
+
72
+ disableAnalytics() {
73
+ publisherId = undefined;
74
+ baseAdapter.disableAnalytics.apply(this, arguments);
75
+ },
76
+
77
+ track({ eventType, args }) {
78
+ switch (eventType) {
79
+ case CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED:
80
+ adRenderSucceededHandler(args);
81
+ break;
82
+ case CONSTANTS.EVENTS.AUCTION_END:
83
+ auctionEndHandler(args);
84
+ break;
85
+ }
86
+ }
87
+ });
88
+
89
+ /// /////////// ADAPTER REGISTRATION /////////////////////////
90
+
91
+ adapterManager.registerAnalyticsAdapter({
92
+ adapter: zetaAdapter,
93
+ code: ADAPTER_CODE,
94
+ gvlid: ZETA_GVL_ID
95
+ });
96
+
97
+ export default zetaAdapter;
@@ -0,0 +1,24 @@
1
+ # Zeta Global SSP Analytics Adapter
2
+
3
+ ## Overview
4
+
5
+ Module Name: Zeta Global SSP Analytics Adapter\
6
+ Module Type: Analytics Adapter\
7
+ Maintainer: abermanov@zetaglobal.com
8
+
9
+ ## Description
10
+
11
+ Analytics Adapter which sends auctionEnd and adRenderSucceeded events to Zeta Global SSP analytics endpoints
12
+
13
+ ## How to configure
14
+ ```
15
+ pbjs.enableAnalytics({
16
+ provider: 'zeta_global_ssp',
17
+ options: {
18
+ sid: 111,
19
+ tags: {
20
+ ...
21
+ }
22
+ }
23
+ });
24
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prebid.js",
3
- "version": "6.4.0",
3
+ "version": "6.8.0",
4
4
  "description": "Header Bidding Management Library",
5
5
  "main": "src/prebid.js",
6
6
  "scripts": {
@@ -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/auction.js CHANGED
@@ -250,10 +250,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
250
250
 
251
251
  let callbacks = auctionCallbacks(auctionDone, this);
252
252
  adapterManager.callBids(_adUnits, bidRequests, function(...args) {
253
- addBidResponse.apply({
254
- dispatch: callbacks.addBidResponse,
255
- bidderRequest: this
256
- }, args)
253
+ callbacks.addBidResponse.apply(this, args);
257
254
  }, callbacks.adapterDone, {
258
255
  request(source, origin) {
259
256
  increment(outstandingRequests, origin);
@@ -344,6 +341,7 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
344
341
  addWinningBid,
345
342
  setBidTargeting,
346
343
  getWinningBids: () => _winningBids,
344
+ getAuctionStart: () => _auctionStart,
347
345
  getTimeout: () => _timeout,
348
346
  getAuctionId: () => _auctionId,
349
347
  getAuctionStatus: () => _auctionStatus,
@@ -355,8 +353,12 @@ export function newAuction({adUnits, adUnitCodes, callback, cbTimeout, labels, a
355
353
  }
356
354
  }
357
355
 
358
- export const addBidResponse = hook('async', function(adUnitCode, bid) {
359
- this.dispatch.call(this.bidderRequest, adUnitCode, bid);
356
+ /**
357
+ * addBidResponse may return a Promise; if it does, the auction will attempt to
358
+ * wait for it to complete (successfully or not) before closing.
359
+ */
360
+ export const addBidResponse = hook('sync', function(adUnitCode, bid) {
361
+ return this.dispatch.call(this.bidderRequest, adUnitCode, bid);
360
362
  }, 'addBidResponse');
361
363
 
362
364
  export const addBidderRequests = hook('sync', function(bidderRequests) {
@@ -374,6 +376,32 @@ export function auctionCallbacks(auctionDone, auctionInstance) {
374
376
  let allAdapterCalledDone = false;
375
377
  let bidderRequestsDone = new Set();
376
378
  let bidResponseMap = {};
379
+ const ready = {};
380
+
381
+ function waitFor(bidderRequest, result) {
382
+ const id = bidderRequest.bidderRequestId;
383
+ if (ready[id] == null) {
384
+ ready[id] = Promise.resolve();
385
+ }
386
+ ready[id] = ready[id].then(() => Promise.resolve(result).catch(() => {}))
387
+ }
388
+
389
+ function guard(bidderRequest, fn) {
390
+ let timeout = bidderRequest.timeout;
391
+ if (timeout == null || timeout > auctionInstance.getTimeout()) {
392
+ timeout = auctionInstance.getTimeout();
393
+ }
394
+ const timeRemaining = auctionInstance.getAuctionStart() + timeout - Date.now();
395
+ const wait = ready[bidderRequest.bidderRequestId];
396
+ if (wait != null && timeRemaining > 0) {
397
+ Promise.race([
398
+ new Promise((resolve) => setTimeout(resolve, timeRemaining)),
399
+ wait
400
+ ]).then(fn);
401
+ } else {
402
+ fn();
403
+ }
404
+ }
377
405
 
378
406
  function afterBidAdded() {
379
407
  outstandingBidsAdded--;
@@ -382,7 +410,7 @@ export function auctionCallbacks(auctionDone, auctionInstance) {
382
410
  }
383
411
  }
384
412
 
385
- function addBidResponse(adUnitCode, bid) {
413
+ function handleBidResponse(adUnitCode, bid) {
386
414
  let bidderRequest = this;
387
415
 
388
416
  bidResponseMap[bid.requestId] = true;
@@ -429,8 +457,15 @@ export function auctionCallbacks(auctionDone, auctionInstance) {
429
457
  }
430
458
 
431
459
  return {
432
- addBidResponse,
433
- adapterDone
460
+ addBidResponse: function (...args) {
461
+ waitFor(this, addBidResponse.apply({
462
+ dispatch: handleBidResponse,
463
+ bidderRequest: this
464
+ }, args));
465
+ },
466
+ adapterDone: function () {
467
+ guard(this, adapterDone.bind(this))
468
+ }
434
469
  }
435
470
  }
436
471
 
package/src/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * Module for getting and setting Prebid configuration.
3
- */
3
+ */
4
4
 
5
5
  /**
6
6
  * @typedef {Object} MediaTypePriceGranularity
@@ -580,7 +580,7 @@ export function newConfig() {
580
580
  .forEach(listener => listener.callback(options));
581
581
  }
582
582
 
583
- function setBidderConfig(config) {
583
+ function setBidderConfig(config, mergeFlag = false) {
584
584
  try {
585
585
  check(config);
586
586
  config.bidders.forEach(bidder => {
@@ -592,7 +592,8 @@ export function newConfig() {
592
592
  let option = (topic === 'fpd') ? convertFpd(config.config[topic]) : config.config[topic];
593
593
 
594
594
  if (isPlainObject(option)) {
595
- bidderConfig[bidder][prop] = Object.assign({}, bidderConfig[bidder][prop] || {}, option);
595
+ const func = mergeFlag ? mergeDeep : Object.assign;
596
+ bidderConfig[bidder][prop] = func({}, bidderConfig[bidder][prop] || {}, option);
596
597
  } else {
597
598
  bidderConfig[bidder][prop] = option;
598
599
  }
@@ -601,6 +602,7 @@ export function newConfig() {
601
602
  } catch (e) {
602
603
  logError(e);
603
604
  }
605
+
604
606
  function check(obj) {
605
607
  if (!isPlainObject(obj)) {
606
608
  throw 'setBidderConfig bidder options must be an object';
@@ -614,6 +616,26 @@ export function newConfig() {
614
616
  }
615
617
  }
616
618
 
619
+ function mergeConfig(obj) {
620
+ if (!isPlainObject(obj)) {
621
+ logError('mergeConfig input must be an object');
622
+ return;
623
+ }
624
+
625
+ const mergedConfig = Object.keys(obj).reduce((accum, key) => {
626
+ const prevConf = _getConfig(key)[key] || {};
627
+ accum[key] = mergeDeep(prevConf, obj[key]);
628
+ return accum;
629
+ }, {});
630
+
631
+ setConfig({ ...mergedConfig });
632
+ return mergedConfig;
633
+ }
634
+
635
+ function mergeBidderConfig(obj) {
636
+ return setBidderConfig(obj, true);
637
+ }
638
+
617
639
  /**
618
640
  * Internal functions for core to execute some synchronous code while having an active bidder set.
619
641
  */
@@ -653,12 +675,14 @@ export function newConfig() {
653
675
  getConfig,
654
676
  readConfig,
655
677
  setConfig,
678
+ mergeConfig,
656
679
  setDefaults,
657
680
  resetConfig,
658
681
  runWithBidder,
659
682
  callbackWithBidder,
660
683
  setBidderConfig,
661
684
  getBidderConfig,
685
+ mergeBidderConfig,
662
686
  convertAdUnitFpd,
663
687
  getLegacyFpd,
664
688
  getLegacyImpFpd
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);