prebid.js 6.7.0 → 6.11.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 (160) hide show
  1. package/.eslintrc.js +8 -1
  2. package/integrationExamples/gpt/{haloRtdProvider_example.html → hadronRtdProvider_example.html} +9 -9
  3. package/integrationExamples/gpt/idImportLibrary_example.html +2 -2
  4. package/integrationExamples/gpt/userId_example.html +2 -2
  5. package/integrationExamples/gpt/weboramaRtdProvider_example.html +154 -115
  6. package/integrationExamples/gpt/x-domain/creative.html +63 -29
  7. package/modules/.submodules.json +3 -0
  8. package/modules/adagioBidAdapter.js +0 -8
  9. package/modules/adagioBidAdapter.md +1 -1
  10. package/modules/adkernelBidAdapter.js +2 -1
  11. package/modules/adnuntiusRtdProvider.js +96 -0
  12. package/modules/adnuntiusRtdProvider.md +41 -0
  13. package/modules/adotBidAdapter.js +516 -567
  14. package/modules/adotBidAdapter.md +6 -44
  15. package/modules/adpod.js +12 -14
  16. package/modules/adyoulikeBidAdapter.js +2 -0
  17. package/modules/appnexusBidAdapter.js +14 -2
  18. package/modules/asealBidAdapter.js +58 -0
  19. package/modules/asealBidAdapter.md +52 -0
  20. package/modules/brandmetricsRtdProvider.js +168 -0
  21. package/modules/brandmetricsRtdProvider.md +40 -0
  22. package/modules/conversantBidAdapter.js +7 -0
  23. package/modules/criteoBidAdapter.js +9 -0
  24. package/modules/currency.js +27 -5
  25. package/modules/displayioBidAdapter.js +157 -0
  26. package/modules/displayioBidAdapter.md +148 -0
  27. package/modules/dspxBidAdapter.js +69 -29
  28. package/modules/dspxBidAdapter.md +2 -1
  29. package/modules/e_volutionBidAdapter.js +158 -0
  30. package/modules/gridBidAdapter.js +15 -1
  31. package/modules/gumgumBidAdapter.js +52 -38
  32. package/modules/hadronIdSystem.js +96 -0
  33. package/modules/hadronIdSystem.md +35 -0
  34. package/modules/hadronRtdProvider.js +254 -0
  35. package/modules/hadronRtdProvider.md +126 -0
  36. package/modules/haloIdSystem.md +4 -35
  37. package/modules/haloRtdProvider.md +3 -126
  38. package/modules/imRtdProvider.js +10 -0
  39. package/modules/improvedigitalBidAdapter.js +5 -0
  40. package/modules/interactiveOffersBidAdapter.js +9 -6
  41. package/modules/iqzoneBidAdapter.js +10 -3
  42. package/modules/iqzoneBidAdapter.md +16 -0
  43. package/modules/ixBidAdapter.js +2 -6
  44. package/modules/kubientBidAdapter.js +50 -19
  45. package/modules/lunamediahbBidAdapter.js +32 -4
  46. package/modules/malltvBidAdapter.js +7 -3
  47. package/modules/malltvBidAdapter.md +64 -51
  48. package/modules/mass.js +3 -5
  49. package/modules/mediakeysBidAdapter.js +0 -5
  50. package/modules/medianetAnalyticsAdapter.js +1 -1
  51. package/modules/mediasquareBidAdapter.js +9 -1
  52. package/modules/nextMillenniumBidAdapter.js +1 -0
  53. package/modules/oguryBidAdapter.js +7 -14
  54. package/modules/prebidServerBidAdapter/index.js +61 -39
  55. package/modules/priceFloors.js +20 -12
  56. package/modules/pubmaticBidAdapter.js +1 -1
  57. package/modules/richaudienceBidAdapter.js +8 -3
  58. package/modules/riseBidAdapter.js +17 -6
  59. package/modules/rtbhouseBidAdapter.js +2 -0
  60. package/modules/rubiconAnalyticsAdapter.js +5 -0
  61. package/modules/rubiconBidAdapter.js +2 -2
  62. package/modules/sizeMappingV2.js +1 -8
  63. package/modules/sortableAnalyticsAdapter.js +5 -4
  64. package/modules/sovrnBidAdapter.js +93 -18
  65. package/modules/sovrnBidAdapter.md +80 -2
  66. package/modules/sspBCBidAdapter.js +53 -20
  67. package/modules/telariaBidAdapter.js +22 -29
  68. package/modules/trustpidSystem.js +197 -0
  69. package/modules/trustpidSystem.md +45 -0
  70. package/modules/undertoneBidAdapter.js +17 -1
  71. package/modules/userId/eids.js +16 -1
  72. package/modules/userId/eids.md +10 -2
  73. package/modules/userId/userId.md +17 -2
  74. package/modules/vibrantmediaBidAdapter.js +220 -0
  75. package/modules/vibrantmediaBidAdapter.md +92 -0
  76. package/modules/vidoomyBidAdapter.js +8 -0
  77. package/modules/vidoomyBidAdapter.md +4 -2
  78. package/modules/weboramaRtdProvider.js +264 -34
  79. package/modules/weboramaRtdProvider.md +110 -40
  80. package/modules/yahoosspBidAdapter.js +3 -1
  81. package/modules/yieldoneBidAdapter.js +6 -0
  82. package/package.json +2 -1
  83. package/src/adRendering.js +38 -0
  84. package/src/adapterManager.js +24 -19
  85. package/src/adapters/bidderFactory.js +14 -11
  86. package/src/adloader.js +2 -1
  87. package/src/auction.js +138 -115
  88. package/src/auctionIndex.js +85 -0
  89. package/src/auctionManager.js +3 -0
  90. package/src/bidderSettings.js +69 -0
  91. package/src/bidfactory.js +18 -6
  92. package/src/native.js +29 -21
  93. package/src/prebid.js +3 -19
  94. package/src/secureCreatives.js +128 -45
  95. package/src/targeting.js +11 -2
  96. package/src/utils.js +14 -17
  97. package/src/video.js +10 -11
  98. package/src/videoCache.js +10 -9
  99. package/test/fixtures/fixtures.js +2 -1
  100. package/test/helpers/indexStub.js +28 -0
  101. package/test/helpers/syncPromise.js +71 -0
  102. package/test/spec/auctionmanager_spec.js +268 -89
  103. package/test/spec/config_spec.js +24 -1
  104. package/test/spec/modules/adagioBidAdapter_spec.js +0 -10
  105. package/test/spec/modules/adnuntiusRtdProvider_spec.js +145 -0
  106. package/test/spec/modules/adotBidAdapter_spec.js +294 -3124
  107. package/test/spec/modules/adpod_spec.js +91 -156
  108. package/test/spec/modules/adyoulikeBidAdapter_spec.js +4 -0
  109. package/test/spec/modules/appnexusBidAdapter_spec.js +27 -0
  110. package/test/spec/modules/asealBidAdapter_spec.js +144 -0
  111. package/test/spec/modules/brandmetricsRtdProvider_spec.js +191 -0
  112. package/test/spec/modules/conversantBidAdapter_spec.js +54 -2
  113. package/test/spec/modules/criteoBidAdapter_spec.js +21 -0
  114. package/test/spec/modules/currency_spec.js +36 -15
  115. package/test/spec/modules/displayioBidAdapter_spec.js +239 -0
  116. package/test/spec/modules/dspxBidAdapter_spec.js +20 -15
  117. package/test/spec/modules/e_volutionBidAdapter_spec.js +242 -0
  118. package/test/spec/modules/eids_spec.js +2 -2
  119. package/test/spec/modules/gridBidAdapter_spec.js +18 -0
  120. package/test/spec/modules/gumgumBidAdapter_spec.js +49 -3
  121. package/test/spec/modules/hadronIdSystem_spec.js +57 -0
  122. package/test/spec/modules/hadronRtdProvider_spec.js +762 -0
  123. package/test/spec/modules/imRtdProvider_spec.js +30 -1
  124. package/test/spec/modules/improvedigitalBidAdapter_spec.js +19 -0
  125. package/test/spec/modules/iqzoneBidAdapter_spec.js +1 -0
  126. package/test/spec/modules/ixBidAdapter_spec.js +1 -1
  127. package/test/spec/modules/kubientBidAdapter_spec.js +182 -84
  128. package/test/spec/modules/lunamediahbBidAdapter_spec.js +27 -1
  129. package/test/spec/modules/mass_spec.js +2 -14
  130. package/test/spec/modules/mediakeysBidAdapter_spec.js +9 -5
  131. package/test/spec/modules/mediasquareBidAdapter_spec.js +25 -1
  132. package/test/spec/modules/oguryBidAdapter_spec.js +63 -5
  133. package/test/spec/modules/prebidServerBidAdapter_spec.js +43 -6
  134. package/test/spec/modules/priceFloors_spec.js +83 -24
  135. package/test/spec/modules/pubmaticBidAdapter_spec.js +40 -0
  136. package/test/spec/modules/riseBidAdapter_spec.js +30 -4
  137. package/test/spec/modules/rubiconAnalyticsAdapter_spec.js +31 -1
  138. package/test/spec/modules/rubiconBidAdapter_spec.js +1 -1
  139. package/test/spec/modules/sortableAnalyticsAdapter_spec.js +2 -3
  140. package/test/spec/modules/sovrnBidAdapter_spec.js +413 -333
  141. package/test/spec/modules/sspBCBidAdapter_spec.js +7 -7
  142. package/test/spec/modules/telariaBidAdapter_spec.js +1 -3
  143. package/test/spec/modules/trustpidSystem_spec.js +232 -0
  144. package/test/spec/modules/undertoneBidAdapter_spec.js +55 -2
  145. package/test/spec/modules/userId_spec.js +39 -39
  146. package/test/spec/modules/vibrantmediaBidAdapter_spec.js +1237 -0
  147. package/test/spec/modules/vidoomyBidAdapter_spec.js +7 -1
  148. package/test/spec/modules/weboramaRtdProvider_spec.js +536 -20
  149. package/test/spec/modules/yahoosspBidAdapter_spec.js +10 -0
  150. package/test/spec/modules/yieldoneBidAdapter_spec.js +33 -0
  151. package/test/spec/native_spec.js +62 -40
  152. package/test/spec/unit/core/adapterManager_spec.js +22 -0
  153. package/test/spec/unit/core/auctionIndex_spec.js +129 -0
  154. package/test/spec/unit/core/bidderFactory_spec.js +65 -12
  155. package/test/spec/unit/core/bidderSettings_spec.js +123 -0
  156. package/test/spec/unit/core/targeting_spec.js +93 -0
  157. package/test/spec/unit/pbjs_api_spec.js +80 -42
  158. package/test/spec/unit/secureCreatives_spec.js +143 -24
  159. package/test/spec/videoCache_spec.js +18 -19
  160. package/test/spec/video_spec.js +51 -61
