Skip to content

Commit

Permalink
Merge branch 'prebid:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
andrey-ka-97 authored Aug 13, 2021
2 parents 573c409 + 707d73f commit a9c97e6
Show file tree
Hide file tree
Showing 43 changed files with 4,881 additions and 47 deletions.
6 changes: 6 additions & 0 deletions integrationExamples/gpt/userId_example.html
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,13 @@
// To get new token, register https://developer.chrome.com/origintrials/#/trials/active for Federated Learning of Cohorts
token: "A3dHTSoNUMjjERBLlrvJSelNnwWUCwVQhZ5tNQ+sll7y+LkPPVZXtB77u2y7CweRIxiYaGwGXNlW1/dFp8VMEgIAAAB+eyJvcmlnaW4iOiJodHRwczovL3NoYXJlZGlkLm9yZzo0NDMiLCJmZWF0dXJlIjoiSW50ZXJlc3RDb2hvcnRBUEkiLCJleHBpcnkiOjE2MjYyMjA3OTksImlzU3ViZG9tYWluIjp0cnVlLCJpc1RoaXJkUGFydHkiOnRydWV9"
}
},
{
"name": "imuid",
"params": {
"cid": 5126 // Set your Intimate Merger Customer ID here for production
}
}
],
"syncDelay": 5000,
"auctionDelay": 1000
Expand Down
136 changes: 136 additions & 0 deletions integrationExamples/postbid/bidViewabilityIO_example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<html>
<head>
<script>
var pbjs = pbjs || {};
pbjs.que = pbjs.que || [];

(function() {
var pbjsEl = document.createElement("script");
pbjsEl.type = "text/javascript";
pbjsEl.async = true;
pbjsEl.src = '../../build/dev/prebid.js';
var pbjsTargetEl = document.getElementsByTagName("head")[0];
pbjsTargetEl.insertBefore(pbjsEl, pbjsTargetEl.firstChild);
})();

pbjs.que.push(function() {
var adUnits = [
{
code: 'regular_iframe',
mediaTypes: {
banner: {
sizes: [[300, 250]]
}
},
bids: [
{
bidder: 'appnexus',
params: {
placementId: 13144370
}
}
]
},
{
code: 'large_iframe',
mediaTypes: {
banner: {
sizes: [[970, 250]]
}
},
bids: [
{
bidder: 'appnexus',
params: {
placementId: 13144370
}
}
]
},
];

pbjs.setConfig({
bidderTimeout: 1000,
bidViewabilityIO: {
enabled: true,
}
});

pbjs.onEvent('adRenderSucceeded', ({bid}) => {
var p = document.createElement('p');
p.innerHTML = bid.adUnitCode + ' was rendered';
document.getElementById('notes').appendChild(p);
});

pbjs.onEvent('bidViewable', (bid) => {
var p = document.createElement('p');
p.innerHTML = bid.adUnitCode + ' was viewed';
document.getElementById('notes').appendChild(p);
});

pbjs.addAdUnits(adUnits);

pbjs.requestBids({
bidsBackHandler: function(bidResponses) {
Object.keys(bidResponses).forEach(adUnitCode => {
var highestCpmBids = pbjs.getHighestCpmBids(adUnitCode);
var winner = highestCpmBids.pop();
var iframe = document.getElementById(adUnitCode);
var iframeDoc = iframe.contentWindow.document;
if (winner && winner.mediaType === 'banner') {
pbjs.renderAd(iframeDoc, winner.adId);
} else if (winner) {
iframe.width = 300;
iframe.height = 300;
iframeDoc.write('<head></head><body>unsupported mediaType</body>');
iframeDoc.close();
} else {
iframe.width = 300;
iframe.height = 300;
iframeDoc.write('<head></head><body>no winner</body>');
iframeDoc.close();
}
});
}
})
});

</script>

</head>

<body>
<div id="notes" style="position: fixed; right: 0; width: 50%; height: 100%;"></div>

<div style="height: 100%"></div>

<iframe id='regular_iframe'
FRAMEBORDER="0"
SCROLLING="no"
MARGINHEIGHT="0"
MARGINWIDTH="0"
TOPMARGIN="0"
LEFTMARGIN="0"
ALLOWTRANSPARENCY="true"
WIDTH="0"
HEIGHT="0">
</iframe>

<div style="height: 100%"></div>

