prebid.js 6.3.0 → 6.7.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/.circleci/config.yml +1 -1
- package/gulpfile.js +87 -74
- package/integrationExamples/gpt/amp/creative.html +11 -33
- package/karma.conf.maker.js +1 -1
- package/modules/.submodules.json +2 -1
- package/modules/adbookpspBidAdapter.js +27 -10
- package/modules/adhashBidAdapter.js +3 -3
- package/modules/adkernelBidAdapter.js +148 -62
- package/modules/adlooxAdServerVideo.js +2 -2
- package/modules/adlooxAnalyticsAdapter.js +4 -4
- package/modules/admanBidAdapter.js +10 -4
- package/modules/adomikAnalyticsAdapter.js +27 -9
- package/modules/adqueryIdSystem.js +103 -0
- package/modules/adqueryIdSystem.md +35 -0
- package/modules/adyoulikeBidAdapter.js +13 -9
- package/modules/aniviewBidAdapter.js +1 -1
- package/modules/beopBidAdapter.js +1 -1
- package/modules/bidViewability.js +3 -3
- package/modules/bidViewabilityIO.js +3 -3
- package/modules/bliinkBidAdapter.js +3 -2
- package/modules/colossussspBidAdapter.js +12 -8
- package/modules/colossussspBidAdapter.md +15 -1
- package/modules/compassBidAdapter.js +208 -0
- package/modules/compassBidAdapter.md +79 -0
- package/modules/consentManagement.js +7 -1
- package/modules/consumableBidAdapter.md +1 -1
- package/modules/criteoBidAdapter.js +1 -1
- package/modules/criteoIdSystem.js +29 -7
- package/modules/currency.js +2 -2
- package/modules/dailyhuntBidAdapter.js +435 -0
- package/modules/dailyhuntBidAdapter.md +4 -0
- package/modules/docereeBidAdapter.js +10 -1
- package/modules/docereeBidAdapter.md +2 -0
- package/modules/engageyaBidAdapter.js +1 -1
- package/modules/feedadBidAdapter.js +2 -2
- package/modules/feedadBidAdapter.md +4 -2
- package/modules/glimpseBidAdapter.js +66 -44
- package/modules/gnetBidAdapter.js +3 -3
- package/modules/gnetBidAdapter.md +4 -4
- package/modules/gptPreAuction.js +55 -7
- package/modules/gridBidAdapter.js +4 -3
- package/modules/gumgumBidAdapter.js +4 -4
- package/modules/idImportLibrary.js +45 -8
- package/modules/idImportLibrary.md +4 -0
- package/modules/improvedigitalBidAdapter.js +42 -4
- package/modules/instreamTracking.js +4 -4
- package/modules/invibesBidAdapter.js +49 -5
- package/modules/invibesBidAdapter.md +2 -1
- package/modules/ixBidAdapter.js +53 -18
- package/modules/jwplayerRtdProvider.js +71 -6
- package/modules/jwplayerRtdProvider.md +27 -11
- package/modules/kargoBidAdapter.js +2 -2
- package/modules/limelightDigitalBidAdapter.js +2 -1
- package/modules/livewrappedAnalyticsAdapter.js +3 -1
- package/modules/livewrappedBidAdapter.js +8 -2
- package/modules/loglyliftBidAdapter.js +79 -0
- package/modules/loglyliftBidAdapter.md +55 -0
- package/modules/nextMillenniumBidAdapter.js +11 -7
- package/modules/oguryBidAdapter.js +9 -2
- package/modules/onetagBidAdapter.js +4 -2
- package/modules/optimeraRtdProvider.js +8 -1
- package/modules/ozoneBidAdapter.js +21 -64
- package/modules/pilotxBidAdapter.js +147 -0
- package/modules/pilotxBidAdapter.md +50 -0
- package/modules/proxistoreBidAdapter.js +0 -2
- package/modules/pubgeniusBidAdapter.js +1 -1
- package/modules/pubmaticAnalyticsAdapter.js +16 -0
- package/modules/pubxaiAnalyticsAdapter.js +17 -0
- package/modules/richaudienceBidAdapter.js +3 -2
- package/modules/riseBidAdapter.js +1 -1
- package/modules/rtbhouseBidAdapter.js +14 -4
- package/modules/rtdModule/index.js +14 -15
- package/modules/rubiconAnalyticsAdapter.js +3 -2
- package/modules/rubiconBidAdapter.js +21 -11
- package/modules/seedingAllianceBidAdapter.js +3 -3
- package/modules/sharethroughBidAdapter.js +12 -17
- package/modules/showheroes-bsBidAdapter.js +13 -2
- package/modules/synacormediaBidAdapter.js +31 -10
- package/modules/tappxBidAdapter.js +8 -5
- package/modules/teadsBidAdapter.js +1 -2
- package/modules/telariaBidAdapter.js +2 -2
- package/modules/trustxBidAdapter.js +8 -16
- package/modules/userId/eids.js +7 -1
- package/modules/userId/userId.md +8 -0
- package/modules/viewability.js +177 -0
- package/modules/viewability.md +87 -0
- package/modules/welectBidAdapter.js +106 -0
- package/modules/yieldmoBidAdapter.js +23 -5
- package/modules/zeta_global_sspAnalyticsAdapter.js +97 -0
- package/modules/zeta_global_sspAnalyticsAdapter.md +24 -0
- package/package.json +1 -1
- package/src/auction.js +2 -2
- package/src/config.js +27 -3
- package/src/hook.js +5 -1
- package/src/prebid.js +20 -4
- package/src/secureCreatives.js +3 -2
- package/src/utils.js +12 -1
- package/test/helpers/prebidGlobal.js +1 -0
- package/test/spec/config_spec.js +279 -0
- package/test/spec/modules/adbookpspBidAdapter_spec.js +17 -3
- package/test/spec/modules/adhashBidAdapter_spec.js +2 -2
- package/test/spec/modules/adlooxAnalyticsAdapter_spec.js +6 -6
- package/test/spec/modules/admanBidAdapter_spec.js +2 -2
- package/test/spec/modules/adomikAnalyticsAdapter_spec.js +9 -1
- package/test/spec/modules/adqueryIdSystem_spec.js +74 -0
- package/test/spec/modules/adyoulikeBidAdapter_spec.js +49 -0
- package/test/spec/modules/beopBidAdapter_spec.js +1 -1
- package/test/spec/modules/bidViewabilityIO_spec.js +2 -2
- package/test/spec/modules/bidViewability_spec.js +4 -4
- package/test/spec/modules/bliinkBidAdapter_spec.js +2 -0
- package/test/spec/modules/colossussspBidAdapter_spec.js +5 -2
- package/test/spec/modules/compassBidAdapter_spec.js +398 -0
- package/test/spec/modules/consentManagement_spec.js +20 -0
- package/test/spec/modules/criteoIdSystem_spec.js +6 -3
- package/test/spec/modules/dailyhuntBidAdapter_spec.js +404 -0
- package/test/spec/modules/docereeBidAdapter_spec.js +9 -1
- package/test/spec/modules/eids_spec.js +15 -0
- package/test/spec/modules/feedadBidAdapter_spec.js +15 -0
- package/test/spec/modules/glimpseBidAdapter_spec.js +0 -18
- package/test/spec/modules/gnetBidAdapter_spec.js +6 -6
- package/test/spec/modules/gptPreAuction_spec.js +177 -2
- package/test/spec/modules/idImportLibrary_spec.js +197 -10
- package/test/spec/modules/improvedigitalBidAdapter_spec.js +45 -1
- package/test/spec/modules/invibesBidAdapter_spec.js +119 -0
- package/test/spec/modules/ixBidAdapter_spec.js +112 -62
- package/test/spec/modules/jwplayerRtdProvider_spec.js +195 -2
- package/test/spec/modules/kargoBidAdapter_spec.js +1 -1
- package/test/spec/modules/limelightDigitalBidAdapter_spec.js +75 -17
- package/test/spec/modules/livewrappedAnalyticsAdapter_spec.js +22 -0
- package/test/spec/modules/livewrappedBidAdapter_spec.js +31 -0
- package/test/spec/modules/loglyliftBidAdapter_spec.js +172 -0
- package/test/spec/modules/nextMillenniumBidAdapter_spec.js +9 -2
- package/test/spec/modules/oguryBidAdapter_spec.js +10 -2
- package/test/spec/modules/optimeraRtdProvider_spec.js +14 -1
- package/test/spec/modules/ozoneBidAdapter_spec.js +43 -31
- package/test/spec/modules/pilotxBidAdapter_spec.js +244 -0
- package/test/spec/modules/pubgeniusBidAdapter_spec.js +3 -3
- package/test/spec/modules/pubmaticAnalyticsAdapter_spec.js +13 -1
- package/test/spec/modules/pubxaiAnalyticsAdapter_spec.js +11 -0
- package/test/spec/modules/realTimeDataModule_spec.js +67 -5
- package/test/spec/modules/richaudienceBidAdapter_spec.js +40 -0
- package/test/spec/modules/riseBidAdapter_spec.js +1 -1
- package/test/spec/modules/rtbhouseBidAdapter_spec.js +20 -0
- package/test/spec/modules/rubiconAnalyticsAdapter_spec.js +30 -0
- package/test/spec/modules/rubiconBidAdapter_spec.js +48 -9
- package/test/spec/modules/sharethroughBidAdapter_spec.js +91 -6
- package/test/spec/modules/showheroes-bsBidAdapter_spec.js +2 -0
- package/test/spec/modules/synacormediaBidAdapter_spec.js +70 -0
- package/test/spec/modules/tappxBidAdapter_spec.js +0 -19
- package/test/spec/modules/teadsBidAdapter_spec.js +14 -59
- package/test/spec/modules/userId_spec.js +68 -19
- package/test/spec/modules/viewability_spec.js +280 -0
- package/test/spec/modules/welectBidAdapter_spec.js +211 -0
- package/test/spec/modules/zeta_global_sspAnalyticsAdapter_spec.js +427 -0
- package/test/spec/unit/pbjs_api_spec.js +3 -1
- package/test/test_deps.js +3 -0
- package/test/test_index.js +1 -3
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { logWarn, logInfo, isStr, isFn, triggerPixel, insertHtmlIntoIframe } from '../src/utils.js';
|
|
2
|
+
import { getGlobal } from '../src/prebidGlobal.js';
|
|
3
|
+
import find from 'core-js-pure/features/array/find.js';
|
|
4
|
+
|
|
5
|
+
export const MODULE_NAME = 'viewability';
|
|
6
|
+
|
|
7
|
+
export function init() {
|
|
8
|
+
(getGlobal()).viewability = {
|
|
9
|
+
startMeasurement: startMeasurement,
|
|
10
|
+
stopMeasurement: stopMeasurement,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
listenMessagesFromCreative();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const observers = {};
|
|
17
|
+
|
|
18
|
+
function isValid(vid, element, tracker, criteria) {
|
|
19
|
+
if (!element) {
|
|
20
|
+
logWarn(`${MODULE_NAME}: no html element provided`);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let validTracker = tracker &&
|
|
25
|
+
((tracker.method === 'img' && isStr(tracker.value)) ||
|
|
26
|
+
(tracker.method === 'js' && isStr(tracker.value)) ||
|
|
27
|
+
(tracker.method === 'callback' && isFn(tracker.value)));
|
|
28
|
+
|
|
29
|
+
if (!validTracker) {
|
|
30
|
+
logWarn(`${MODULE_NAME}: invalid tracker`, tracker);
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!criteria || !criteria.inViewThreshold || !criteria.timeInView) {
|
|
35
|
+
logWarn(`${MODULE_NAME}: missing criteria`, criteria);
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!vid || observers[vid]) {
|
|
40
|
+
logWarn(`${MODULE_NAME}: must provide an unregistered vid`, vid);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function stopObserving(observer, vid, element) {
|
|
48
|
+
observer.unobserve(element);
|
|
49
|
+
observers[vid].done = true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function fireViewabilityTracker(element, tracker) {
|
|
53
|
+
switch (tracker.method) {
|
|
54
|
+
case 'img':
|
|
55
|
+
triggerPixel(tracker.value, () => {
|
|
56
|
+
logInfo(`${MODULE_NAME}: viewability pixel fired`, tracker.value);
|
|
57
|
+
});
|
|
58
|
+
break;
|
|
59
|
+
case 'js':
|
|
60
|
+
insertHtmlIntoIframe(`<script src="${tracker.value}"></script>`);
|
|
61
|
+
break;
|
|
62
|
+
case 'callback':
|
|
63
|
+
tracker.value(element);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function viewabilityCriteriaMet(observer, vid, element, tracker) {
|
|
69
|
+
stopObserving(observer, vid, element);
|
|
70
|
+
fireViewabilityTracker(element, tracker);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Start measuring viewability of an element
|
|
75
|
+
* @typedef {{ method: string='img','js','callback', value: string|function }} ViewabilityTracker { method: 'img', value: 'http://my.tracker/123' }
|
|
76
|
+
* @typedef {{ inViewThreshold: number, timeInView: number }} ViewabilityCriteria { inViewThreshold: 0.5, timeInView: 1000 }
|
|
77
|
+
* @param {string} vid unique viewability identifier
|
|
78
|
+
* @param {HTMLElement} element
|
|
79
|
+
* @param {ViewabilityTracker} tracker
|
|
80
|
+
* @param {ViewabilityCriteria} criteria
|
|
81
|
+
*/
|
|
82
|
+
export function startMeasurement(vid, element, tracker, criteria) {
|
|
83
|
+
if (!isValid(vid, element, tracker, criteria)) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const options = {
|
|
88
|
+
root: null,
|
|
89
|
+
rootMargin: '0px',
|
|
90
|
+
threshold: criteria.inViewThreshold,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
let observer;
|
|
94
|
+
let viewable = false;
|
|
95
|
+
let stateChange = (entries) => {
|
|
96
|
+
viewable = entries[0].isIntersecting;
|
|
97
|
+
|
|
98
|
+
if (viewable) {
|
|
99
|
+
observers[vid].timeoutId = window.setTimeout(() => {
|
|
100
|
+
viewabilityCriteriaMet(observer, vid, element, tracker);
|
|
101
|
+
}, criteria.timeInView);
|
|
102
|
+
} else if (observers[vid].timeoutId) {
|
|
103
|
+
window.clearTimeout(observers[vid].timeoutId);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
observer = new IntersectionObserver(stateChange, options);
|
|
108
|
+
observers[vid] = {
|
|
109
|
+
observer: observer,
|
|
110
|
+
element: element,
|
|
111
|
+
timeoutId: null,
|
|
112
|
+
done: false,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
observer.observe(element);
|
|
116
|
+
|
|
117
|
+
logInfo(`${MODULE_NAME}: startMeasurement called with:`, arguments);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Stop measuring viewability of an element
|
|
122
|
+
* @param {string} vid unique viewability identifier
|
|
123
|
+
*/
|
|
124
|
+
export function stopMeasurement(vid) {
|
|
125
|
+
if (!vid || !observers[vid]) {
|
|
126
|
+
logWarn(`${MODULE_NAME}: must provide a registered vid`, vid);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
observers[vid].observer.unobserve(observers[vid].element);
|
|
131
|
+
if (observers[vid].timeoutId) {
|
|
132
|
+
window.clearTimeout(observers[vid].timeoutId);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// allow the observer under this vid to be created again
|
|
136
|
+
if (!observers[vid].done) {
|
|
137
|
+
delete observers[vid];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function listenMessagesFromCreative() {
|
|
142
|
+
window.addEventListener('message', receiveMessage, false);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Recieve messages from creatives
|
|
147
|
+
* @param {MessageEvent} evt
|
|
148
|
+
*/
|
|
149
|
+
export function receiveMessage(evt) {
|
|
150
|
+
var key = evt.message ? 'message' : 'data';
|
|
151
|
+
var data = {};
|
|
152
|
+
try {
|
|
153
|
+
data = JSON.parse(evt[key]);
|
|
154
|
+
} catch (e) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!data || data.message !== 'Prebid Viewability') {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
switch (data.action) {
|
|
163
|
+
case 'startMeasurement':
|
|
164
|
+
let element = data.elementId && document.getElementById(data.elementId);
|
|
165
|
+
if (!element) {
|
|
166
|
+
element = find(document.getElementsByTagName('IFRAME'), iframe => (iframe.contentWindow || iframe.contentDocument.defaultView) == evt.source);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
startMeasurement(data.vid, element, data.tracker, data.criteria);
|
|
170
|
+
break;
|
|
171
|
+
case 'stopMeasurement':
|
|
172
|
+
stopMeasurement(data.vid);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
init();
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Overview
|
|
2
|
+
|
|
3
|
+
Module Name: Viewability
|
|
4
|
+
|
|
5
|
+
Purpose: Track when a given HTML element becomes viewable
|
|
6
|
+
|
|
7
|
+
Maintainer: atrajkovic@magnite.com
|
|
8
|
+
|
|
9
|
+
# Configuration
|
|
10
|
+
|
|
11
|
+
Module does not need any configuration, as long as you include it in your PBJS bundle.
|
|
12
|
+
Viewability module has only two functions `startMeasurement` and `stopMeasurement` which can be used to enable more complex viewability measurements. Since it allows tracking from within creative (possibly inside a safe frame) this module registers a message listener, for messages with a format that is described bellow.
|
|
13
|
+
|
|
14
|
+
## `startMeasurement`
|
|
15
|
+
|
|
16
|
+
| startMeasurement Arg Object | Scope | Type | Description | Example |
|
|
17
|
+
| --------------------- | -------- | ------------ | -------------------------------------------------------------------------------- | --------- |
|
|
18
|
+
| vid | Required | String | Unique viewability identifier, used to reference particular observer | `"ae0f9"` |
|
|
19
|
+
| element | Required | HTMLElement | Reference to an HTML element that needs to be tracked | `document.getElementById('test_div')` |
|
|
20
|
+
| tracker | Required | ViewabilityTracker | How viewaility event is communicated back to the parties of interest | `{ method: 'img', value: 'http://my.tracker/123' }` |
|
|
21
|
+
| criteria | Required | ViewabilityCriteria| Defines custom viewability criteria using the threshold and duration provided | `{ inViewThreshold: 0.5, timeInView: 1000 }` |
|
|
22
|
+
|
|
23
|
+
| ViewabilityTracker | Scope | Type | Description | Example |
|
|
24
|
+
| --------------------- | -------- | ------------ | -------------------------------------------------------------------------------- | --------- |
|
|
25
|
+
| method | Required | String | Type of method for Tracker | `'img' OR 'js' OR 'callback'` |
|
|
26
|
+
| value | Required | String | URL string for 'img' and 'js' Trackers, or a function for 'callback' Tracker | `'http://my.tracker/123'` |
|
|
27
|
+
|
|
28
|
+
| ViewabilityCriteria | Scope | Type | Description | Example |
|
|
29
|
+
| --------------------- | -------- | ------------ | -------------------------------------------------------------------------------- | --------- |
|
|
30
|
+
| inViewThreshold | Required | Number | Represents a percentage threshold for the Element to be registered as in view | `0.5` |
|
|
31
|
+
| timeInView | Required | Number | Number of milliseconds that a given element needs to be in view continuously, above the threshold | `1000` |
|
|
32
|
+
|
|
33
|
+
## Please Note:
|
|
34
|
+
- `vid` allows for multiple trackers, with different criteria to be registered for a given HTML element, independently. It's not autogenerated by `startMeasurement()`, it needs to be provided by the caller so that it doesn't have to be posted back to the source iframe (in case viewability is started from within the creative).
|
|
35
|
+
- In case of 'callback' method, HTML element is being passed back to the callback function.
|
|
36
|
+
- When a tracker needs to be started, without direct access to pbjs, postMessage mechanism can be used to invoke `startMeasurement`, with a following payload: `vid`, `tracker` and `criteria` as described above, but also with `message: 'Prebid Viewability'` and `action: 'startMeasurement'`. Optionally payload can provide `elementId`, if available at that time (for ad servers where name of the iframe is known, or adservers that render outside an iframe). If `elementId` is not provided, viewability module will try to find the iframe that corresponds to the message source.
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## `stopMeasurement`
|
|
40
|
+
|
|
41
|
+
| stopMeasurement Arg Object | Scope | Type | Description | Example |
|
|
42
|
+
| --------------------- | -------- | ------------ | -------------------------------------------------------------------------------- | --------- |
|
|
43
|
+
| vid | Required | String | Unique viewability identifier, referencing an already started viewability tracker. | `"ae0f9"` |
|
|
44
|
+
|
|
45
|
+
## Please Note:
|
|
46
|
+
- When a tracker needs to be stopped, without direct access to pbjs, postMessage mechanism can be used here as well. To invoke `stopMeasurement`, you provide the payload with `vid`, `message: 'Prebid Viewability'` and `action: 'stopMeasurement`. Check the example bellow.
|
|
47
|
+
|
|
48
|
+
# Examples
|
|
49
|
+
|
|
50
|
+
## Example of starting a viewability measurement, when you have direct access to pbjs
|
|
51
|
+
```
|
|
52
|
+
pbjs.viewability.startMeasurement(
|
|
53
|
+
'ae0f9',
|
|
54
|
+
document.getElementById('test_div'),
|
|
55
|
+
{ method: 'img', value: 'http://my.tracker/123' },
|
|
56
|
+
{ inViewThreshold: 0.5, timeInView: 1000 }
|
|
57
|
+
);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Example of starting a viewability measurement from within a rendered creative
|
|
61
|
+
```
|
|
62
|
+
let viewabilityRecord = {
|
|
63
|
+
vid: 'ae0f9',
|
|
64
|
+
tracker: { method: 'img', value: 'http://my.tracker/123'},
|
|
65
|
+
criteria: { inViewThreshold: 0.5, timeInView: 1000 },
|
|
66
|
+
message: 'Prebid Viewability',
|
|
67
|
+
action: 'startMeasurement'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
window.parent.postMessage(JSON.stringify(viewabilityRecord), '*');
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Example of stopping the viewability measurement, when you have direct access to pbjs
|
|
74
|
+
```
|
|
75
|
+
pbjs.viewability.stopMeasurement('ae0f9');
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Example of stopping the viewability measurement from within a rendered creative
|
|
79
|
+
```
|
|
80
|
+
let viewabilityRecord = {
|
|
81
|
+
vid: 'ae0f9',
|
|
82
|
+
message: 'Prebid Viewability',
|
|
83
|
+
action: 'stopMeasurement'
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
window.parent.postMessage(JSON.stringify(viewabilityRecord), '*');
|
|
87
|
+
```
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { deepAccess } from '../src/utils.js';
|
|
2
|
+
import { registerBidder } from '../src/adapters/bidderFactory.js';
|
|
3
|
+
|
|
4
|
+
const BIDDER_CODE = 'welect';
|
|
5
|
+
const DEFAULT_DOMAIN = 'www.welect.de';
|
|
6
|
+
|
|
7
|
+
export const spec = {
|
|
8
|
+
code: BIDDER_CODE,
|
|
9
|
+
aliases: ['wlt'],
|
|
10
|
+
gvlid: 282,
|
|
11
|
+
supportedMediaTypes: ['video'],
|
|
12
|
+
|
|
13
|
+
// short code
|
|
14
|
+
/**
|
|
15
|
+
* Determines whether or not the given bid request is valid.
|
|
16
|
+
*
|
|
17
|
+
* @param {BidRequest} bid The bid params to validate.
|
|
18
|
+
* @return boolean True if this is a valid bid, and false otherwise.
|
|
19
|
+
*/
|
|
20
|
+
isBidRequestValid: function (bid) {
|
|
21
|
+
return (
|
|
22
|
+
deepAccess(bid, 'mediaTypes.video.context') === 'instream' &&
|
|
23
|
+
!!bid.params.placementId
|
|
24
|
+
);
|
|
25
|
+
},
|
|
26
|
+
/**
|
|
27
|
+
* Make a server request from the list of BidRequests.
|
|
28
|
+
*
|
|
29
|
+
* @param {validBidRequests[]} - an array of bids
|
|
30
|
+
* @return ServerRequest Info describing the request to the server.
|
|
31
|
+
*/
|
|
32
|
+
buildRequests: function (validBidRequests) {
|
|
33
|
+
return validBidRequests.map((bidRequest) => {
|
|
34
|
+
let rawSizes =
|
|
35
|
+
deepAccess(bidRequest, 'mediaTypes.video.playerSize') ||
|
|
36
|
+
bidRequest.sizes;
|
|
37
|
+
let size = rawSizes[0];
|
|
38
|
+
|
|
39
|
+
let domain = bidRequest.params.domain || DEFAULT_DOMAIN;
|
|
40
|
+
|
|
41
|
+
let url = `https://${domain}/api/v2/preflight/${bidRequest.params.placementId}`;
|
|
42
|
+
|
|
43
|
+
let gdprConsent = null;
|
|
44
|
+
|
|
45
|
+
if (bidRequest && bidRequest.gdprConsent) {
|
|
46
|
+
gdprConsent = {
|
|
47
|
+
gdpr_consent: {
|
|
48
|
+
gdprApplies: bidRequest.gdprConsent.gdprApplies,
|
|
49
|
+
tcString: bidRequest.gdprConsent.gdprConsent,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const data = {
|
|
55
|
+
width: size[0],
|
|
56
|
+
height: size[1],
|
|
57
|
+
bid_id: bidRequest.bidId,
|
|
58
|
+
...gdprConsent,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
url: url,
|
|
64
|
+
data: data,
|
|
65
|
+
options: {
|
|
66
|
+
contentType: 'application/json',
|
|
67
|
+
withCredentials: false,
|
|
68
|
+
crossOrigin: true,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
/**
|
|
74
|
+
* Unpack the response from the server into a list of bids.
|
|
75
|
+
*
|
|
76
|
+
* @param {ServerResponse} serverResponse A successful response from the server.
|
|
77
|
+
* @return {Bid[]} An array of bids which were nested inside the server.
|
|
78
|
+
*/
|
|
79
|
+
interpretResponse: function (serverResponse, bidRequest) {
|
|
80
|
+
const responseBody = serverResponse.body;
|
|
81
|
+
|
|
82
|
+
if (typeof responseBody !== 'object' || responseBody.available !== true) {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const bidResponses = [];
|
|
87
|
+
const bidResponse = {
|
|
88
|
+
requestId: responseBody.bidResponse.requestId,
|
|
89
|
+
cpm: responseBody.bidResponse.cpm,
|
|
90
|
+
width: responseBody.bidResponse.width,
|
|
91
|
+
height: responseBody.bidResponse.height,
|
|
92
|
+
creativeId: responseBody.bidResponse.creativeId,
|
|
93
|
+
currency: responseBody.bidResponse.currency,
|
|
94
|
+
netRevenue: responseBody.bidResponse.netRevenue,
|
|
95
|
+
ttl: responseBody.bidResponse.ttl,
|
|
96
|
+
ad: responseBody.bidResponse.ad,
|
|
97
|
+
vastUrl: responseBody.bidResponse.vastUrl,
|
|
98
|
+
meta: {
|
|
99
|
+
advertiserDomains: responseBody.bidResponse.meta.advertiserDomains
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
bidResponses.push(bidResponse);
|
|
103
|
+
return bidResponses;
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
registerBidder(spec);
|
|
@@ -55,13 +55,8 @@ export const spec = {
|
|
|
55
55
|
p: [],
|
|
56
56
|
page_url: bidderRequest.refererInfo.referer,
|
|
57
57
|
bust: new Date().getTime().toString(),
|
|
58
|
-
pr: (LOCAL_WINDOW.document && LOCAL_WINDOW.document.referrer) || '',
|
|
59
|
-
scrd: LOCAL_WINDOW.devicePixelRatio || 0,
|
|
60
58
|
dnt: getDNT(),
|
|
61
59
|
description: getPageDescription(),
|
|
62
|
-
title: LOCAL_WINDOW.document.title || '',
|
|
63
|
-
w: LOCAL_WINDOW.innerWidth,
|
|
64
|
-
h: LOCAL_WINDOW.innerHeight,
|
|
65
60
|
userConsent: JSON.stringify({
|
|
66
61
|
// case of undefined, stringify will remove param
|
|
67
62
|
gdprApplies: deepAccess(bidderRequest, 'gdprConsent.gdprApplies') || '',
|
|
@@ -70,6 +65,14 @@ export const spec = {
|
|
|
70
65
|
us_privacy: deepAccess(bidderRequest, 'uspConsent') || ''
|
|
71
66
|
};
|
|
72
67
|
|
|
68
|
+
if (canAccessTopWindow()) {
|
|
69
|
+
serverRequest.pr = (LOCAL_WINDOW.document && LOCAL_WINDOW.document.referrer) || '';
|
|
70
|
+
serverRequest.scrd = LOCAL_WINDOW.devicePixelRatio || 0;
|
|
71
|
+
serverRequest.title = LOCAL_WINDOW.document.title || '';
|
|
72
|
+
serverRequest.w = LOCAL_WINDOW.innerWidth;
|
|
73
|
+
serverRequest.h = LOCAL_WINDOW.innerHeight;
|
|
74
|
+
}
|
|
75
|
+
|
|
73
76
|
const mtp = window.navigator.maxTouchPoints;
|
|
74
77
|
if (mtp) {
|
|
75
78
|
serverRequest.mtp = mtp;
|
|
@@ -609,3 +612,18 @@ function getEids(bidRequest) {
|
|
|
609
612
|
return createEidsArray(bidRequest.userId) || [];
|
|
610
613
|
}
|
|
611
614
|
};
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Check if top window can be accessed
|
|
618
|
+
*
|
|
619
|
+
* @return {boolean} true if can access top window otherwise false
|
|
620
|
+
*/
|
|
621
|
+
function canAccessTopWindow() {
|
|
622
|
+
try {
|
|
623
|
+
if (getWindowTop().location.href) {
|
|
624
|
+
return true;
|
|
625
|
+
}
|
|
626
|
+
} catch (error) {
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { logInfo, logError } from '../src/utils.js';
|
|
2
|
+
import { ajax } from '../src/ajax.js';
|
|
3
|
+
import adapterManager from '../src/adapterManager.js';
|
|
4
|
+
import CONSTANTS from '../src/constants.json';
|
|
5
|
+
|
|
6
|
+
import adapter from '../src/AnalyticsAdapter.js';
|
|
7
|
+
|
|
8
|
+
const ZETA_GVL_ID = 833;
|
|
9
|
+
const ADAPTER_CODE = 'zeta_global_ssp';
|
|
10
|
+
const BASE_URL = 'https://ssp.disqus.com/prebid/event';
|
|
11
|
+
const LOG_PREFIX = 'ZetaGlobalSsp-Analytics: ';
|
|
12
|
+
|
|
13
|
+
/// /////////// VARIABLES ////////////////////////////////////
|
|
14
|
+
|
|
15
|
+
let publisherId; // int
|
|
16
|
+
|
|
17
|
+
/// /////////// HELPER FUNCTIONS /////////////////////////////
|
|
18
|
+
|
|
19
|
+
function sendEvent(eventType, event) {
|
|
20
|
+
ajax(
|
|
21
|
+
BASE_URL + '/' + eventType,
|
|
22
|
+
null,
|
|
23
|
+
JSON.stringify(event)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/// /////////// ADAPTER EVENT HANDLER FUNCTIONS //////////////
|
|
28
|
+
|
|
29
|
+
function adRenderSucceededHandler(args) {
|
|
30
|
+
let eventType = CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED
|
|
31
|
+
logInfo(LOG_PREFIX + 'handle ' + eventType + ' event');
|
|
32
|
+
|
|
33
|
+
sendEvent(eventType, args);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function auctionEndHandler(args) {
|
|
37
|
+
let eventType = CONSTANTS.EVENTS.AUCTION_END;
|
|
38
|
+
logInfo(LOG_PREFIX + 'handle ' + eventType + ' event');
|
|
39
|
+
|
|
40
|
+
sendEvent(eventType, args);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// /////////// ADAPTER DEFINITION ///////////////////////////
|
|
44
|
+
|
|
45
|
+
let baseAdapter = adapter({ analyticsType: 'endpoint' });
|
|
46
|
+
let zetaAdapter = Object.assign({}, baseAdapter, {
|
|
47
|
+
|
|
48
|
+
enableAnalytics(config = {}) {
|
|
49
|
+
let error = false;
|
|
50
|
+
|
|
51
|
+
if (typeof config.options === 'object') {
|
|
52
|
+
if (config.options.sid) {
|
|
53
|
+
publisherId = Number(config.options.sid);
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
logError(LOG_PREFIX + 'Config not found');
|
|
57
|
+
error = true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!publisherId) {
|
|
61
|
+
logError(LOG_PREFIX + 'Missing sid (publisher id)');
|
|
62
|
+
error = true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (error) {
|
|
66
|
+
logError(LOG_PREFIX + 'Analytics is disabled due to error(s)');
|
|
67
|
+
} else {
|
|
68
|
+
baseAdapter.enableAnalytics.call(this, config);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
disableAnalytics() {
|
|
73
|
+
publisherId = undefined;
|
|
74
|
+
baseAdapter.disableAnalytics.apply(this, arguments);
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
track({ eventType, args }) {
|
|
78
|
+
switch (eventType) {
|
|
79
|
+
case CONSTANTS.EVENTS.AD_RENDER_SUCCEEDED:
|
|
80
|
+
adRenderSucceededHandler(args);
|
|
81
|
+
break;
|
|
82
|
+
case CONSTANTS.EVENTS.AUCTION_END:
|
|
83
|
+
auctionEndHandler(args);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
/// /////////// ADAPTER REGISTRATION /////////////////////////
|
|
90
|
+
|
|
91
|
+
adapterManager.registerAnalyticsAdapter({
|
|
92
|
+
adapter: zetaAdapter,
|
|
93
|
+
code: ADAPTER_CODE,
|
|
94
|
+
gvlid: ZETA_GVL_ID
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export default zetaAdapter;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Zeta Global SSP Analytics Adapter
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Module Name: Zeta Global SSP Analytics Adapter\
|
|
6
|
+
Module Type: Analytics Adapter\
|
|
7
|
+
Maintainer: abermanov@zetaglobal.com
|
|
8
|
+
|
|
9
|
+
## Description
|
|
10
|
+
|
|
11
|
+
Analytics Adapter which sends auctionEnd and adRenderSucceeded events to Zeta Global SSP analytics endpoints
|
|
12
|
+
|
|
13
|
+
## How to configure
|
|
14
|
+
```
|
|
15
|
+
pbjs.enableAnalytics({
|
|
16
|
+
provider: 'zeta_global_ssp',
|
|
17
|
+
options: {
|
|
18
|
+
sid: 111,
|
|
19
|
+
tags: {
|
|
20
|
+
...
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
```
|
package/package.json
CHANGED
package/src/auction.js
CHANGED
|
@@ -569,7 +569,7 @@ function setupBidTargeting(bidObject, bidderRequest) {
|
|
|
569
569
|
let keyValues;
|
|
570
570
|
const cpmCheck = (isAllowZeroCpmBidsEnabled(bidObject.bidderCode)) ? bidObject.cpm >= 0 : bidObject.cpm > 0;
|
|
571
571
|
if (bidObject.bidderCode && (cpmCheck || bidObject.dealId)) {
|
|
572
|
-
let bidReq = find(bidderRequest.bids, bid => bid.adUnitCode === bidObject.adUnitCode);
|
|
572
|
+
let bidReq = find(bidderRequest.bids, bid => bid.adUnitCode === bidObject.adUnitCode && bid.bidId === bidObject.requestId);
|
|
573
573
|
keyValues = getKeyValueTargetingPairs(bidObject.bidderCode, bidObject, bidReq);
|
|
574
574
|
}
|
|
575
575
|
|
|
@@ -744,7 +744,7 @@ function setKeys(keyValues, bidderSettings, custBidObj, bidReq) {
|
|
|
744
744
|
var value = kvPair.val;
|
|
745
745
|
|
|
746
746
|
if (keyValues[key]) {
|
|
747
|
-
logWarn('The key: ' + key + ' is
|
|
747
|
+
logWarn('The key: ' + key + ' is being overwritten');
|
|
748
748
|
}
|
|
749
749
|
|
|
750
750
|
if (isFn(value)) {
|
package/src/config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Module for getting and setting Prebid configuration.
|
|
3
|
-
|
|
3
|
+
*/
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @typedef {Object} MediaTypePriceGranularity
|
|
@@ -580,7 +580,7 @@ export function newConfig() {
|
|
|
580
580
|
.forEach(listener => listener.callback(options));
|
|
581
581
|
}
|
|
582
582
|
|
|
583
|
-
function setBidderConfig(config) {
|
|
583
|
+
function setBidderConfig(config, mergeFlag = false) {
|
|
584
584
|
try {
|
|
585
585
|
check(config);
|
|
586
586
|
config.bidders.forEach(bidder => {
|
|
@@ -592,7 +592,8 @@ export function newConfig() {
|
|
|
592
592
|
let option = (topic === 'fpd') ? convertFpd(config.config[topic]) : config.config[topic];
|
|
593
593
|
|
|
594
594
|
if (isPlainObject(option)) {
|
|
595
|
-
|
|
595
|
+
const func = mergeFlag ? mergeDeep : Object.assign;
|
|
596
|
+
bidderConfig[bidder][prop] = func({}, bidderConfig[bidder][prop] || {}, option);
|
|
596
597
|
} else {
|
|
597
598
|
bidderConfig[bidder][prop] = option;
|
|
598
599
|
}
|
|
@@ -601,6 +602,7 @@ export function newConfig() {
|
|
|
601
602
|
} catch (e) {
|
|
602
603
|
logError(e);
|
|
603
604
|
}
|
|
605
|
+
|
|
604
606
|
function check(obj) {
|
|
605
607
|
if (!isPlainObject(obj)) {
|
|
606
608
|
throw 'setBidderConfig bidder options must be an object';
|
|
@@ -614,6 +616,26 @@ export function newConfig() {
|
|
|
614
616
|
}
|
|
615
617
|
}
|
|
616
618
|
|
|
619
|
+
function mergeConfig(obj) {
|
|
620
|
+
if (!isPlainObject(obj)) {
|
|
621
|
+
logError('mergeConfig input must be an object');
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const mergedConfig = Object.keys(obj).reduce((accum, key) => {
|
|
626
|
+
const prevConf = _getConfig(key)[key] || {};
|
|
627
|
+
accum[key] = mergeDeep(prevConf, obj[key]);
|
|
628
|
+
return accum;
|
|
629
|
+
}, {});
|
|
630
|
+
|
|
631
|
+
setConfig({ ...mergedConfig });
|
|
632
|
+
return mergedConfig;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function mergeBidderConfig(obj) {
|
|
636
|
+
return setBidderConfig(obj, true);
|
|
637
|
+
}
|
|
638
|
+
|
|
617
639
|
/**
|
|
618
640
|
* Internal functions for core to execute some synchronous code while having an active bidder set.
|
|
619
641
|
*/
|
|
@@ -653,12 +675,14 @@ export function newConfig() {
|
|
|
653
675
|
getConfig,
|
|
654
676
|
readConfig,
|
|
655
677
|
setConfig,
|
|
678
|
+
mergeConfig,
|
|
656
679
|
setDefaults,
|
|
657
680
|
resetConfig,
|
|
658
681
|
runWithBidder,
|
|
659
682
|
callbackWithBidder,
|
|
660
683
|
setBidderConfig,
|
|
661
684
|
getBidderConfig,
|
|
685
|
+
mergeBidderConfig,
|
|
662
686
|
convertAdUnitFpd,
|
|
663
687
|
getLegacyFpd,
|
|
664
688
|
getLegacyImpFpd
|
package/src/hook.js
CHANGED
|
@@ -13,14 +13,18 @@ export function setupBeforeHookFnOnce(baseFn, hookFn, priority = 15) {
|
|
|
13
13
|
baseFn.before(hookFn, priority);
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
+
const submoduleInstallMap = {};
|
|
16
17
|
|
|
17
|
-
export function module(name, install) {
|
|
18
|
+
export function module(name, install, {postInstallAllowed = false} = {}) {
|
|
18
19
|
hook('async', function (submodules) {
|
|
19
20
|
submodules.forEach(args => install(...args));
|
|
21
|
+
if (postInstallAllowed) submoduleInstallMap[name] = install;
|
|
20
22
|
}, name)([]); // will be queued until hook.ready() called in pbjs.processQueue();
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export function submodule(name, ...args) {
|
|
26
|
+
const install = submoduleInstallMap[name];
|
|
27
|
+
if (install) return install(...args);
|
|
24
28
|
getHook(name).before((next, modules) => {
|
|
25
29
|
modules.push(args);
|
|
26
30
|
next(modules);
|