@@ -1,313 +1,238 @@
1
1
  import {Renderer} from '../src/Renderer.js';
2
2
  import {registerBidder} from '../src/adapters/bidderFactory.js';
3
3
  import {BANNER, NATIVE, VIDEO} from '../src/mediaTypes.js';
4
- import {isStr, isArray, isNumber, isPlainObject, isBoolean, logError, replaceAuctionPrice} from '../src/utils.js';
4
+ import {isStr, isFn, isArray, isPlainObject, isBoolean, logError, replaceAuctionPrice} from '../src/utils.js';
5
5
  import find from 'core-js-pure/features/array/find.js';
6
6
  import { config } from '../src/config.js';
7
+ import { OUTSTREAM } from '../src/video.js';
7
8
 
8
- const ADAPTER_VERSION = 'v1.0.0';
9
+ const BIDDER_CODE = 'adot';
10
+ const ADAPTER_VERSION = 'v2.0.0';
9
11
  const BID_METHOD = 'POST';
10
12
  const BIDDER_URL = 'https://dsp.adotmob.com/headerbidding{PUBLISHER_PATH}/bidrequest';
11
- const FIRST_PRICE = 1;
12
- const NET_REVENUE = true;
13
- // eslint-disable-next-line no-template-curly-in-string
14
- const AUCTION_PRICE = '${AUCTION_PRICE}';
15
- const TTL = 10;
16
-
17
- const SUPPORTED_VIDEO_CONTEXTS = ['instream', 'outstream'];
18
- const SUPPORTED_INSTREAM_CONTEXTS = ['pre-roll', 'mid-roll', 'post-roll'];
19
- const SUPPORTED_VIDEO_MIMES = ['video/mp4'];
20
- const BID_SUPPORTED_MEDIA_TYPES = ['banner', 'video', 'native'];
21
-
13
+ const REQUIRED_VIDEO_PARAMS = ['mimes', 'minduration', 'maxduration', 'protocols'];
22
14
  const DOMAIN_REGEX = new RegExp('//([^/]*)');
23
- const OUTSTREAM_VIDEO_PLAYER_URL = 'https://adserver.adotmob.com/video/player.min.js';
24
-
15
+ const FIRST_PRICE = 1;
16
+ const IMP_BUILDER = { banner: buildBanner, video: buildVideo, native: buildNative };
25
17
  const NATIVE_PLACEMENTS = {
26
- title: {id: 1, name: 'title'},
27
- icon: {id: 2, type: 1, name: 'img'},
28
- image: {id: 3, type: 3, name: 'img'},
29
- sponsoredBy: {id: 4, name: 'data', type: 1},
30
- body: {id: 5, name: 'data', type: 2},
31
- cta: {id: 6, type: 12, name: 'data'}
18
+ title: { id: 1, name: 'title' },
19
+ icon: { id: 2, type: 1, name: 'img' },
20
+ image: { id: 3, type: 3, name: 'img' },
21
+ sponsoredBy: { id: 4, name: 'data', type: 1 },
22
+ body: { id: 5, name: 'data', type: 2 },
23
+ cta: { id: 6, type: 12, name: 'data' }
32
24
  };