<iframe id='large_iframe'
FRAMEBORDER="0"
SCROLLING="no"
MARGINHEIGHT="0"
MARGINWIDTH="0"
TOPMARGIN="0"
LEFTMARGIN="0"
ALLOWTRANSPARENCY="true"
WIDTH="0"
HEIGHT="0">
</iframe>

<div style="height: 100%"></div>

</body>
</html>
3 changes: 2 additions & 1 deletion modules/.submodules.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"dmdIdSystem",
"akamaiDAPId",
"flocIdSystem",
"amxIdSystem"
"amxIdSystem",
"imuIdSystem"
],
"adpod": [
"freeWheelAdserverVideo",
Expand Down
5 changes: 5 additions & 0 deletions modules/adfBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export const spec = {
const currency = getConfig('currency.adServerCurrency');
const cur = currency && [ currency ];
const eids = setOnAny(validBidRequests, 'userIdAsEids');
const schain = setOnAny(validBidRequests, 'schain');

const imp = validBidRequests.map((bid, id) => {
bid.netRevenue = pt;
Expand Down Expand Up @@ -206,6 +207,10 @@ export const spec = {
utils.deepSetValue(request, 'user.ext.eids', eids);
}

if (schain) {
utils.deepSetValue(request, 'source.ext.schain', schain);
}

return {
method: 'POST',
url: 'https://' + adxDomain + '/adx/openrtb',
Expand Down
3 changes: 2 additions & 1 deletion modules/adkernelBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ export const spec = {
{code: 'engageadx'},
{code: 'converge', gvlid: 248},
{code: 'adomega'},
{code: 'denakop'}
{code: 'denakop'},
{code: 'rtbanalytica'}
],
supportedMediaTypes: [BANNER, VIDEO, NATIVE],

Expand Down
2 changes: 1 addition & 1 deletion modules/admixerBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {registerBidder} from '../src/adapters/bidderFactory.js';
import {config} from '../src/config.js';

const BIDDER_CODE = 'admixer';
const ALIASES = ['go2net', 'adblender'];
const ALIASES = ['go2net', 'adblender', 'adsyield'];
const ENDPOINT_URL = 'https://inv-nets.admixer.net/prebid.1.2.aspx';
export const spec = {
code: BIDDER_CODE,
Expand Down
13 changes: 12 additions & 1 deletion modules/betweenBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {registerBidder} from '../src/adapters/bidderFactory.js';
import { getAdUnitSizes, parseSizesInput } from '../src/utils.js';
import { getAdUnitSizes, parseSizesInput, deepAccess } from '../src/utils.js';
import { getRefererInfo } from '../src/refererDetection.js';

const BIDDER_CODE = 'between';
Expand Down Expand Up @@ -37,6 +37,8 @@ export const spec = {
tz: getTz(),
fl: getFl(),
rr: getRr(),
shid: getSharedId(i)('id'),
shid3: getSharedId(i)('third'),
s: i.params.s,
bidid: i.bidId,
transactionid: i.transactionId,
Expand Down Expand Up @@ -147,6 +149,15 @@ export const spec = {
}
}

function getSharedId(bid) {
const id = deepAccess(bid, 'userId.sharedid.id');
const third = deepAccess(bid, 'userId.sharedid.third');
return function(kind) {
if (kind === 'id') return id || '';
return third || '';
}
}

function getRr() {
try {
var td = top.document;
Expand Down
91 changes: 91 additions & 0 deletions modules/bidViewabilityIO.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { config } from '../src/config.js';
import * as events from '../src/events.js';
import { EVENTS } from '../src/constants.json';
import * as utils from '../src/utils.js';

const MODULE_NAME = 'bidViewabilityIO';
const CONFIG_ENABLED = 'enabled';

// IAB numbers from: https://support.google.com/admanager/answer/4524488?hl=en
const IAB_VIEWABLE_DISPLAY_TIME = 1000;
const IAB_VIEWABLE_DISPLAY_LARGE_PX = 242000;
export const IAB_VIEWABLE_DISPLAY_THRESHOLD = 0.5
export const IAB_VIEWABLE_DISPLAY_LARGE_THRESHOLD = 0.3;

const CLIENT_SUPPORTS_IO = window.IntersectionObserver && window.IntersectionObserverEntry && window.IntersectionObserverEntry.prototype &&
'intersectionRatio' in window.IntersectionObserverEntry.prototype;

const supportedMediaTypes = [
'banner'
];

export let isSupportedMediaType = (bid) => {
return supportedMediaTypes.indexOf(bid.mediaType) > -1;
}

let logMessage = (message) => {
return utils.logMessage(`${MODULE_NAME}: ${message}`);
}

// returns options for the iO that detects if the ad is viewable
export let getViewableOptions = (bid) => {
if (bid.mediaType === 'banner') {
return {
root: null,
rootMargin: '0px',
threshold: bid.width * bid.height > IAB_VIEWABLE_DISPLAY_LARGE_PX ? IAB_VIEWABLE_DISPLAY_LARGE_THRESHOLD : IAB_VIEWABLE_DISPLAY_THRESHOLD
}
}
}

// markViewed returns a function what will be executed when an ad satisifes the viewable iO
export let markViewed = (bid, entry, observer) => {
return () => {
observer.unobserve(entry.target);
events.emit(EVENTS.BID_VIEWABLE, bid);
logMessage(`id: ${entry.target.getAttribute('id')} code: ${bid.adUnitCode} was viewed`);
}
}

// viewCallbackFactory creates the callback used by the viewable IntersectionObserver.
// When an ad comes into view, it sets a timeout for a function to be executed
// when that ad would be considered viewed per the IAB specs. The bid that was rendered
// is passed into the factory, so it can pass it into markViewed, so that it can be included
// in the BID_VIEWABLE event data. If the ad leaves view before the timer goes off, the setTimeout
// is cancelled, an the bid will not be marked as viewed. There's probably some kind of race-ish
// thing going on between IO and setTimeout but this isn't going to be perfect, it's just going to
// be pretty good.
export let viewCallbackFactory = (bid) => {
return (entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
logMessage(`viewable timer starting for id: ${entry.target.getAttribute('id')} code: ${bid.adUnitCode}`);
entry.target.view_tracker = setTimeout(markViewed(bid, entry, observer), IAB_VIEWABLE_DISPLAY_TIME);
} else {
logMessage(`id: ${entry.target.getAttribute('id')} code: ${bid.adUnitCode} is out of view`);
if (entry.target.view_tracker) {
clearTimeout(entry.target.view_tracker);
logMessage(`viewable timer stopped for id: ${entry.target.getAttribute('id')} code: ${bid.adUnitCode}`);
}
}
});
};
};

export let init = () => {
config.getConfig(MODULE_NAME, conf => {
if (conf[MODULE_NAME][CONFIG_ENABLED] && CLIENT_SUPPORTS_IO) {
// if the module is enabled and the browser supports Intersection Observer,
// then listen to AD_RENDER_SUCCEEDED to setup IO's for supported mediaTypes
events.on(EVENTS.AD_RENDER_SUCCEEDED, ({doc, bid, id}) => {
if (isSupportedMediaType(bid)) {
let viewable = new IntersectionObserver(viewCallbackFactory(bid), getViewableOptions(bid));
let element = document.getElementById(bid.adUnitCode);
viewable.observe(element);
}
});
}
});
}

init()
41 changes: 41 additions & 0 deletions modules/bidViewabilityIO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Overview

Module Name: bidViewabilityIO

Purpose: Emit a BID_VIEWABLE event when a bid becomes viewable using the browsers IntersectionObserver API

Maintainer: [email protected]

# Description
- This module will trigger a BID_VIEWABLE event which other modules, adapters or publisher code can use to get a sense of viewability
- You can check if this module is part of the final build and whether it is enabled or not by accessing ```pbjs.getConfig('bidViewabilityIO')```
- Viewability, as measured by this module is not perfect, nor should it be expected to be.
- The module does not require any specific ad server, or an adserver at all.

# Limitations

- Currently only supports the banner mediaType
- Assumes that the adUnitCode of the ad is also the id attribute of the element that the ad is rendered into.
- Does not make any attempt to ensure that the ad inside that element is itself visible. It assumes that the publisher is operating in good faith.

# Params
- enabled [required] [type: boolean, default: false], when set to true, the module will emit BID_VIEWABLE when applicable

# Example of consuming BID_VIEWABLE event
```
pbjs.onEvent('bidViewable', function(bid){
console.log('got bid details in bidViewable event', bid);
});
```

# Example of using config
```
pbjs.setConfig({
bidViewabilityIO: {
enabled: true,
}
});
```

An example implmentation without an ad server can be found in integrationExamples/postbid/bidViewabilityIO_example.html
Loading

0 comments on commit a9c97e6

Please sign in to comment.