prebid.js 7.29.0 → 7.31.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/dist/33acrossBidAdapter.js +1 -1
- package/dist/33acrossIdSystem.js +1 -1
- package/dist/adagioBidAdapter.js +1 -1
- package/dist/adbookpspBidAdapter.js +1 -1
- package/dist/adgenerationBidAdapter.js +1 -1
- package/dist/adkernelBidAdapter.js +1 -1
- package/dist/adrelevantisBidAdapter.js +1 -1
- package/dist/adtrgtmeBidAdapter.js +1 -1
- package/dist/adxcgBidAdapter.js +1 -1
- package/dist/ajaBidAdapter.js +1 -1
- package/dist/amxBidAdapter.js +1 -1
- package/dist/amxIdSystem.js +1 -1
- package/dist/appierAnalyticsAdapter.js +1 -1
- package/dist/appnexusBidAdapter.js +1 -1
- package/dist/asoBidAdapter.js +1 -1
- package/dist/axonixBidAdapter.js +1 -1
- package/dist/bidViewability.js +1 -1
- package/dist/bidglassBidAdapter.js +1 -1
- package/dist/big-richmediaBidAdapter.js +1 -1
- package/dist/bridgewellBidAdapter.js +1 -1
- package/dist/brightMountainMediaBidAdapter.js +1 -1
- package/dist/byDataAnalyticsAdapter.js +1 -1
- package/dist/carodaBidAdapter.js +1 -1
- package/dist/ccxBidAdapter.js +1 -1
- package/dist/chtnwBidAdapter.js +1 -1
- package/dist/colossussspBidAdapter.js +1 -1
- package/dist/concertBidAdapter.js +1 -1
- package/dist/confiantRtdProvider.js +1 -0
- package/dist/connectadBidAdapter.js +1 -1
- package/dist/consentManagement.js +1 -1
- package/dist/consentManagementGpp.js +1 -0
- package/dist/consumableBidAdapter.js +1 -1
- package/dist/conversantBidAdapter.js +1 -1
- package/dist/craftBidAdapter.js +1 -1
- package/dist/criteoBidAdapter.js +1 -1
- package/dist/datawrkzBidAdapter.js +1 -1
- package/dist/dfpAdServerVideo.js +1 -1
- package/dist/dspxBidAdapter.js +1 -1
- package/dist/eplanningBidAdapter.js +1 -1
- package/dist/feedadBidAdapter.js +1 -1
- package/dist/finativeBidAdapter.js +1 -1
- package/dist/glimpseBidAdapter.js +1 -1
- package/dist/gmosspBidAdapter.js +1 -1
- package/dist/goldbachBidAdapter.js +1 -1
- package/dist/gridBidAdapter.js +1 -1
- package/dist/gridNMBidAdapter.js +1 -1
- package/dist/gumgumBidAdapter.js +1 -1
- package/dist/h12mediaBidAdapter.js +1 -1
- package/dist/id5IdSystem.js +1 -1
- package/dist/improvedigitalBidAdapter.js +1 -1
- package/dist/inmarBidAdapter.js +1 -1
- package/dist/insticatorBidAdapter.js +1 -1
- package/dist/ixBidAdapter.js +1 -1
- package/dist/justpremiumBidAdapter.js +1 -1
- package/dist/konduitAnalyticsAdapter.js +1 -1
- package/dist/kueezBidAdapter.js +1 -1
- package/dist/kueezRtbBidAdapter.js +1 -1
- package/dist/lassoBidAdapter.js +1 -1
- package/dist/lifestreetBidAdapter.js +1 -1
- package/dist/liveyieldAnalyticsAdapter.js +1 -1
- package/dist/logicadBidAdapter.js +1 -1
- package/dist/loglyliftBidAdapter.js +1 -1
- package/dist/magniteAnalyticsAdapter.js +1 -1
- package/dist/malltvAnalyticsAdapter.js +1 -1
- package/dist/marsmediaBidAdapter.js +1 -1
- package/dist/mediafuseBidAdapter.js +1 -1
- package/dist/mediasquareBidAdapter.js +1 -1
- package/dist/mgidBidAdapter.js +1 -1
- package/dist/minutemediaBidAdapter.js +1 -1
- package/dist/nexx360BidAdapter.js +1 -1
- package/dist/not-for-prod/prebid.js +127 -124
- package/dist/oguryBidAdapter.js +1 -1
- package/dist/onetagBidAdapter.js +1 -1
- package/dist/ooloAnalyticsAdapter.js +1 -1
- package/dist/outbrainBidAdapter.js +1 -1
- package/dist/parrableIdSystem.js +1 -1
- package/dist/pbsExtensions.js +1 -1
- package/dist/pixfutureBidAdapter.js +1 -1
- package/dist/prebid-core.js +1 -1
- package/dist/prebidServerBidAdapter.js +1 -1
- package/dist/publinkIdSystem.js +1 -1
- package/dist/pubmaticBidAdapter.js +1 -1
- package/dist/pubwiseAnalyticsAdapter.js +1 -1
- package/dist/pxyzBidAdapter.js +1 -1
- package/dist/quantcastBidAdapter.js +1 -1
- package/dist/readpeakBidAdapter.js +1 -1
- package/dist/relaidoBidAdapter.js +1 -1
- package/dist/rhythmoneBidAdapter.js +1 -1
- package/dist/riseBidAdapter.js +1 -1
- package/dist/rtdModule.js +1 -1
- package/dist/rubiconAnalyticsAdapter.js +1 -1
- package/dist/rubiconBidAdapter.js +1 -1
- package/dist/seedingAllianceBidAdapter.js +1 -1
- package/dist/seedtagBidAdapter.js +1 -1
- package/dist/sharethroughAnalyticsAdapter.js +1 -1
- package/dist/sharethroughBidAdapter.js +1 -1
- package/dist/shinezBidAdapter.js +1 -1
- package/dist/smaatoBidAdapter.js +1 -1
- package/dist/smartadserverBidAdapter.js +1 -1
- package/dist/smartxBidAdapter.js +1 -1
- package/dist/smilewantedBidAdapter.js +1 -1
- package/dist/sonobiBidAdapter.js +1 -1
- package/dist/sovrnAnalyticsAdapter.js +1 -1
- package/dist/sovrnBidAdapter.js +1 -1
- package/dist/sspBCBidAdapter.js +1 -1
- package/dist/sublimeBidAdapter.js +1 -1
- package/dist/synacormediaBidAdapter.js +1 -1
- package/dist/targetVideoBidAdapter.js +1 -1
- package/dist/teadsBidAdapter.js +1 -1
- package/dist/trionBidAdapter.js +1 -1
- package/dist/tripleliftBidAdapter.js +1 -1
- package/dist/ttdBidAdapter.js +1 -1
- package/dist/ucfunnelAnalyticsAdapter.js +1 -1
- package/dist/uid2IdSystem.js +1 -1
- package/dist/underdogmediaBidAdapter.js +1 -1
- package/dist/undertoneBidAdapter.js +1 -1
- package/dist/vidazooBidAdapter.js +1 -1
- package/dist/videobyteBidAdapter.js +1 -1
- package/dist/viouslyBidAdapter.js +1 -0
- package/dist/visxBidAdapter.js +1 -1
- package/dist/vuukleBidAdapter.js +1 -1
- package/dist/widespaceBidAdapter.js +1 -1
- package/dist/winrBidAdapter.js +1 -1
- package/dist/yahoosspBidAdapter.js +1 -1
- package/dist/yieldmoBidAdapter.js +1 -1
- package/dist/yieldoneAnalyticsAdapter.js +1 -1
- package/integrationExamples/gpt/gpp_us_hello_world.html +124 -0
- package/integrationExamples/gpt/gpp_us_hello_world_iframe.html +12 -0
- package/integrationExamples/gpt/gpp_us_hello_world_iframe_subpage.html +140 -0
- package/karma.conf.maker.js +1 -1
- package/libraries/pbsExtensions/processors/adUnitCode.js +13 -0
- package/libraries/pbsExtensions/processors/pbs.js +5 -0
- package/modules/.submodules.json +1 -0
- package/modules/adkernelBidAdapter.js +2 -1
- package/modules/appnexusBidAdapter.js +23 -2
- package/modules/bidViewability.js +6 -1
- package/modules/ccxBidAdapter.js +3 -2
- package/modules/colossussspBidAdapter.js +8 -0
- package/modules/confiantRtdProvider.js +128 -0
- package/modules/confiantRtdProvider.md +45 -0
- package/modules/consentManagement.js +4 -4
- package/modules/consentManagementGpp.js +386 -0
- package/modules/datawrkzBidAdapter.js +24 -4
- package/modules/dfpAdServerVideo.js +6 -1
- package/modules/feedadBidAdapter.js +11 -3
- package/modules/gridBidAdapter.js +1 -2
- package/modules/lassoBidAdapter.js +2 -2
- package/modules/magniteAnalyticsAdapter.js +9 -1
- package/modules/onetagBidAdapter.js +20 -29
- package/modules/prebidServerBidAdapter/index.js +19 -8
- package/modules/prebidServerBidAdapter/ortbConverter.js +1 -0
- package/modules/rtdModule/index.js +2 -1
- package/modules/viouslyBidAdapter.js +209 -0
- package/modules/viouslyBidAdapter.md +35 -0
- package/package.json +1 -1
- package/src/adapterManager.js +11 -10
- package/src/adapters/bidderFactory.js +5 -5
- package/src/adloader.js +2 -1
- package/src/consentHandler.js +11 -0
- package/src/prebid.js +9 -9
- package/test/helpers/hookSetup.js +5 -0
- package/test/spec/modules/ViouslyBidAdapter_spec.js +378 -0
- package/test/spec/modules/appnexusBidAdapter_spec.js +46 -0
- package/test/spec/modules/colossussspBidAdapter_spec.js +89 -2
- package/test/spec/modules/confiantRtdProvider_spec.js +107 -0
- package/test/spec/modules/consentManagementGpp_spec.js +577 -0
- package/test/spec/modules/feedadBidAdapter_spec.js +38 -13
- package/test/spec/modules/gridBidAdapter_spec.js +0 -18
- package/test/spec/modules/magniteAnalyticsAdapter_spec.js +36 -14
- package/test/spec/modules/onetagBidAdapter_spec.js +29 -23
- package/test/spec/modules/topicsFpdModule_spec.js +41 -22
- package/test/spec/ortbConverter/pbsExtensions/adUnitCode_spec.js +23 -0
- package/test/spec/unit/pbjs_api_spec.js +11 -0
|
@@ -87,6 +87,11 @@ export const spec = {
|
|
|
87
87
|
logMessage(e);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
const firstPartyData = bidderRequest.ortb2 || {};
|
|
91
|
+
const userObj = firstPartyData.user;
|
|
92
|
+
const siteObj = firstPartyData.site;
|
|
93
|
+
const appObj = firstPartyData.app;
|
|
94
|
+
|
|
90
95
|
// TODO: does the fallback to window.location make sense?
|
|
91
96
|
const location = refferLocation || winLocation;
|
|
92
97
|
let placements = [];
|
|
@@ -97,6 +102,9 @@ export const spec = {
|
|
|
97
102
|
secure: location.protocol === 'https:' ? 1 : 0,
|
|
98
103
|
host: location.host,
|
|
99
104
|
page: location.pathname,
|
|
105
|
+
userObj,
|
|
106
|
+
siteObj,
|
|
107
|
+
appObj,
|
|
100
108
|
placements: placements
|
|
101
109
|
};
|
|
102
110
|
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This module provides comprehensive detection of security, quality, and privacy threats by Confiant Inc,
|
|
3
|
+
* the industry leader in real-time detecting and blocking of bad ads
|
|
4
|
+
*
|
|
5
|
+
* The {@link module:modules/realTimeData} module is required
|
|
6
|
+
* The module will inject a Confiant Inc. script into the page to monitor ad impressions
|
|
7
|
+
* @module modules/cleanioRtdProvider
|
|
8
|
+
* @requires module:modules/realTimeData
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { submodule } from '../src/hook.js';
|
|
12
|
+
import { logError, generateUUID } from '../src/utils.js';
|
|
13
|
+
import { loadExternalScript } from '../src/adloader.js';
|
|
14
|
+
import * as events from '../src/events.js';
|
|
15
|
+
import CONSTANTS from '../src/constants.json';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Injects the Confiant Inc. configuration script into the page, based on proprtyId provided
|
|
19
|
+
* @param {string} propertyId
|
|
20
|
+
*/
|
|
21
|
+
function injectConfigScript(propertyId) {
|
|
22
|
+
const scriptSrc = `https://cdn.confiant-integrations.net/${propertyId}/gpt_and_prebid/config.js`;
|
|
23
|
+
|
|
24
|
+
loadExternalScript(scriptSrc, 'confiant', () => {});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Set up page with Confiant integration
|
|
29
|
+
* @param {Object} config
|
|
30
|
+
*/
|
|
31
|
+
function setupPage(config) {
|
|
32
|
+
const propertyId = config?.params?.propertyId;
|
|
33
|
+
if (!propertyId) {
|
|
34
|
+
logError('Confiant pbjs module: no propertyId provided');
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const confiant = window.confiant || Object.create(null);
|
|
39
|
+
confiant[propertyId] = confiant[propertyId] || Object.create(null);
|
|
40
|
+
confiant[propertyId].clientSettings = confiant[propertyId].clientSettings || Object.create(null);
|
|
41
|
+
confiant[propertyId].clientSettings.isMGBL = true;
|
|
42
|
+
confiant[propertyId].clientSettings.prebidExcludeBidders = config?.params?.prebidExcludeBidders;
|
|
43
|
+
confiant[propertyId].clientSettings.prebidNameSpace = config?.params?.prebidNameSpace;
|
|
44
|
+
|
|
45
|
+
if (config?.params?.shouldEmitBillableEvent) {
|
|
46
|
+
if (window.frames['cnftComm']) {
|
|
47
|
+
subscribeToConfiantCommFrame(window);
|
|
48
|
+
} else {
|
|
49
|
+
setUpMutationObserver();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
injectConfigScript(propertyId);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Subscribe to window's message events to report Billable events
|
|
59
|
+
* @param {Window} targetWindow window instance to subscribe to
|
|
60
|
+
*/
|
|
61
|
+
function subscribeToConfiantCommFrame(targetWindow) {
|
|
62
|
+
targetWindow.addEventListener('message', reportBillableEvents);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let mutationObserver;
|
|
66
|
+
/**
|
|
67
|
+
* Set up mutation observer to subscribe to Confiant's communication channel ASAP
|
|
68
|
+
*/
|
|
69
|
+
function setUpMutationObserver() {
|
|
70
|
+
mutationObserver = new MutationObserver((mutations) => {
|
|
71
|
+
mutations.forEach((mutation) => {
|
|
72
|
+
mutation.addedNodes.forEach((addedNode) => {
|
|
73
|
+
if (addedNode.nodeName === 'IFRAME' && addedNode.name === 'cnftComm' && !addedNode.pbjsModuleSubscribed) {
|
|
74
|
+
addedNode.pbjsModuleSubscribed = true;
|
|
75
|
+
mutationObserver.disconnect();
|
|
76
|
+
mutationObserver = null;
|
|
77
|
+
const iframeWindow = addedNode.contentWindow;
|
|
78
|
+
subscribeToConfiantCommFrame(iframeWindow);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
mutationObserver.observe(document.head, { childList: true, subtree: true });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Emit billable event when Confiant integration reports that it has monitored an impression
|
|
88
|
+
*/
|
|
89
|
+
function reportBillableEvents (e) {
|
|
90
|
+
events.emit(CONSTANTS.EVENTS.BILLABLE_EVENT, {
|
|
91
|
+
auctionId: e.data.auctionId,
|
|
92
|
+
billingId: generateUUID(),
|
|
93
|
+
transactionId: e.data.transactionId,
|
|
94
|
+
type: 'impression',
|
|
95
|
+
vendor: 'confiant'
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Confiant submodule registration
|
|
101
|
+
*/
|
|
102
|
+
function registerConfiantSubmodule() {
|
|
103
|
+
submodule('realTimeData', {
|
|
104
|
+
name: 'confiant',
|
|
105
|
+
init: (config) => {
|
|
106
|
+
try {
|
|
107
|
+
return setupPage(config);
|
|
108
|
+
} catch (err) {
|
|
109
|
+
logError(err.message);
|
|
110
|
+
if (mutationObserver) {
|
|
111
|
+
mutationObserver.disconnect();
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
registerConfiantSubmodule();
|
|
120
|
+
|
|
121
|
+
export default {
|
|
122
|
+
injectConfigScript,
|
|
123
|
+
setupPage,
|
|
124
|
+
subscribeToConfiantCommFrame,
|
|
125
|
+
setUpMutationObserver,
|
|
126
|
+
reportBillableEvents,
|
|
127
|
+
registerConfiantSubmodule
|
|
128
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Overview
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
Module Name: Confiant Inc. Rtd provider
|
|
5
|
+
Module Type: Rtd Provider
|
|
6
|
+
Maintainer:
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Confiant’s module provides comprehensive detection of security, quality, and privacy threats across your ad stack.
|
|
10
|
+
Confiant is the industry leader in real-time detecting and blocking of bad ads when it comes to protecting your users and brand reputation.
|
|
11
|
+
|
|
12
|
+
To start using this module, please contact [Confiant](https://www.confiant.com/contact) to get an account and customer key.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Integration
|
|
16
|
+
|
|
17
|
+
1) Build Prebid bundle with Confiant module included:
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
gulp build --modules=confiantRtdProvider,...
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
2) Include the resulting bundle on your page.
|
|
25
|
+
|
|
26
|
+
# Configuration
|
|
27
|
+
|
|
28
|
+
Configuration of Confiant module is plain simple:
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
pbjs.setConfig({
|
|
32
|
+
realTimeData: {
|
|
33
|
+
dataProviders: [{
|
|
34
|
+
name: 'confiant',
|
|
35
|
+
params: {
|
|
36
|
+
// so please get in touch with us so we could help you to set up the module with proper parameters
|
|
37
|
+
propertyId: '', // required, string param, obtained from Confiant Inc.
|
|
38
|
+
prebidExcludeBidders: '', // optional, comma separated list of bidders to exclude from Confiant's prebid.js integration
|
|
39
|
+
prebidNameSpace: '', // optional, string param, namespace for prebid.js integration
|
|
40
|
+
shouldEmitBillableEvent: false, // optional, boolean param, upon being set to true enables firing of the BillableEvent upon Confiant's impression scanning
|
|
41
|
+
}
|
|
42
|
+
}]
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
```
|
|
@@ -94,7 +94,7 @@ function lookupIabConsent({onSuccess, onError}) {
|
|
|
94
94
|
const { cmpFrame, cmpFunction } = findCMP();
|
|
95
95
|
|
|
96
96
|
if (!cmpFrame) {
|
|
97
|
-
return onError('CMP not found.');
|
|
97
|
+
return onError('TCF2 CMP not found.');
|
|
98
98
|
}
|
|
99
99
|
// to collect the consent information from the user, we perform two calls to the CMP in parallel:
|
|
100
100
|
// first to collect the user's consent choices represented in an encoded string (via getConsentData)
|
|
@@ -314,11 +314,11 @@ export function resetConsentData() {
|
|
|
314
314
|
* @param {{cmp:string, timeout:number, allowAuctionWithoutConsent:boolean, defaultGdprScope:boolean}} config required; consentManagement module config settings; cmp (string), timeout (int), allowAuctionWithoutConsent (boolean)
|
|
315
315
|
*/
|
|
316
316
|
export function setConsentConfig(config) {
|
|
317
|
-
// if `config.gdpr` or `config.
|
|
317
|
+
// if `config.gdpr`, `config.usp` or `config.gpp` exist, assume new config format.
|
|
318
318
|
// else for backward compatability, just use `config`
|
|
319
|
-
config = config && (config.gdpr || config.usp ? config.gdpr : config);
|
|
319
|
+
config = config && (config.gdpr || config.usp || config.gpp ? config.gdpr : config);
|
|
320
320
|
if (!config || typeof config !== 'object') {
|
|
321
|
-
logWarn('consentManagement config not defined, exiting consent manager');
|
|
321
|
+
logWarn('consentManagement (gdpr) config not defined, exiting consent manager');
|
|
322
322
|
return;
|
|
323
323
|
}
|
|
324
324
|
if (isStr(config.cmpApi)) {
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This module adds GPP consentManagement support to prebid.js. It interacts with
|
|
3
|
+
* supported CMPs (Consent Management Platforms) to grab the user's consent information
|
|
4
|
+
* and make it available for any GPP supported adapters to read/pass this information to
|
|
5
|
+
* their system and for various other features/modules in Prebid.js.
|
|
6
|
+
*/
|
|
7
|
+
import {deepSetValue, isNumber, isPlainObject, isStr, logError, logInfo, logWarn} from '../src/utils.js';
|
|
8
|
+
import {config} from '../src/config.js';
|
|
9
|
+
import {gppDataHandler} from '../src/adapterManager.js';
|
|
10
|
+
import {includes} from '../src/polyfill.js';
|
|
11
|
+
import {timedAuctionHook} from '../src/utils/perfMetrics.js';
|
|
12
|
+
import { enrichFPD } from '../src/fpd/enrichment.js';
|
|
13
|
+
|
|
14
|
+
const DEFAULT_CMP = 'iab';
|
|
15
|
+
const DEFAULT_CONSENT_TIMEOUT = 10000;
|
|
16
|
+
const CMP_VERSION = 1;
|
|
17
|
+
|
|
18
|
+
export let userCMP;
|
|
19
|
+
export let consentTimeout;
|
|
20
|
+
export let staticConsentData;
|
|
21
|
+
|
|
22
|
+
let consentData;
|
|
23
|
+
let addedConsentHook = false;
|
|
24
|
+
|
|
25
|
+
// add new CMPs here, with their dedicated lookup function
|
|
26
|
+
const cmpCallMap = {
|
|
27
|
+
'iab': lookupIabConsent,
|
|
28
|
+
'static': lookupStaticConsentData
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* This function checks the state of the IAB gppData's applicableSection field (to ensure it's populated and has a valid value).
|
|
33
|
+
* section === 0 represents a CMP's default value when CMP is loading, it shoud not be used a real user's section.
|
|
34
|
+
*
|
|
35
|
+
* TODO --- The initial version of the GPP CMP API spec used this naming convention, but it was later changed as an update to the spec.
|
|
36
|
+
* CMPs should adjust their logic to use the new format (applicableSecctions), but that may not be the case with the initial release.
|
|
37
|
+
* Added support just in case for this transition period, can likely be removed at a later date...
|
|
38
|
+
* @param gppData represents the IAB gppData object
|
|
39
|
+
* @returns true|false
|
|
40
|
+
*/
|
|
41
|
+
function checkApplicableSectionIsReady(gppData) {
|
|
42
|
+
return gppData && Array.isArray(gppData.applicableSection) && gppData.applicableSection.length > 0 && gppData.applicableSection[0] !== 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* This function checks the state of the IAB gppData's applicableSections field (to ensure it's populated and has a valid value).
|
|
47
|
+
* section === 0 represents a CMP's default value when CMP is loading, it shoud not be used a real user's section.
|
|
48
|
+
* @param gppData represents the IAB gppData object
|
|
49
|
+
* @returns true|false
|
|
50
|
+
*/
|
|
51
|
+
function checkApplicableSectionsIsReady(gppData) {
|
|
52
|
+
return gppData && Array.isArray(gppData.applicableSections) && gppData.applicableSections.length > 0 && gppData.applicableSections[0] !== 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* This function reads the consent string from the config to obtain the consent information of the user.
|
|
57
|
+
* @param {function({})} onSuccess acts as a success callback when the value is read from config; pass along consentObject from CMP
|
|
58
|
+
*/
|
|
59
|
+
function lookupStaticConsentData({onSuccess, onError}) {
|
|
60
|
+
processCmpData(staticConsentData, {onSuccess, onError});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* This function handles interacting with an IAB compliant CMP to obtain the consent information of the user.
|
|
65
|
+
* Given the async nature of the CMP's API, we pass in acting success/error callback functions to exit this function
|
|
66
|
+
* based on the appropriate result.
|
|
67
|
+
* @param {function({})} onSuccess acts as a success callback when CMP returns a value; pass along consentObjectfrom CMP
|
|
68
|
+
* @param {function(string, ...{}?)} cmpError acts as an error callback while interacting with CMP; pass along an error message (string) and any extra error arguments (purely for logging)
|
|
69
|
+
*/
|
|
70
|
+
function lookupIabConsent({onSuccess, onError}) {
|
|
71
|
+
const cmpApiName = '__gpp';
|
|
72
|
+
const cmpCallbacks = {};
|
|
73
|
+
let registeredPostMessageResponseListener = false;
|
|
74
|
+
|
|
75
|
+
function findCMP() {
|
|
76
|
+
let f = window;
|
|
77
|
+
let cmpFrame;
|
|
78
|
+
let cmpDirectAccess = false;
|
|
79
|
+
while (true) {
|
|
80
|
+
try {
|
|
81
|
+
if (typeof f[cmpApiName] === 'function') {
|
|
82
|
+
cmpFrame = f;
|
|
83
|
+
cmpDirectAccess = true;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
} catch (e) {}
|
|
87
|
+
|
|
88
|
+
// need separate try/catch blocks due to the exception errors thrown when trying to check for a frame that doesn't exist in 3rd party env
|
|
89
|
+
try {
|
|
90
|
+
if (f.frames['__gppLocator']) {
|
|
91
|
+
cmpFrame = f;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
} catch (e) {}
|
|
95
|
+
|
|
96
|
+
if (f === window.top) break;
|
|
97
|
+
f = f.parent;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
cmpFrame,
|
|
102
|
+
cmpDirectAccess
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const {cmpFrame, cmpDirectAccess} = findCMP();
|
|
107
|
+
|
|
108
|
+
if (!cmpFrame) {
|
|
109
|
+
return onError('GPP CMP not found.');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const invokeCMP = (cmpDirectAccess) ? invokeCMPDirect : invokeCMPFrame;
|
|
113
|
+
|
|
114
|
+
function invokeCMPDirect({command, callback, parameter, version = CMP_VERSION}, resultCb) {
|
|
115
|
+
if (typeof resultCb === 'function') {
|
|
116
|
+
resultCb(cmpFrame[cmpApiName](command, callback, parameter, version));
|
|
117
|
+
} else {
|
|
118
|
+
cmpFrame[cmpApiName](command, callback, parameter, version);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function invokeCMPFrame({command, callback, parameter, version = CMP_VERSION}, resultCb) {
|
|
123
|
+
const callName = `${cmpApiName}Call`;
|
|
124
|
+
if (!registeredPostMessageResponseListener) {
|
|
125
|
+
// when we get the return message, call the stashed callback;
|
|
126
|
+
window.addEventListener('message', readPostMessageResponse, false);
|
|
127
|
+
registeredPostMessageResponseListener = true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// call CMP via postMessage
|
|
131
|
+
const callId = Math.random().toString();
|
|
132
|
+
const msg = {
|
|
133
|
+
[callName]: {
|
|
134
|
+
command: command,
|
|
135
|
+
parameter,
|
|
136
|
+
version,
|
|
137
|
+
callId: callId
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// TODO? - add logic to check if random was already used in the same session, and roll another if so?
|
|
142
|
+
cmpCallbacks[callId] = (typeof callback === 'function') ? callback : resultCb;
|
|
143
|
+
cmpFrame.postMessage(msg, '*');
|
|
144
|
+
|
|
145
|
+
function readPostMessageResponse(event) {
|
|
146
|
+
const cmpDataPkgName = `${cmpApiName}Return`;
|
|
147
|
+
const json = (typeof event.data === 'string' && event.data.includes(cmpDataPkgName)) ? JSON.parse(event.data) : event.data;
|
|
148
|
+
if (json[cmpDataPkgName] && json[cmpDataPkgName].callId) {
|
|
149
|
+
const payload = json[cmpDataPkgName];
|
|
150
|
+
|
|
151
|
+
if (cmpCallbacks.hasOwnProperty(payload.callId)) {
|
|
152
|
+
cmpCallbacks[payload.callId](payload.returnValue);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const startupMsg = (cmpDirectAccess) ? 'Detected GPP CMP API is directly accessible, calling it now...'
|
|
159
|
+
: 'Detected GPP CMP is outside the current iframe where Prebid.js is located, calling it now...';
|
|
160
|
+
logInfo(startupMsg);
|
|
161
|
+
|
|
162
|
+
invokeCMP({
|
|
163
|
+
command: 'addEventListener',
|
|
164
|
+
callback: function (evt) {
|
|
165
|
+
if (evt) {
|
|
166
|
+
logInfo(`Received a ${(cmpDirectAccess ? 'direct' : 'postmsg')} response from GPP CMP for event`, evt);
|
|
167
|
+
if (evt.eventName === 'sectionChange' || evt.pingData.cmpStatus === 'loaded') {
|
|
168
|
+
invokeCMP({command: 'getGPPData'}, function (gppData) {
|
|
169
|
+
logInfo(`Received a ${cmpDirectAccess ? 'direct' : 'postmsg'} response from GPP CMP for getGPPData`, gppData);
|
|
170
|
+
processCmpData(gppData, {onSuccess, onError});
|
|
171
|
+
});
|
|
172
|
+
} else if (evt.pingData.cmpStatus === 'error') {
|
|
173
|
+
onError('CMP returned with a cmpStatus:error response. Please check CMP setup.');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Look up consent data and store it in the `consentData` global as well as `adapterManager.js`' gdprDataHandler.
|
|
182
|
+
*
|
|
183
|
+
* @param cb A callback that takes: a boolean that is true if the auction should be canceled; an error message and extra
|
|
184
|
+
* error arguments that will be undefined if there's no error.
|
|
185
|
+
*/
|
|
186
|
+
function loadConsentData(cb) {
|
|
187
|
+
let isDone = false;
|
|
188
|
+
let timer = null;
|
|
189
|
+
|
|
190
|
+
function done(consentData, shouldCancelAuction, errMsg, ...extraArgs) {
|
|
191
|
+
if (timer != null) {
|
|
192
|
+
clearTimeout(timer);
|
|
193
|
+
}
|
|
194
|
+
isDone = true;
|
|
195
|
+
gppDataHandler.setConsentData(consentData);
|
|
196
|
+
if (typeof cb === 'function') {
|
|
197
|
+
cb(shouldCancelAuction, errMsg, ...extraArgs);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!includes(Object.keys(cmpCallMap), userCMP)) {
|
|
202
|
+
done(null, false, `GPP CMP framework (${userCMP}) is not a supported framework. Aborting consentManagement module and resuming auction.`);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const callbacks = {
|
|
207
|
+
onSuccess: (data) => done(data, false),
|
|
208
|
+
onError: function (msg, ...extraArgs) {
|
|
209
|
+
done(null, true, msg, ...extraArgs);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
cmpCallMap[userCMP](callbacks);
|
|
213
|
+
|
|
214
|
+
if (!isDone) {
|
|
215
|
+
const onTimeout = () => {
|
|
216
|
+
const continueToAuction = (data) => {
|
|
217
|
+
done(data, false, 'GPP CMP did not load, continuing auction...');
|
|
218
|
+
}
|
|
219
|
+
processCmpData(consentData, {
|
|
220
|
+
onSuccess: continueToAuction,
|
|
221
|
+
onError: () => continueToAuction(storeConsentData(undefined))
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
if (consentTimeout === 0) {
|
|
225
|
+
onTimeout();
|
|
226
|
+
} else {
|
|
227
|
+
timer = setTimeout(onTimeout, consentTimeout);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Like `loadConsentData`, but cache and re-use previously loaded data.
|
|
234
|
+
* @param cb
|
|
235
|
+
*/
|
|
236
|
+
function loadIfMissing(cb) {
|
|
237
|
+
if (consentData) {
|
|
238
|
+
logInfo('User consent information already known. Pulling internally stored information...');
|
|
239
|
+
// eslint-disable-next-line standard/no-callback-literal
|
|
240
|
+
cb(false);
|
|
241
|
+
} else {
|
|
242
|
+
loadConsentData(cb);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* If consentManagement module is enabled (ie included in setConfig), this hook function will attempt to fetch the
|
|
248
|
+
* user's encoded consent string from the supported CMP. Once obtained, the module will store this
|
|
249
|
+
* data as part of a gppConsent object which gets transferred to adapterManager's gppDataHandler object.
|
|
250
|
+
* This information is later added into the bidRequest object for any supported adapters to read/pass along to their system.
|
|
251
|
+
* @param {object} reqBidsConfigObj required; This is the same param that's used in pbjs.requestBids.
|
|
252
|
+
* @param {function} fn required; The next function in the chain, used by hook.js
|
|
253
|
+
*/
|
|
254
|
+
export const requestBidsHook = timedAuctionHook('gpp', function requestBidsHook(fn, reqBidsConfigObj) {
|
|
255
|
+
loadIfMissing(function (shouldCancelAuction, errMsg, ...extraArgs) {
|
|
256
|
+
if (errMsg) {
|
|
257
|
+
let log = logWarn;
|
|
258
|
+
if (shouldCancelAuction) {
|
|
259
|
+
log = logError;
|
|
260
|
+
errMsg = `${errMsg} Canceling auction as per consentManagement config.`;
|
|
261
|
+
}
|
|
262
|
+
log(errMsg, ...extraArgs);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (shouldCancelAuction) {
|
|
266
|
+
fn.stopTiming();
|
|
267
|
+
if (typeof reqBidsConfigObj.bidsBackHandler === 'function') {
|
|
268
|
+
reqBidsConfigObj.bidsBackHandler();
|
|
269
|
+
} else {
|
|
270
|
+
logError('Error executing bidsBackHandler');
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
fn.call(this, reqBidsConfigObj);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* This function checks the consent data provided by CMP to ensure it's in an expected state.
|
|
280
|
+
* If it's bad, we call `onError`
|
|
281
|
+
* If it's good, then we store the value and call `onSuccess`
|
|
282
|
+
*/
|
|
283
|
+
function processCmpData(consentObject, {onSuccess, onError}) {
|
|
284
|
+
function checkData() {
|
|
285
|
+
const gppString = consentObject && consentObject.gppString;
|
|
286
|
+
const gppSection = (checkApplicableSectionsIsReady(consentObject)) ? consentObject.applicableSections
|
|
287
|
+
: (checkApplicableSectionIsReady(consentObject)) ? consentObject.applicableSection : [];
|
|
288
|
+
|
|
289
|
+
return !!(
|
|
290
|
+
(!Array.isArray(gppSection)) ||
|
|
291
|
+
(Array.isArray(gppSection) && (!gppString || !isStr(gppString)))
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (checkData()) {
|
|
296
|
+
onError(`CMP returned unexpected value during lookup process.`, consentObject);
|
|
297
|
+
} else {
|
|
298
|
+
onSuccess(storeConsentData(consentObject));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Stores CMP data locally in module to make information available in adaptermanager.js for later in the auction
|
|
304
|
+
* @param {object} cmpConsentObject required; an object representing user's consent choices (can be undefined in certain use-cases for this function only)
|
|
305
|
+
*/
|
|
306
|
+
function storeConsentData(cmpConsentObject) {
|
|
307
|
+
consentData = {
|
|
308
|
+
gppString: (cmpConsentObject) ? cmpConsentObject.gppString : undefined,
|
|
309
|
+
|
|
310
|
+
fullGppData: (cmpConsentObject) || undefined,
|
|
311
|
+
};
|
|
312
|
+
consentData.applicableSections = (checkApplicableSectionsIsReady(cmpConsentObject)) ? cmpConsentObject.applicableSections
|
|
313
|
+
: (checkApplicableSectionIsReady(cmpConsentObject)) ? cmpConsentObject.applicableSection : [];
|
|
314
|
+
consentData.apiVersion = CMP_VERSION;
|
|
315
|
+
return consentData;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Simply resets the module's consentData variable back to undefined, mainly for testing purposes
|
|
320
|
+
*/
|
|
321
|
+
export function resetConsentData() {
|
|
322
|
+
consentData = undefined;
|
|
323
|
+
userCMP = undefined;
|
|
324
|
+
consentTimeout = undefined;
|
|
325
|
+
gppDataHandler.reset();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* A configuration function that initializes some module variables, as well as add a hook into the requestBids function
|
|
330
|
+
* @param {{cmp:string, timeout:number, allowAuctionWithoutConsent:boolean, defaultGdprScope:boolean}} config required; consentManagement module config settings; cmp (string), timeout (int), allowAuctionWithoutConsent (boolean)
|
|
331
|
+
*/
|
|
332
|
+
export function setConsentConfig(config) {
|
|
333
|
+
config = config && config.gpp;
|
|
334
|
+
if (!config || typeof config !== 'object') {
|
|
335
|
+
logWarn('consentManagement.gpp config not defined, exiting consent manager module');
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (isStr(config.cmpApi)) {
|
|
340
|
+
userCMP = config.cmpApi;
|
|
341
|
+
} else {
|
|
342
|
+
userCMP = DEFAULT_CMP;
|
|
343
|
+
logInfo(`consentManagement.gpp config did not specify cmp. Using system default setting (${DEFAULT_CMP}).`);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (isNumber(config.timeout)) {
|
|
347
|
+
consentTimeout = config.timeout;
|
|
348
|
+
} else {
|
|
349
|
+
consentTimeout = DEFAULT_CONSENT_TIMEOUT;
|
|
350
|
+
logInfo(`consentManagement.gpp config did not specify timeout. Using system default setting (${DEFAULT_CONSENT_TIMEOUT}).`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (userCMP === 'static') {
|
|
354
|
+
if (isPlainObject(config.consentData)) {
|
|
355
|
+
staticConsentData = config.consentData;
|
|
356
|
+
consentTimeout = 0;
|
|
357
|
+
} else {
|
|
358
|
+
logError(`consentManagement.gpp config with cmpApi: 'static' did not specify consentData. No consents will be available to adapters.`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
logInfo('consentManagement.gpp module has been activated...');
|
|
363
|
+
|
|
364
|
+
if (!addedConsentHook) {
|
|
365
|
+
$$PREBID_GLOBAL$$.requestBids.before(requestBidsHook, 50);
|
|
366
|
+
}
|
|
367
|
+
addedConsentHook = true;
|
|
368
|
+
gppDataHandler.enable();
|
|
369
|
+
loadConsentData(); // immediately look up consent data to make it available without requiring an auction
|
|
370
|
+
}
|
|
371
|
+
config.getConfig('consentManagement', config => setConsentConfig(config.consentManagement));
|
|
372
|
+
|
|
373
|
+
export function enrichFPDHook(next, fpd) {
|
|
374
|
+
return next(fpd.then(ortb2 => {
|
|
375
|
+
const consent = gppDataHandler.getConsentData();
|
|
376
|
+
if (consent) {
|
|
377
|
+
if (Array.isArray(consent.applicableSections)) {
|
|
378
|
+
deepSetValue(ortb2, 'regs.gpp_sid', consent.applicableSections);
|
|
379
|
+
}
|
|
380
|
+
deepSetValue(ortb2, 'regs.gpp', consent.gppString);
|
|
381
|
+
}
|
|
382
|
+
return ortb2;
|
|
383
|
+
}));
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
enrichFPD.before(enrichFPDHook);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deepAccess, getBidIdParameter, isArray, getUniqueIdentifierStr, contains } from '../src/utils.js';
|
|
1
|
+
import { deepAccess, getBidIdParameter, isArray, getUniqueIdentifierStr, contains, isFn, isPlainObject } from '../src/utils.js';
|
|
2
2
|
import { config } from '../src/config.js';
|
|
3
3
|
import { Renderer } from '../src/Renderer.js';
|
|
4
4
|
import { registerBidder } from '../src/adapters/bidderFactory.js';
|
|
@@ -84,7 +84,7 @@ export const spec = {
|
|
|
84
84
|
|
|
85
85
|
/* Generate bid request for banner adunit */
|
|
86
86
|
function buildBannerRequest(bidRequest, bidderRequest) {
|
|
87
|
-
let bidFloor =
|
|
87
|
+
let bidFloor = getBidFloor(bidRequest);
|
|
88
88
|
|
|
89
89
|
let adW = 0;
|
|
90
90
|
let adH = 0;
|
|
@@ -135,7 +135,7 @@ function buildNativeRequest(bidRequest, bidderRequest) {
|
|
|
135
135
|
let counter = 0;
|
|
136
136
|
let assets = [];
|
|
137
137
|
|
|
138
|
-
let bidFloor =
|
|
138
|
+
let bidFloor = getBidFloor(bidRequest);
|
|
139
139
|
|
|
140
140
|
let title = deepAccess(bidRequest, 'mediaTypes.native.title');
|
|
141
141
|
if (title && title.len) {
|
|
@@ -196,7 +196,7 @@ function buildNativeRequest(bidRequest, bidderRequest) {
|
|
|
196
196
|
|
|
197
197
|
/* Generate bid request for video adunit */
|
|
198
198
|
function buildVideoRequest(bidRequest, bidderRequest) {
|
|
199
|
-
let bidFloor =
|
|
199
|
+
let bidFloor = getBidFloor(bidRequest);
|
|
200
200
|
|
|
201
201
|
let sizeObj = getVideoAdUnitSize(bidRequest);
|
|
202
202
|
|
|
@@ -414,6 +414,7 @@ function buildBannerResponse(bidRequest, bidResponse) {
|
|
|
414
414
|
}
|
|
415
415
|
let bidSizes = (deepAccess(bidRequest, 'mediaTypes.banner.sizes')) ? deepAccess(bidRequest, 'mediaTypes.banner.sizes') : bidRequest.sizes;
|
|
416
416
|
bidResponse.requestId = bidRequest.bidId;
|
|
417
|
+
bidResponse.auctionId = bidRequest.auctionId;
|
|
417
418
|
bidResponse.transactionId = bidRequest.transactionId;
|
|
418
419
|
bidResponse.placementCode = placementCode;
|
|
419
420
|
bidResponse.cpm = responseCPM;
|
|
@@ -455,6 +456,7 @@ function buildNativeResponse(bidRequest, response) {
|
|
|
455
456
|
return;
|
|
456
457
|
}
|
|
457
458
|
bidResponse.requestId = bidRequest.bidId;
|
|
459
|
+
bidResponse.auctionId = bidRequest.auctionId;
|
|
458
460
|
bidResponse.transactionId = bidRequest.transactionId;
|
|
459
461
|
bidResponse.placementCode = placementCode;
|
|
460
462
|
bidResponse.cpm = responseCPM;
|
|
@@ -507,6 +509,7 @@ function buildVideoResponse(bidRequest, response) {
|
|
|
507
509
|
let context = bidRequest.mediaTypes.video.context;
|
|
508
510
|
|
|
509
511
|
bidResponse.requestId = bidRequest.bidId;
|
|
512
|
+
bidResponse.auctionId = bidRequest.auctionId;
|
|
510
513
|
bidResponse.transactionId = bidRequest.transactionId;
|
|
511
514
|
bidResponse.placementCode = placementCode;
|
|
512
515
|
bidResponse.cpm = responseCPM;
|
|
@@ -630,4 +633,21 @@ function getNativeAssestObj(obj, assets) {
|
|
|
630
633
|
}
|
|
631
634
|
}
|
|
632
635
|
|
|
636
|
+
// BUILD REQUESTS: BIDFLOORS
|
|
637
|
+
function getBidFloor(bid) {
|
|
638
|
+
if (!isFn(bid.getFloor)) {
|
|
639
|
+
return (bid.params.bidfloor) ? bid.params.bidfloor : null;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
let floor = bid.getFloor({
|
|
643
|
+
currency: 'USD',
|
|
644
|
+
mediaType: '*',
|
|
645
|
+
size: '*'
|
|
646
|
+
});
|
|
647
|
+
if (isPlainObject(floor) && !isNaN(floor.floor) && floor.currency === 'USD') {
|
|
648
|
+
return floor.floor;
|
|
649
|
+
}
|
|
650
|
+
return null;
|
|
651
|
+
}
|
|
652
|
+
|
|
633
653
|
registerBidder(spec);
|