33
- const NATIVE_ID_MAPPING = {1: 'title', 2: 'icon', 3: 'image', 4: 'sponsoredBy', 5: 'body', 6: 'cta'};
34
- const NATIVE_PRESET_FORMATTERS = {
35
- image: formatNativePresetImage
36
- }
37
-
38
- function isNone(value) {
39
- return (value === null) || (value === undefined);
40
- }
41
-
42
- function groupBy(values, key) {
43
- const groups = values.reduce((acc, value) => {
44
- const groupId = value[key];
45
-
46
- if (!acc[groupId]) acc[groupId] = [];
47
- acc[groupId].push(value);
48
-
49
- return acc;
50
- }, {});
51
-
52
- return Object
53
- .keys(groups)
54
- .map(id => ({id, key, values: groups[id]}));
55
- }
56
-
57
- function validateMediaTypes(mediaTypes, allowedMediaTypes) {
58
- if (!isPlainObject(mediaTypes)) return false;
59
- if (!allowedMediaTypes.some(mediaType => mediaType in mediaTypes)) return false;
60
-
61
- if (isBanner(mediaTypes)) {
62
- if (!validateBanner(mediaTypes.banner)) return false;
63
- }
64
-
65
- if (isVideo(mediaTypes)) {
66
- if (!validateVideo(mediaTypes.video)) return false;
25
+ const NATIVE_ID_MAPPING = { 1: 'title', 2: 'icon', 3: 'image', 4: 'sponsoredBy', 5: 'body', 6: 'cta' };
26
+ const OUTSTREAM_VIDEO_PLAYER_URL = 'https://adserver.adotmob.com/video/player.min.js';
27
+ const BID_RESPONSE_NET_REVENUE = true;
28
+ const BID_RESPONSE_TTL = 10;
29
+ const DEFAULT_CURRENCY = 'USD';
30
+
31
+ /**
32
+ * Parse string in plain object
33
+ *
34
+ * @param {string} data
35
+ * @returns {object|null} Parsed object or null
36
+ */
37
+ function tryParse(data) {
38
+ try {
39
+ return JSON.parse(data);
40
+ } catch (err) {
41
+ logError(err);
42
+ return null;
67
43
  }
68
-
69
- return true;
70
- }
71
-
72
- function isBanner(mediaTypes) {
73
- return isPlainObject(mediaTypes) && isPlainObject(mediaTypes.banner);
74
- }
75
-
76
- function isVideo(mediaTypes) {
77
- return isPlainObject(mediaTypes) && 'video' in mediaTypes;
78
- }
79
-
80
- function validateBanner(banner) {
81
- return isPlainObject(banner) &&
82
- isArray(banner.sizes) &&
83
- (banner.sizes.length > 0) &&
84
- banner.sizes.every(validateMediaSizes);
85
44
  }
86
45
 
87
- function validateVideo(video) {
88
- if (!isPlainObject(video)) return false;
89
- if (!isStr(video.context)) return false;
90
- if (SUPPORTED_VIDEO_CONTEXTS.indexOf(video.context) === -1) return false;
91
-
92
- if (!video.playerSize) return true;
93
- if (!isArray(video.playerSize)) return false;
94
-
95
- return video.playerSize.every(validateMediaSizes);
96
- }
97
-
98
- function validateMediaSizes(mediaSize) {
99
- return isArray(mediaSize) &&
100
- (mediaSize.length === 2) &&
101
- mediaSize.every(size => (isNumber(size) && size >= 0));
102
- }
103
-
104
- function validateParameters(parameters, adUnit) {
105
- if (isVideo(adUnit.mediaTypes)) {
106
- if (!isPlainObject(parameters)) return false;
107
- if (!isPlainObject(adUnit.mediaTypes.video)) return false;
108
- if (!validateVideoParameters(parameters.video, adUnit)) return false;
109
- }
110
-
111
- return true;
46
+ /**
47
+ * Extract domain from given url
48
+ *
49
+ * @param {string} url
50
+ * @returns {string|null} Extracted domain
51
+ */
52
+ function extractDomainFromURL(url) {
53
+ if (!url || !isStr(url)) return null;
54
+ const domain = url.match(DOMAIN_REGEX);
55
+ if (isArray(domain) && domain.length === 2) return domain[1];
56
+ return null;
112
57
  }
113
58
 
114
- function validateVideoParameters(videoParams, adUnit) {
115
- const video = adUnit.mediaTypes.video;
116
-
117
- if (!video) return false;
59
+ /**
60
+ * Create and return site OpenRtb object from given bidderRequest
61
+ *
62
+ * @param {BidderRequest} bidderRequest
63
+ * @returns {Site|null} Formatted Site OpenRtb object or null
64
+ */
65
+ function getOpenRTBSiteObject(bidderRequest) {
66
+ if (!bidderRequest || !bidderRequest.refererInfo) return null;
118
67
 
119
- if (!isArray(video.mimes)) return false;
120
- if (video.mimes.length === 0) return false;
121
- if (!video.mimes.every(isStr)) return false;
122
-
123
- if (video.minDuration && !isNumber(video.minDuration)) return false;
124
- if (video.maxDuration && !isNumber(video.maxDuration)) return false;
125
-
126
- if (!isArray(video.protocols)) return false;
127
- if (video.protocols.length === 0) return false;
128
- if (!video.protocols.every(isNumber)) return false;
129
-
130
- if (isInstream(video)) {
131
- if (!videoParams.instreamContext) return false;
132
- if (SUPPORTED_INSTREAM_CONTEXTS.indexOf(videoParams.instreamContext) === -1) return false;
133
- }
134
-
135
- return true;
136
- }
68
+ const domain = extractDomainFromURL(bidderRequest.refererInfo.referer);
69
+ const publisherId = config.getConfig('adot.publisherId');
137
70
 
138
- function validateServerRequest(serverRequest) {
139
- return isPlainObject(serverRequest) &&
140
- isPlainObject(serverRequest.data) &&
141
- isArray(serverRequest.data.imp) &&
142
- isPlainObject(serverRequest._adot_internal) &&
143
- isArray(serverRequest._adot_internal.impressions)
144
- }
71
+ if (!domain) return null;
145
72
 
146
- function createServerRequestFromAdUnits(adUnits, bidRequestId, adUnitContext) {
147
- const publisherPath = config.getConfig('adot.publisherPath') === undefined ? '' : '/' + config.getConfig('adot.publisherPath');
148
73
  return {
149
- method: BID_METHOD,
150
- url: BIDDER_URL.replace('{PUBLISHER_PATH}', publisherPath),
151
- data: generateBidRequestsFromAdUnits(adUnits, bidRequestId, adUnitContext),
152
- _adot_internal: generateAdotInternal(adUnits)
153
- }
154
- }
155
-
156
- function generateAdotInternal(adUnits) {
157
- const impressions = adUnits.reduce((acc, adUnit) => {
158
- const {bidId, mediaTypes, adUnitCode, params} = adUnit;
159
- const base = {bidId, adUnitCode, container: params.video && params.video.container};
160
-
161
- const imps = Object
162
- .keys(mediaTypes)
163
- .reduce((acc, mediaType, index) => {
164
- const data = mediaTypes[mediaType];
165
- const impressionId = `${bidId}_${index}`;
166
-
167
- if (mediaType !== 'banner') return acc.concat({...base, impressionId});
168
-
169
- const bannerImps = data.sizes.map((item, i) => ({...base, impressionId: `${impressionId}_${i}`}));
170
-
171
- return acc.concat(bannerImps);
172
- }, []);
173
-
174
- return acc.concat(imps);
175
- }, []);
176
-
177
- return {impressions};
74
+ page: bidderRequest.refererInfo.referer,
75
+ domain: domain,
76
+ name: domain,
77
+ publisher: {
78
+ id: publisherId
79
+ }
80
+ };
178
81
  }
179
82
 
180
- function generateBidRequestsFromAdUnits(adUnits, bidRequestId, adUnitContext) {
83
+ /**
84
+ * Create and return Device OpenRtb object
85
+ *
86
+ * @returns {Device} Formatted Device OpenRtb object or null
87
+ */
88
+ function getOpenRTBDeviceObject() {
89
+ return { ua: navigator.userAgent, language: navigator.language };
90
+ }
91
+
92
+ /**
93
+ * Create and return User OpenRtb object
94
+ *
95
+ * @param {BidderRequest} bidderRequest
96
+ * @returns {User|null} Formatted User OpenRtb object or null
97
+ */
98
+ function getOpenRTBUserObject(bidderRequest) {
99
+ if (!bidderRequest || !bidderRequest.gdprConsent || !isStr(bidderRequest.gdprConsent.consentString)) return null;
100
+ return { ext: { consent: bidderRequest.gdprConsent.consentString } };
101
+ }
102
+
103
+ /**
104
+ * Create and return Regs OpenRtb object
105
+ *
106
+ * @param {BidderRequest} bidderRequest
107
+ * @returns {Regs|null} Formatted Regs OpenRtb object or null
108
+ */
109
+ function getOpenRTBRegsObject(bidderRequest) {
110
+ if (!bidderRequest || !bidderRequest.gdprConsent || !isBoolean(bidderRequest.gdprConsent.gdprApplies)) return null;
111
+ return { ext: { gdpr: bidderRequest.gdprConsent.gdprApplies } };
112
+ }
113
+
114
+ /**
115
+ * Create and return Ext OpenRtb object
116
+ *
117
+ * @param {BidderRequest} bidderRequest
118
+ * @returns {Ext|null} Formatted Ext OpenRtb object or null
119
+ */
120
+ function getOpenRTBExtObject() {
181
121
  return {
182
- id: bidRequestId,
183
- imp: adUnits.reduce(generateImpressionsFromAdUnit, []),
184
- site: generateSiteFromAdUnitContext(adUnitContext),
185
- device: getDeviceInfo(),
186
- user: getUserInfoFromAdUnitContext(adUnitContext),
187
- regs: getRegulationFromAdUnitContext(adUnitContext),
188
- at: FIRST_PRICE,
189
- ext: generateBidRequestExtension()
122
+ adot: { adapter_version: ADAPTER_VERSION },
123
+ should_use_gzip: true
190
124
  };
191
125
  }
192
126
 
193
- function generateImpressionsFromAdUnit(acc, adUnit) {
194
- const {bidId, mediaTypes, params} = adUnit;
195
- const {placementId} = params;
196
- const pmp = {};
197
- const ext = {placementId};
198
-
199
- if (placementId) pmp.deals = [{id: placementId}]
200
-
201
- const imps = Object
202
- .keys(mediaTypes)
203
- .reduce((acc, mediaType, index) => {
204
- const data = mediaTypes[mediaType];
205
- const impId = `${bidId}_${index}`;
206
-
207
- if (mediaType === 'banner') return acc.concat(generateBannerFromAdUnit(impId, data, params));
208
- if (mediaType === 'video') return acc.concat({id: impId, video: generateVideoFromAdUnit(data, params), pmp, ext});
209
- if (mediaType === 'native') return acc.concat({id: impId, native: generateNativeFromAdUnit(data), pmp, ext});
210
- }, []);
211
-
212
- return acc.concat(imps);
213
- }
214
-
215
- function isImpressionAVideo(impression) {
216
- return isPlainObject(impression) && isPlainObject(impression.video);
127
+ /**
128
+ * Return MediaType from MediaTypes object
129
+ *
130
+ * @param {MediaType} mediaTypes Prebid MediaTypes
131
+ * @returns {string|null} Mediatype or null if not found
132
+ */
133
+ function getMediaType(mediaTypes) {
134
+ if (mediaTypes.banner) return 'banner';
135
+ if (mediaTypes.video) return 'video';
136
+ if (mediaTypes.native) return 'native';
137
+ return null;
217
138
  }
218
139
 
219
- function generateBannerFromAdUnit(impId, data, params) {
220
- const {position, placementId} = params;
221
- const pos = position || 0;
222
- const pmp = {};
223
- const ext = {placementId};
224
-
225
- if (placementId) pmp.deals = [{id: placementId}]
140
+ /**
141
+ * Build OpenRtb imp banner from given bidderRequest and media
142
+ *
143
+ * @param {Banner} banner MediaType Banner Object
144
+ * @param {BidderRequest} bidderRequest
145
+ * @returns {OpenRtbBanner} OpenRtb banner object
146
+ */
147
+ function buildBanner(banner, bidderRequest) {
148
+ const pos = bidderRequest.position || 0;
149
+ const format = (banner.sizes || []).map(([w, h]) => ({ w, h }));
150
+ return { format, pos };
151
+ }
152
+
153
+ /**
154
+ * Build object with w and h value depending on given video media
155
+ *
156
+ * @param {Video} video MediaType Video Object
157
+ * @returns {Object} Size as { w: number; h: number }
158
+ */
159
+ function getVideoSize(video) {
160
+ const sizes = video.playerSize || [];
161
+ const format = sizes.length > 0 ? sizes[0] : [];
226
162
 
227
- return data.sizes.map(([w, h], index) => ({id: `${impId}_${index}`, banner: {format: [{w, h}], w, h, pos}, pmp, ext}));
163
+ return {
164
+ w: format[0] || null,
165
+ h: format[1] || null
166
+ };
228
167
  }
229
168
 
230
- function generateVideoFromAdUnit(data, params) {
231
- const {playerSize} = data;
232
- const video = data
233
-
234
- const hasPlayerSize = isArray(playerSize) && playerSize.length > 0;
235
- const {minDuration, maxDuration, protocols} = video;
236
-
237
- const size = {width: hasPlayerSize ? playerSize[0][0] : null, height: hasPlayerSize ? playerSize[0][1] : null};
238
- const duration = {min: isNumber(minDuration) ? minDuration : null, max: isNumber(maxDuration) ? maxDuration : null};
239
- const startdelay = computeStartDelay(data, params);
169
+ /**
170
+ * Build OpenRtb imp video from given bidderRequest and media
171
+ *
172
+ * @param {Video} video MediaType Video Object
173
+ * @returns {OpenRtbVideo} OpenRtb video object
174
+ */
175
+ function buildVideo(video) {
176
+ const { w, h } = getVideoSize(video);
240
177
 
241
178
  return {
242
- mimes: SUPPORTED_VIDEO_MIMES,
243
- skip: video.skippable || 0,
244
- w: size.width,
245
- h: size.height,
246
- startdelay: startdelay,
179
+ api: video.api,
180
+ w,
181
+ h,
247
182
  linearity: video.linearity || null,
248
- minduration: duration.min,
249
- maxduration: duration.max,
250
- protocols,
251
- api: getApi(protocols),
252
- format: hasPlayerSize ? playerSize.map(s => {
253
- return {w: s[0], h: s[1]};
254
- }) : null,
255
- pos: video.position || 0
183
+ mimes: video.mimes,
184
+ minduration: video.minduration,
185
+ maxduration: video.maxduration,
186
+ placement: video.placement,
187
+ playbackmethod: video.playbackmethod,
188
+ pos: video.position || 0,
189
+ protocols: video.protocols,
190
+ skip: video.skip || 0,
191
+ startdelay: video.startdelay
256
192
  };
257
193
  }
258
194
 
259
- function getApi(protocols) {
260
- let defaultValue = [2];
261
- let listProtocols = [
262
- {key: 'VPAID_1_0', value: 1},
263
- {key: 'VPAID_2_0', value: 2},
264
- {key: 'MRAID_1', value: 3},
265
- {key: 'ORMMA', value: 4},
266
- {key: 'MRAID_2', value: 5},
267
- {key: 'MRAID_3', value: 6},
268
- ];
269
- if (protocols) {
270
- return listProtocols.filter(p => {
271
- return protocols.indexOf(p.key) !== -1;
272
- }).map(p => p.value)
273
- } else {
274
- return defaultValue;
275
- }
276
- }
277
-
278
- function isInstream(video) {
279
- return isPlainObject(video) && (video.context === 'instream');
280
- }
195
+ /**
196
+ * Check if given Native Media is an asset of type Image.
197
+ *
198
+ * Return default native assets if given media is an asset
199
+ * Return given native assets if given media is not an asset
200
+ *
201
+ * @param {NativeMedia} native Native Mediatype
202
+ * @returns {OpenRtbNativeAssets}
203
+ */
204
+ function cleanNativeMedia(native) {
205
+ if (native.type !== 'image') return native;
281
206
 
282
- function isOutstream(video) {
283
- return isPlainObject(video) && (video.startdelay === null)
207
+ return {
208
+ image: { required: true, sizes: native.sizes },
209
+ title: { required: true },
210
+ sponsoredBy: { required: true },
211
+ body: { required: false },
212
+ cta: { required: false },
213
+ icon: { required: false }
214
+ };
284
215
  }
285
216
 
286
- function computeStartDelay(data, params) {
287
- if (isInstream(data)) {
288
- if (params.video.instreamContext === 'pre-roll') return 0;
289
- if (params.video.instreamContext === 'mid-roll') return -1;
290
- if (params.video.instreamContext === 'post-roll') return -2;
291
- }
292
-
293
- return null;
294
- }
217
+ /**
218
+ * Build Native OpenRtb Imp from Native Mediatype
219
+ *
220
+ * @param {NativeMedia} native Native Mediatype
221
+ * @returns {OpenRtbNative}
222
+ */
223
+ function buildNative(native) {
224
+ native = cleanNativeMedia(native);
295
225
 
296
- function generateNativeFromAdUnit(data) {
297
- const {type} = data;
298
- const presetFormatter = type && NATIVE_PRESET_FORMATTERS[data.type];
299
- const nativeFields = presetFormatter ? presetFormatter(data) : data;
226
+ const assets = Object.keys(native)
227
+ .reduce((nativeAssets, assetKey) => {
228
+ const asset = native[assetKey];
229
+ const assetInfo = NATIVE_PLACEMENTS[assetKey];
300
230
 
301
- const assets = Object
302
- .keys(nativeFields)
303
- .reduce((acc, placement) => {
304
- const placementData = nativeFields[placement];
305
- const assetInfo = NATIVE_PLACEMENTS[placement];
231
+ if (!assetInfo) return nativeAssets;
306
232
 
307
- if (!assetInfo) return acc;
233
+ const { id, name, type } = assetInfo;
234
+ const { required, len, sizes = [] } = asset;
308
235
 
309
- const {id, name, type} = assetInfo;
310
- const {required, len, sizes = []} = placementData;
311
236
  let wmin;
312
237
  let hmin;
313
238
 
@@ -319,249 +244,165 @@ function generateNativeFromAdUnit(data) {
319
244
  hmin = sizes[1];
320
245
  }
321
246
 
322
- const content = {};
247
+ const newAsset = {};
323
248
 
324
- if (type) content.type = type;
325
- if (len) content.len = len;
326
- if (wmin) content.wmin = wmin;
327
- if (hmin) content.hmin = hmin;
249
+ if (type) newAsset.type = type;
250
+ if (len) newAsset.len = len;
251
+ if (wmin) newAsset.wmin = wmin;
252
+ if (hmin) newAsset.hmin = hmin;
328
253
 
329
- acc.push({id, required, [name]: content});
254
+ nativeAssets.push({ id, required, [name]: newAsset });
330
255
 
331
- return acc;
256
+ return nativeAssets;
332
257
  }, []);
333
258
 
334
- return {
335
- request: JSON.stringify({assets})
336
- };
259
+ return { request: JSON.stringify({ assets }) };
337
260
  }
338
261
 
339
- function formatNativePresetImage(data) {
340
- const sizes = data.sizes;
262
+ /**
263
+ * Build OpenRtb Imp object from given Adunit and Context
264
+ *
265
+ * @param {AdUnit} adUnit PrebidJS Adunit
266
+ * @param {BidderRequest} bidderRequest PrebidJS Bidder Request
267
+ * @returns {Imp} OpenRtb Impression
268
+ */
269
+ function buildImpFromAdUnit(adUnit, bidderRequest) {
270
+ const { bidId, mediaTypes, params, adUnitCode } = adUnit;
271
+ const mediaType = getMediaType(mediaTypes);
341
272
 
342
- return {
343
- image: {
344
- required: true,
345
- sizes
346
- },
347
- title: {
348
- required: true
349
- },
350
- sponsoredBy: {
351
- required: true
352
- },
353
- body: {
354
- required: false
355
- },
356
- cta: {
357
- required: false
358
- },
359
- icon: {
360
- required: false
361
- }
362
- };
363
- }
273
+ if (!mediaType) return null;
364
274
 
365
- function generateSiteFromAdUnitContext(adUnitContext) {
366
- if (!adUnitContext || !adUnitContext.refererInfo) return null;
367
-
368
- const domain = extractSiteDomainFromURL(adUnitContext.refererInfo.referer);
369
- const publisherId = config.getConfig('adot.publisherId');
370
-
371
- if (!domain) return null;
372
-
373
- return {
374
- page: adUnitContext.refererInfo.referer,
375
- domain: domain,
376
- name: domain,
377
- publisher: {
378
- id: publisherId
379
- }
380
- };
381
- }
382
-
383
- function extractSiteDomainFromURL(url) {
384
- if (!url || !isStr(url)) return null;
385
-
386
- const domain = url.match(DOMAIN_REGEX);
387
-
388
- if (isArray(domain) && domain.length === 2) return domain[1];
389
-
390
- return null;
391
- }
392
-
393
- function getDeviceInfo() {
394
- return {ua: navigator.userAgent, language: navigator.language};
395
- }
396
-
397
- function getUserInfoFromAdUnitContext(adUnitContext) {
398
- if (!adUnitContext || !adUnitContext.gdprConsent) return null;
399
- if (!isStr(adUnitContext.gdprConsent.consentString)) return null;
400
-
401
- return {
402
- ext: {
403
- consent: adUnitContext.gdprConsent.consentString
404
- }
405
- };
406
- }
407
-
408
- function getRegulationFromAdUnitContext(adUnitContext) {
409
- if (!adUnitContext || !adUnitContext.gdprConsent) return null;
410
- if (!isBoolean(adUnitContext.gdprConsent.gdprApplies)) return null;
275
+ const media = IMP_BUILDER[mediaType](mediaTypes[mediaType], bidderRequest, adUnit)
276
+ const currency = config.getConfig('currency.adServerCurrency') || DEFAULT_CURRENCY;
277
+ const bidfloor = getMainFloor(adUnit, media.format, mediaType, currency);
411
278
 
412
279
  return {
280
+ id: bidId,
413
281
  ext: {
414
- gdpr: adUnitContext.gdprConsent.gdprApplies
415
- }
416
- };
417
- }
418
-
419
- function generateBidRequestExtension() {
420
- return {
421
- adot: {adapter_version: ADAPTER_VERSION},
422
- should_use_gzip: true
282
+ placementId: params.placementId,
283
+ adUnitCode,
284
+ container: params.video && params.video.container
285
+ },
286
+ [mediaType]: media,
287
+ bidfloorcur: currency,
288
+ bidfloor
423
289
  };
424
290
  }
425
291
 
426
- function validateServerResponse(serverResponse) {
427
- return isPlainObject(serverResponse) &&
428
- isPlainObject(serverResponse.body) &&
429
- isStr(serverResponse.body.cur) &&
430
- isArray(serverResponse.body.seatbid);
431
- }
432
-
433
- function seatBidsToAds(seatBid, bidResponse, serverRequest) {
434
- return seatBid.bid
435
- .filter(bid => validateBids(bid, serverRequest))
436
- .map(bid => generateAdFromBid(bid, bidResponse, serverRequest));
437
- }
438
-
439
- function validateBids(bid, serverRequest) {
440
- if (!isPlainObject(bid)) return false;
441
- if (!isStr(bid.impid)) return false;
442
- if (!isStr(bid.crid)) return false;
443
- if (!isNumber(bid.price)) return false;
444
-
445
- if (!isPlainObject(bid.ext)) return false;
446
- if (!isPlainObject(bid.ext.adot)) return false;
447
- if (!isStr(bid.ext.adot.media_type)) return false;
448
- if (BID_SUPPORTED_MEDIA_TYPES.indexOf(bid.ext.adot.media_type) === -1) return false;
449
-
450
- if (!bid.adm && !bid.nurl) return false;
451
- if (bid.adm) {
452
- if (!isStr(bid.adm)) return false;
453
- if (bid.adm.indexOf(AUCTION_PRICE) === -1) return false;
454
- }
455
- if (bid.nurl) {
456
- if (!isStr(bid.nurl)) return false;
457
- if (bid.nurl.indexOf(AUCTION_PRICE) === -1) return false;
458
- }
459
-
460
- if (isBidABanner(bid)) {
461
- if (!isNumber(bid.h)) return false;
462
- if (!isNumber(bid.w)) return false;
463
- }
464
- if (isBidAVideo(bid)) {
465
- if (!(isNone(bid.h) || isNumber(bid.h))) return false;
466
- if (!(isNone(bid.w) || isNumber(bid.w))) return false;
467
- }
468
-
469
- const impression = getImpressionData(serverRequest, bid.impid);
470
-
471
- if (!isPlainObject(impression.openRTB)) return false;
472
- if (!isPlainObject(impression.internal)) return false;
473
- if (!isStr(impression.internal.adUnitCode)) return false;
474
-
475
- if (isBidABanner(bid)) {
476
- if (!isPlainObject(impression.openRTB.banner)) return false;
477
- }
478
- if (isBidAVideo(bid)) {
479
- if (!isPlainObject(impression.openRTB.video)) return false;
480
- }
481
- if (isBidANative(bid)) {
482
- if (!isPlainObject(impression.openRTB.native) || !tryParse(bid.adm)) return false;
483
- }
484
-
292
+ /**
293
+ * Return if given video is Valid.
294
+ * A video is defined as valid if it contains all required fields
295
+ *
296
+ * @param {VideoMedia} video
297
+ * @returns {boolean}
298
+ */
299
+ function isValidVideo(video) {
300
+ if (REQUIRED_VIDEO_PARAMS.some((param) => video[param] === undefined)) return false;
485
301
  return true;
486
302
  }
487
303
 
488
- function isBidABanner(bid) {
489
- return isPlainObject(bid) &&
490
- isPlainObject(bid.ext) &&
491
- isPlainObject(bid.ext.adot) &&
492
- bid.ext.adot.media_type === 'banner';
493
- }
494
-
495
- function isBidAVideo(bid) {
496
- return isPlainObject(bid) &&
497
- isPlainObject(bid.ext) &&
498
- isPlainObject(bid.ext.adot) &&
499
- bid.ext.adot.media_type === 'video';
500
- }
501
-
502
- function isBidANative(bid) {
503
- return isPlainObject(bid) &&
504
- isPlainObject(bid.ext) &&
505
- isPlainObject(bid.ext.adot) &&
506
- bid.ext.adot.media_type === 'native';
507
- }
508
-
509
- function getImpressionData(serverRequest, impressionId) {
510
- const openRTBImpression = find(serverRequest.data.imp, imp => imp.id === impressionId);
511
- const internalImpression = find(serverRequest._adot_internal.impressions, imp => imp.impressionId === impressionId);
512
-
304
+ /**
305
+ * Return if given bid is Valid.
306
+ * A bid is defined as valid if it media is a valid video or other media
307
+ *
308
+ * @param {Bid} bid
309
+ * @returns {boolean}
310
+ */
311
+ function isBidRequestValid(bid) {
312
+ const video = bid.mediaTypes.video;
313
+ return !video || isValidVideo(video);
314
+ }
315
+
316
+ /**
317
+ * Build OpenRtb request from Prebid AdUnits and Bidder request
318
+ *
319
+ * @param {Array<AdUnit>} adUnits Array of PrebidJS Adunit
320
+ * @param {BidderRequest} bidderRequest PrebidJS BidderRequest
321
+ * @param {string} requestId Request ID
322
+ *
323
+ * @returns {OpenRTBBidRequest} OpenRTB bid request
324
+ */
325
+ function buildBidRequest(adUnits, bidderRequest, requestId) {
326
+ const data = {
327
+ id: requestId,
328
+ imp: adUnits.map((adUnit) => buildImpFromAdUnit(adUnit, bidderRequest)).filter((item) => !!item),
329
+ site: getOpenRTBSiteObject(bidderRequest),
330
+ device: getOpenRTBDeviceObject(),
331
+ user: getOpenRTBUserObject(bidderRequest),
332
+ regs: getOpenRTBRegsObject(bidderRequest),
333
+ ext: getOpenRTBExtObject(),
334
+ at: FIRST_PRICE
335
+ };
336
+ return data;
337
+ }
338
+
339
+ /**
340
+ * Build PrebidJS Ajax request
341
+ *
342
+ * @param {Array<AdUnit>} adUnits Array of PrebidJS Adunit
343
+ * @param {BidderRequest} bidderRequest PrebidJS BidderRequest
344
+ * @param {string} bidderUrl Adot Bidder URL
345
+ * @param {string} requestId Request ID
346
+ * @returns
347
+ */
348
+ function buildAjaxRequest(adUnits, bidderRequest, bidderUrl, requestId) {
513
349
  return {
514
- id: impressionId,
515
- openRTB: openRTBImpression || null,
516
- internal: internalImpression || null
350
+ method: BID_METHOD,
351
+ url: bidderUrl,
352
+ data: buildBidRequest(adUnits, bidderRequest, requestId)
517
353
  };
518
354
  }
519
355
 
520
- function generateAdFromBid(bid, bidResponse, serverRequest) {
521
- const impressionData = getImpressionData(serverRequest, bid.impid);
522
- const isVideo = isBidAVideo(bid);
523
- const base = {
524
- requestId: impressionData.internal.bidId,
525
- cpm: bid.price,
526
- currency: bidResponse.cur,
527
- ttl: TTL,
528
- creativeId: bid.crid,
529
- netRevenue: NET_REVENUE,
530
- mediaType: bid.ext.adot.media_type,
531
- };
532
-
533
- if (bid.adomain) {
534
- base.meta = { advertiserDomains: bid.adomain };
535
- }
536
-
537
- if (isBidANative(bid)) return {...base, native: formatNativeData(bid)};
538
-
539
- const size = getSizeFromBid(bid, impressionData);
540
- const creative = getCreativeFromBid(bid, impressionData);
541
-
542
- return {
543
- ...base,
544
- height: size.height,
545
- width: size.width,
546
- ad: creative.markup,
547
- adUrl: creative.markupUrl,
548
- vastXml: isVideo && !isStr(creative.markupUrl) ? creative.markup : null,
549
- vastUrl: isVideo && isStr(creative.markupUrl) ? creative.markupUrl : null,
550
- renderer: creative.renderer
551
- };
356
+ /**
357
+ * Split given PrebidJS Request in Dictionnary
358
+ *
359
+ * @param {Array<PrebidBidRequest>} validBidRequests
360
+ * @returns {Dictionnary<PrebidBidRequest>}
361
+ */
362
+ function splitAdUnits(validBidRequests) {
363
+ return validBidRequests.reduce((adUnits, adUnit) => {
364
+ const bidderRequestId = adUnit.bidderRequestId;
365
+ if (!adUnits[bidderRequestId]) {
366
+ adUnits[bidderRequestId] = [];
367
+ }
368
+ adUnits[bidderRequestId].push(adUnit);
369
+ return adUnits;
370
+ }, {});
552
371
  }
553
372
 
554
- function formatNativeData({adm, price}) {
373
+ /**
374
+ * Build Ajax request Array
375
+ *
376
+ * @param {Array<PrebidBidRequest>} validBidRequests
377
+ * @param {BidderRequest} bidderRequest
378
+ * @returns {Array<AjaxRequest>}
379
+ */
380
+ function buildRequests(validBidRequests, bidderRequest) {
381
+ const adUnits = splitAdUnits(validBidRequests);
382
+ const publisherPathConfig = config.getConfig('adot.publisherPath');
383
+ const publisherPath = publisherPathConfig === undefined ? '' : '/' + publisherPathConfig;
384
+ const bidderUrl = BIDDER_URL.replace('{PUBLISHER_PATH}', publisherPath);
385
+
386
+ return Object.keys(adUnits).map((requestId) => buildAjaxRequest(adUnits[requestId], bidderRequest, bidderUrl, requestId));
387
+ }
388
+
389
+ /**
390
+ * Build Native PrebidJS Response grom OpenRtb Response
391
+ *
392
+ * @param {OpenRtbBid} bid
393
+ *
394
+ * @returns {NativeAssets} Native PrebidJS
395
+ */
396
+ function buildNativeBidData(bid) {
397
+ const { adm, price } = bid;
555
398
  const parsedAdm = tryParse(adm);
556
- const {assets, link: {url, clicktrackers}, imptrackers, jstracker} = parsedAdm.native;
557
- const placements = NATIVE_PLACEMENTS;
558
- const placementIds = NATIVE_ID_MAPPING;
399
+ const { assets, link: { url, clicktrackers }, imptrackers, jstracker } = parsedAdm.native;
559
400
 
560
401
  return assets.reduce((acc, asset) => {
561
- const placementName = placementIds[asset.id];
562
- const content = placementName && asset[placements[placementName].name];
402
+ const placementName = NATIVE_ID_MAPPING[asset.id];
403
+ const content = placementName && asset[NATIVE_PLACEMENTS[placementName].name];
563
404
  if (!content) return acc;
564
- acc[placementName] = content.text || content.value || {url: content.url, width: content.w, height: content.h};
405
+ acc[placementName] = content.text || content.value || { url: content.url, width: content.w, height: content.h };
565
406
  return acc;
566
407
  }, {
567
408
  clickUrl: url,
@@ -571,57 +412,38 @@ function formatNativeData({adm, price}) {
571
412
  });
572
413
  }
573
414
 
574
- function getSizeFromBid(bid, impressionData) {
575
- if (isNumber(bid.w) && isNumber(bid.h)) {
576
- return { width: bid.w, height: bid.h };
577
- }
578
-
579
- if (isImpressionAVideo(impressionData.openRTB)) {
580
- const { video } = impressionData.openRTB;
581
-
582
- if (isNumber(video.w) && isNumber(video.h)) {
583
- return { width: video.w, height: video.h };
584
- }
585
- }
586
-
587
- return { width: null, height: null };
588
- }
589
-
590
- function getCreativeFromBid(bid, impressionData) {
591
- const shouldUseAdMarkup = !!bid.adm;
592
- const price = bid.price;
415
+ /**
416
+ * Return Adot Renderer if given Bid is a video one
417
+ *
418
+ * @param {OpenRtbBid} bid
419
+ * @param {string} mediaType
420
+ * @returns {any|null}
421
+ */
422
+ function buildRenderer(bid, mediaType) {
423
+ if (!(mediaType === VIDEO &&
424
+ bid.ext &&
425
+ bid.ext.adot &&
426
+ bid.ext.adot.container &&
427
+ bid.ext.adot.adUnitCode &&
428
+ bid.ext.adot.video &&
429
+ bid.ext.adot.video.type === OUTSTREAM)) return null;
430
+
431
+ const container = bid.ext.adot.container
432
+ const adUnitCode = bid.ext.adot.adUnitCode
593
433
 
594
- return {
595
- markup: shouldUseAdMarkup ? replaceAuctionPrice(bid.adm, price) : null,
596
- markupUrl: !shouldUseAdMarkup ? replaceAuctionPrice(bid.nurl, price) : null,
597
- renderer: getRendererFromBid(bid, impressionData)
598
- };
599
- }
600
-
601
- function getRendererFromBid(bid, impressionData) {
602
- const isOutstreamImpression = isBidAVideo(bid) &&
603
- isImpressionAVideo(impressionData.openRTB) &&
604
- isOutstream(impressionData.openRTB.video);
605
-
606
- return isOutstreamImpression
607
- ? buildOutstreamRenderer(impressionData)
608
- : null;
609
- }
610
-
611
- function buildOutstreamRenderer(impressionData) {
612
434
  const renderer = Renderer.install({
613
435
  url: OUTSTREAM_VIDEO_PLAYER_URL,
614
436
  loaded: false,
615
- adUnitCode: impressionData.internal.adUnitCode
437
+ adUnitCode: adUnitCode
616
438
  });
617
439
 
618
440
  renderer.setRender((ad) => {
619
441
  ad.renderer.push(() => {
620
- const container = impressionData.internal.container
621
- ? document.querySelector(impressionData.internal.container)
622
- : document.getElementById(impressionData.internal.adUnitCode);
442
+ const domContainer = container
443
+ ? document.querySelector(container)
444
+ : document.getElementById(adUnitCode);
623
445
 
624
- const player = new window.VASTPlayer(container);
446
+ const player = new window.VASTPlayer(domContainer);
625
447
 
626
448
  player.on('ready', () => {
627
449
  player.adVolume = 0;
@@ -641,54 +463,181 @@ function buildOutstreamRenderer(impressionData) {
641
463
  return renderer;
642
464
  }
643
465
 
644
- function tryParse(data) {
645
- try {
646
- return JSON.parse(data);
647
- } catch (err) {
648
- logError(err);
649
- return null;
650
- }
466
+ /**
467
+ * Build PrebidJS response from OpenRtbBid
468
+ *
469
+ * @param {OpenRtbBid} bid
470
+ * @param {string} mediaType
471
+ * @returns {Object}
472
+ */
473
+ function buildCreativeBidData(bid, mediaType) {
474
+ const adm = bid.adm ? replaceAuctionPrice(bid.adm, bid.price) : null;
475
+ const nurl = (!bid.adm && bid.nurl) ? replaceAuctionPrice(bid.nurl, bid.price) : null;
476
+
477
+ return {
478
+ width: bid.ext.adot.size && bid.ext.adot.size.w,
479
+ height: bid.ext.adot.size && bid.ext.adot.size.h,
480
+ ad: adm,
481
+ adUrl: nurl,
482
+ vastXml: mediaType === VIDEO && !isStr(nurl) ? adm : null,
483
+ vastUrl: mediaType === VIDEO && isStr(nurl) ? nurl : null,
484
+ renderer: buildRenderer(bid, mediaType)
485
+ };
651
486
  }
652
487
 
653
- const adotBidderSpec = {
654
- code: 'adot',
655
- supportedMediaTypes: [BANNER, VIDEO, NATIVE],
656
- isBidRequestValid(adUnit) {
657
- const allowedBidderCodes = [this.code];
658
-
659
- return isPlainObject(adUnit) &&
660
- allowedBidderCodes.indexOf(adUnit.bidder) !== -1 &&
661
- isStr(adUnit.adUnitCode) &&
662
- isStr(adUnit.bidderRequestId) &&
663
- isStr(adUnit.bidId) &&
664
- validateMediaTypes(adUnit.mediaTypes, this.supportedMediaTypes) &&
665
- validateParameters(adUnit.params, adUnit);
666
- },
667
- buildRequests(adUnits, adUnitContext) {
668
- if (!adUnits) return null;
669
-
670
- return groupBy(adUnits, 'bidderRequestId').map(group => {
671
- const bidRequestId = group.id;
672
- const adUnits = groupBy(group.values, 'bidId').map((group) => {
673
- const length = group.values.length;
674
- return length > 0 && group.values[length - 1]
675
- });
488
+ /**
489
+ * Return if given bid and imp are valid
490
+ *
491
+ * @param {OpenRtbBid} bid OpenRtb Bid
492
+ * @param {Imp} imp OpenRtb Imp
493
+ * @returns {boolean}
494
+ */
495
+ function isBidImpInvalid(bid, imp) {
496
+ return !bid || !imp;
497
+ }
498
+
499
+ /**
500
+ * Build PrebidJS Bid Response from given OpenRTB Bid
501
+ *
502
+ * @param {OpenRtbBid} bid
503
+ * @param {OpenRtbBidResponse} bidResponse
504
+ * @param {OpenRtbBid} imp
505
+ * @returns {PrebidJSResponse}
506
+ */
507
+ function buildBidResponse(bid, bidResponse, imp) {
508
+ if (isBidImpInvalid(bid, imp)) return null;
509
+ const mediaType = bid.ext.adot.media_type;
510
+ const baseBid = {
511
+ requestId: bid.impid,
512
+ cpm: bid.price,
513
+ currency: bidResponse.cur,
514
+ ttl: BID_RESPONSE_TTL,
515
+ creativeId: bid.crid,
516
+ netRevenue: BID_RESPONSE_NET_REVENUE,
517
+ mediaType
518
+ };
676
519
 
677
- return createServerRequestFromAdUnits(adUnits, bidRequestId, adUnitContext)
520
+ if (bid.dealid) baseBid.dealId = bid.dealid;
521
+ if (bid.adomain) baseBid.meta = { advertiserDomains: bid.adomain };
522
+
523
+ if (mediaType === NATIVE) return { ...baseBid, native: buildNativeBidData(bid) };
524
+ return { ...baseBid, ...buildCreativeBidData(bid, mediaType) };
525
+ }
526
+
527
+ /**
528
+ * Find OpenRtb Imp from request with same id that given bid
529
+ *
530
+ * @param {OpenRtbBid} bid
531
+ * @param {OpenRtbRequest} bidRequest
532
+ * @returns {Imp} OpenRtb Imp
533
+ */
534
+ function getImpfromBid(bid, bidRequest) {
535
+ if (!bidRequest || !bidRequest.imp) return null;
536
+ const imps = bidRequest.imp;
537
+ return find(imps, (imp) => imp.id === bid.impid);
538
+ }
539
+
540
+ /**
541
+ * Return if given response is valid
542
+ *
543
+ * @param {OpenRtbBidResponse} response
544
+ * @returns {boolean}
545
+ */
546
+ function isValidResponse(response) {
547
+ return isPlainObject(response) &&
548
+ isPlainObject(response.body) &&
549
+ isStr(response.body.cur) &&
550
+ isArray(response.body.seatbid);
551
+ }
552
+
553
+ /**
554
+ * Return if given request is valid
555
+ *
556
+ * @param {OpenRtbRequest} request
557
+ * @returns {boolean}
558
+ */
559
+ function isValidRequest(request) {
560
+ return isPlainObject(request) &&
561
+ isPlainObject(request.data) &&
562
+ isArray(request.data.imp);
563
+ }
564
+
565
+ /**
566
+ * Interpret given OpenRtb Response to build PrebidJS Response
567
+ *
568
+ * @param {OpenRtbBidResponse} serverResponse
569
+ * @param {OpenRtbRequest} request
570
+ * @returns {PrebidJSResponse}
571
+ */
572
+ function interpretResponse(serverResponse, request) {
573
+ if (!isValidResponse(serverResponse) || !isValidRequest(request)) return [];
574
+
575
+ const bidsResponse = serverResponse.body;
576
+ const bidRequest = request.data;
577
+
578
+ return bidsResponse.seatbid.reduce((pbsResponse, seatbid) => {
579
+ if (!seatbid || !isArray(seatbid.bid)) return pbsResponse;
580
+ seatbid.bid.forEach((bid) => {
581
+ const imp = getImpfromBid(bid, bidRequest);
582
+ const bidResponse = buildBidResponse(bid, bidsResponse, imp);
583
+ if (bidResponse) pbsResponse.push(bidResponse);
678
584
  });
679
- },
680
- interpretResponse(serverResponse, serverRequest) {
681
- if (!validateServerRequest(serverRequest)) return [];
682
- if (!validateServerResponse(serverResponse)) return [];
683
-
684
- const bidResponse = serverResponse.body;
585
+ return pbsResponse;
586
+ }, []);
587
+ }
685
588
 
686
- return bidResponse.seatbid
687
- .filter(seatBid => isPlainObject(seatBid) && isArray(seatBid.bid))
688
- .reduce((acc, seatBid) => acc.concat(seatBidsToAds(seatBid, bidResponse, serverRequest)), []);
689
- }
589
+ /**
590
+ * Call Adunit getFloor function with given argument to get specific floor.
591
+ * Return 0 by default
592
+ *
593
+ * @param {AdUnit} adUnit
594
+ * @param {Array<number>|string} size Adunit size or *
595
+ * @param {string} mediaType
596
+ * @param {string} currency USD by default
597
+ *
598
+ * @returns {number} Floor price
599
+ */
600
+ function getFloor(adUnit, size, mediaType, currency) {
601
+ if (!isFn(adUnit.getFloor)) return 0;
602
+
603
+ const floorResult = adUnit.getFloor({ currency, mediaType, size });
604
+
605
+ return floorResult.currency === currency ? floorResult.floor : 0;
606
+ }
607
+
608
+ /**
609
+ * Call getFloor for each format and return the lower floor
610
+ * Return 0 by default
611
+ *
612
+ * interface Format { w: number; h: number }
613
+ *
614
+ * @param {AdUnit} adUnit
615
+ * @param {Array<Format>} formats Media formats
616
+ * @param {string} mediaType
617
+ * @param {string} currency USD by default
618
+ *
619
+ * @returns {number} Lower floor.
620
+ */
621
+ function getMainFloor(adUnit, formats, mediaType, currency) {
622
+ if (!formats) return getFloor(adUnit, '*', mediaType, currency);
623
+
624
+ return formats.reduce((bidFloor, format) => {
625
+ const floor = getFloor(adUnit, [format.w, format.h], mediaType, currency)
626
+ const maxFloor = bidFloor || Number.MAX_SAFE_INTEGER;
627
+ return floor !== 0 && floor < maxFloor ? floor : bidFloor;
628
+ }, null) || 0;
629
+ }
630
+
631
+ /**
632
+ * Adot PrebidJS Adapter
633
+ */
634
+ export const spec = {
635
+ code: BIDDER_CODE,
636
+ supportedMediaTypes: [BANNER, NATIVE, VIDEO],
637
+ isBidRequestValid,
638
+ buildRequests,
639
+ interpretResponse,
640
+ getFloor
690
641
  };
691
642
 
692
- registerBidder(adotBidderSpec);
693
-
694
- export {adotBidderSpec as spec};
643
+ registerBidder(spec);