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.
- package/.eslintrc.js +8 -1
- package/integrationExamples/gpt/{haloRtdProvider_example.html → hadronRtdProvider_example.html} +9 -9
- package/integrationExamples/gpt/idImportLibrary_example.html +2 -2
- package/integrationExamples/gpt/userId_example.html +2 -2
- package/integrationExamples/gpt/weboramaRtdProvider_example.html +154 -115
- package/integrationExamples/gpt/x-domain/creative.html +63 -29
- package/modules/.submodules.json +3 -0
- package/modules/adagioBidAdapter.js +0 -8
- package/modules/adagioBidAdapter.md +1 -1
- package/modules/adkernelBidAdapter.js +2 -1
- package/modules/adnuntiusRtdProvider.js +96 -0
- package/modules/adnuntiusRtdProvider.md +41 -0
- package/modules/adotBidAdapter.js +516 -567
- package/modules/adotBidAdapter.md +6 -44
- package/modules/adpod.js +12 -14
- package/modules/adyoulikeBidAdapter.js +2 -0
- package/modules/appnexusBidAdapter.js +14 -2
- package/modules/asealBidAdapter.js +58 -0
- package/modules/asealBidAdapter.md +52 -0
- package/modules/brandmetricsRtdProvider.js +168 -0
- package/modules/brandmetricsRtdProvider.md +40 -0
- package/modules/conversantBidAdapter.js +7 -0
- package/modules/criteoBidAdapter.js +9 -0
- package/modules/currency.js +27 -5
- package/modules/displayioBidAdapter.js +157 -0
- package/modules/displayioBidAdapter.md +148 -0
- package/modules/dspxBidAdapter.js +69 -29
- package/modules/dspxBidAdapter.md +2 -1
- package/modules/e_volutionBidAdapter.js +158 -0
- package/modules/gridBidAdapter.js +15 -1
- package/modules/gumgumBidAdapter.js +52 -38
- package/modules/hadronIdSystem.js +96 -0
- package/modules/hadronIdSystem.md +35 -0
- package/modules/hadronRtdProvider.js +254 -0
- package/modules/hadronRtdProvider.md +126 -0
- package/modules/haloIdSystem.md +4 -35
- package/modules/haloRtdProvider.md +3 -126
- package/modules/imRtdProvider.js +10 -0
- package/modules/improvedigitalBidAdapter.js +5 -0
- package/modules/interactiveOffersBidAdapter.js +9 -6
- package/modules/iqzoneBidAdapter.js +10 -3
- package/modules/iqzoneBidAdapter.md +16 -0
- package/modules/ixBidAdapter.js +2 -6
- package/modules/kubientBidAdapter.js +50 -19
- package/modules/lunamediahbBidAdapter.js +32 -4
- package/modules/malltvBidAdapter.js +7 -3
- package/modules/malltvBidAdapter.md +64 -51
- package/modules/mass.js +3 -5
- package/modules/mediakeysBidAdapter.js +0 -5
- package/modules/medianetAnalyticsAdapter.js +1 -1
- package/modules/mediasquareBidAdapter.js +9 -1
- package/modules/nextMillenniumBidAdapter.js +1 -0
- package/modules/oguryBidAdapter.js +7 -14
- package/modules/prebidServerBidAdapter/index.js +61 -39
- package/modules/priceFloors.js +20 -12
- package/modules/pubmaticBidAdapter.js +1 -1
- package/modules/richaudienceBidAdapter.js +8 -3
- package/modules/riseBidAdapter.js +17 -6
- package/modules/rtbhouseBidAdapter.js +2 -0
- package/modules/rubiconAnalyticsAdapter.js +5 -0
- package/modules/rubiconBidAdapter.js +2 -2
- package/modules/sizeMappingV2.js +1 -8
- package/modules/sortableAnalyticsAdapter.js +5 -4
- package/modules/sovrnBidAdapter.js +93 -18
- package/modules/sovrnBidAdapter.md +80 -2
- package/modules/sspBCBidAdapter.js +53 -20
- package/modules/telariaBidAdapter.js +22 -29
- package/modules/trustpidSystem.js +197 -0
- package/modules/trustpidSystem.md +45 -0
- package/modules/undertoneBidAdapter.js +17 -1
- package/modules/userId/eids.js +16 -1
- package/modules/userId/eids.md +10 -2
- package/modules/userId/userId.md +17 -2
- package/modules/vibrantmediaBidAdapter.js +220 -0
- package/modules/vibrantmediaBidAdapter.md +92 -0
- package/modules/vidoomyBidAdapter.js +8 -0
- package/modules/vidoomyBidAdapter.md +4 -2
- package/modules/weboramaRtdProvider.js +264 -34
- package/modules/weboramaRtdProvider.md +110 -40
- package/modules/yahoosspBidAdapter.js +3 -1
- package/modules/yieldoneBidAdapter.js +6 -0
- package/package.json +2 -1
- package/src/adRendering.js +38 -0
- package/src/adapterManager.js +24 -19
- package/src/adapters/bidderFactory.js +14 -11
- package/src/adloader.js +2 -1
- package/src/auction.js +138 -115
- package/src/auctionIndex.js +85 -0
- package/src/auctionManager.js +3 -0
- package/src/bidderSettings.js +69 -0
- package/src/bidfactory.js +18 -6
- package/src/native.js +29 -21
- package/src/prebid.js +3 -19
- package/src/secureCreatives.js +128 -45
- package/src/targeting.js +11 -2
- package/src/utils.js +14 -17
- package/src/video.js +10 -11
- package/src/videoCache.js +10 -9
- package/test/fixtures/fixtures.js +2 -1
- package/test/helpers/indexStub.js +28 -0
- package/test/helpers/syncPromise.js +71 -0
- package/test/spec/auctionmanager_spec.js +268 -89
- package/test/spec/config_spec.js +24 -1
- package/test/spec/modules/adagioBidAdapter_spec.js +0 -10
- package/test/spec/modules/adnuntiusRtdProvider_spec.js +145 -0
- package/test/spec/modules/adotBidAdapter_spec.js +294 -3124
- package/test/spec/modules/adpod_spec.js +91 -156
- package/test/spec/modules/adyoulikeBidAdapter_spec.js +4 -0
- package/test/spec/modules/appnexusBidAdapter_spec.js +27 -0
- package/test/spec/modules/asealBidAdapter_spec.js +144 -0
- package/test/spec/modules/brandmetricsRtdProvider_spec.js +191 -0
- package/test/spec/modules/conversantBidAdapter_spec.js +54 -2
- package/test/spec/modules/criteoBidAdapter_spec.js +21 -0
- package/test/spec/modules/currency_spec.js +36 -15
- package/test/spec/modules/displayioBidAdapter_spec.js +239 -0
- package/test/spec/modules/dspxBidAdapter_spec.js +20 -15
- package/test/spec/modules/e_volutionBidAdapter_spec.js +242 -0
- package/test/spec/modules/eids_spec.js +2 -2
- package/test/spec/modules/gridBidAdapter_spec.js +18 -0
- package/test/spec/modules/gumgumBidAdapter_spec.js +49 -3
- package/test/spec/modules/hadronIdSystem_spec.js +57 -0
- package/test/spec/modules/hadronRtdProvider_spec.js +762 -0
- package/test/spec/modules/imRtdProvider_spec.js +30 -1
- package/test/spec/modules/improvedigitalBidAdapter_spec.js +19 -0
- package/test/spec/modules/iqzoneBidAdapter_spec.js +1 -0
- package/test/spec/modules/ixBidAdapter_spec.js +1 -1
- package/test/spec/modules/kubientBidAdapter_spec.js +182 -84
- package/test/spec/modules/lunamediahbBidAdapter_spec.js +27 -1
- package/test/spec/modules/mass_spec.js +2 -14
- package/test/spec/modules/mediakeysBidAdapter_spec.js +9 -5
- package/test/spec/modules/mediasquareBidAdapter_spec.js +25 -1
- package/test/spec/modules/oguryBidAdapter_spec.js +63 -5
- package/test/spec/modules/prebidServerBidAdapter_spec.js +43 -6
- package/test/spec/modules/priceFloors_spec.js +83 -24
- package/test/spec/modules/pubmaticBidAdapter_spec.js +40 -0
- package/test/spec/modules/riseBidAdapter_spec.js +30 -4
- package/test/spec/modules/rubiconAnalyticsAdapter_spec.js +31 -1
- package/test/spec/modules/rubiconBidAdapter_spec.js +1 -1
- package/test/spec/modules/sortableAnalyticsAdapter_spec.js +2 -3
- package/test/spec/modules/sovrnBidAdapter_spec.js +413 -333
- package/test/spec/modules/sspBCBidAdapter_spec.js +7 -7
- package/test/spec/modules/telariaBidAdapter_spec.js +1 -3
- package/test/spec/modules/trustpidSystem_spec.js +232 -0
- package/test/spec/modules/undertoneBidAdapter_spec.js +55 -2
- package/test/spec/modules/userId_spec.js +39 -39
- package/test/spec/modules/vibrantmediaBidAdapter_spec.js +1237 -0
- package/test/spec/modules/vidoomyBidAdapter_spec.js +7 -1
- package/test/spec/modules/weboramaRtdProvider_spec.js +536 -20
- package/test/spec/modules/yahoosspBidAdapter_spec.js +10 -0
- package/test/spec/modules/yieldoneBidAdapter_spec.js +33 -0
- package/test/spec/native_spec.js +62 -40
- package/test/spec/unit/core/adapterManager_spec.js +22 -0
- package/test/spec/unit/core/auctionIndex_spec.js +129 -0
- package/test/spec/unit/core/bidderFactory_spec.js +65 -12
- package/test/spec/unit/core/bidderSettings_spec.js +123 -0
- package/test/spec/unit/core/targeting_spec.js +93 -0
- package/test/spec/unit/pbjs_api_spec.js +80 -42
- package/test/spec/unit/secureCreatives_spec.js +143 -24
- package/test/spec/videoCache_spec.js +18 -19
- 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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
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
|
-
|
|
183
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
|
163
|
+
return {
|
|
164
|
+
w: format[0] || null,
|
|
165
|
+
h: format[1] || null
|
|
166
|
+
};
|
|
228
167
|
}
|
|
229
168
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
h: size.height,
|
|
246
|
-
startdelay: startdelay,
|
|
179
|
+
api: video.api,
|
|
180
|
+
w,
|
|
181
|
+
h,
|
|
247
182
|
linearity: video.linearity || null,
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (
|
|
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
|
-
|
|
283
|
-
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
226
|
+
const assets = Object.keys(native)
|
|
227
|
+
.reduce((nativeAssets, assetKey) => {
|
|
228
|
+
const asset = native[assetKey];
|
|
229
|
+
const assetInfo = NATIVE_PLACEMENTS[assetKey];
|
|
300
230
|
|
|
301
|
-
|
|
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
|
-
|
|
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
|
|
247
|
+
const newAsset = {};
|
|
323
248
|
|
|
324
|
-
if (type)
|
|
325
|
-
if (len)
|
|
326
|
-
if (wmin)
|
|
327
|
-
if (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
|
-
|
|
254
|
+
nativeAssets.push({ id, required, [name]: newAsset });
|
|
330
255
|
|
|
331
|
-
return
|
|
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
|
-
|
|
340
|
-
|
|
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
|
-
|
|
366
|
-
|
|
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
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
function
|
|
434
|
-
return
|
|
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
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
function
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
function
|
|
510
|
-
const
|
|
511
|
-
|
|
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
|
-
|
|
515
|
-
|
|
516
|
-
|
|
350
|
+
method: BID_METHOD,
|
|
351
|
+
url: bidderUrl,
|
|
352
|
+
data: buildBidRequest(adUnits, bidderRequest, requestId)
|
|
517
353
|
};
|
|
518
354
|
}
|
|
519
355
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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
|
-
|
|
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 =
|
|
562
|
-
const content = placementName && asset[
|
|
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
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
const
|
|
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:
|
|
437
|
+
adUnitCode: adUnitCode
|
|
616
438
|
});
|
|
617
439
|
|
|
618
440
|
renderer.setRender((ad) => {
|
|
619
441
|
ad.renderer.push(() => {
|
|
620
|
-
const
|
|
621
|
-
? document.querySelector(
|
|
622
|
-
: document.getElementById(
|
|
442
|
+
const domContainer = container
|
|
443
|
+
? document.querySelector(container)
|
|
444
|
+
: document.getElementById(adUnitCode);
|
|
623
445
|
|
|
624
|
-
const player = new window.VASTPlayer(
|
|
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
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
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
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
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
|
-
|
|
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
|
-
|
|
681
|
-
|
|
682
|
-
if (!validateServerResponse(serverResponse)) return [];
|
|
683
|
-
|
|
684
|
-
const bidResponse = serverResponse.body;
|
|
585
|
+
return pbsResponse;
|
|
586
|
+
}, []);
|
|
587
|
+
}
|
|
685
588
|
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
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(
|
|
693
|
-
|
|
694
|
-
export {adotBidderSpec as spec};
|
|
643
|
+
registerBidder(spec);
|