-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* introducing a new event, bidViewable * new module: bidViewability * details in bidViewability.md
- Loading branch information
1 parent
0a33349
commit 8f77660
Showing
6 changed files
with
481 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// This module, when included, will trigger a BID_VIEWABLE event which can be consumed by Bidders and Analytics adapters | ||
// GPT API is used to find when a bid is viewable, https://developers.google.com/publisher-tag/reference#googletag.events.impressionviewableevent | ||
// Does not work with other than GPT integration | ||
|
||
import { config } from '../src/config.js'; | ||
import * as events from '../src/events.js'; | ||
import { EVENTS } from '../src/constants.json'; | ||
import { logWarn, isFn, triggerPixel } from '../src/utils.js'; | ||
import { getGlobal } from '../src/prebidGlobal.js'; | ||
import adapterManager, { gdprDataHandler, uspDataHandler } from '../src/adapterManager.js'; | ||
import find from 'core-js-pure/features/array/find.js'; | ||
|
||
const MODULE_NAME = 'bidViewability'; | ||
const CONFIG_ENABLED = 'enabled'; | ||
const CONFIG_FIRE_PIXELS = 'firePixels'; | ||
const CONFIG_CUSTOM_MATCH = 'customMatchFunction'; | ||
const BID_VURL_ARRAY = 'vurls'; | ||
const GPT_IMPRESSION_VIEWABLE_EVENT = 'impressionViewable'; | ||
|
||
export let isBidAdUnitCodeMatchingSlot = (bid, slot) => { | ||
return (slot.getAdUnitPath() === bid.adUnitCode || slot.getSlotElementId() === bid.adUnitCode); | ||
} | ||
|
||
export let getMatchingWinningBidForGPTSlot = (globalModuleConfig, slot) => { | ||
return find(getGlobal().getAllWinningBids(), | ||
// supports custom match function from config | ||
bid => isFn(globalModuleConfig[CONFIG_CUSTOM_MATCH]) | ||
? globalModuleConfig[CONFIG_CUSTOM_MATCH](bid, slot) | ||
: isBidAdUnitCodeMatchingSlot(bid, slot) | ||
) || null; | ||
}; | ||
|
||
export let fireViewabilityPixels = (globalModuleConfig, bid) => { | ||
if (globalModuleConfig[CONFIG_FIRE_PIXELS] === true && bid.hasOwnProperty(BID_VURL_ARRAY)) { | ||
let queryParams = {}; | ||
|
||
const gdprConsent = gdprDataHandler.getConsentData(); | ||
if (gdprConsent) { | ||
if (typeof gdprConsent.gdprApplies === 'boolean') { queryParams.gdpr = Number(gdprConsent.gdprApplies); } | ||
if (gdprConsent.consentString) { queryParams.gdpr_consent = gdprConsent.consentString; } | ||
if (gdprConsent.addtlConsent) { queryParams.addtl_consent = gdprConsent.addtlConsent; } | ||
} | ||
|
||
const uspConsent = uspDataHandler.getConsentData(); | ||
if (uspConsent) { queryParams.us_privacy = uspConsent; } | ||
|
||
bid[BID_VURL_ARRAY].forEach(url => { | ||
// add '?' if not present in URL | ||
if (Object.keys(queryParams).length > 0 && url.indexOf('?') === -1) { | ||
url += '?'; | ||
} | ||
// append all query params, `&key=urlEncoded(value)` | ||
url += Object.keys(queryParams).reduce((prev, key) => prev += `&${key}=${encodeURIComponent(queryParams[key])}`, ''); | ||
triggerPixel(url) | ||
}); | ||
} | ||
}; | ||
|
||
export let logWinningBidNotFound = (slot) => { | ||
logWarn(`bid details could not be found for ${slot.getSlotElementId()}, probable reasons: a non-prebid bid is served OR check the prebid.AdUnit.code to GPT.AdSlot relation.`); | ||
}; | ||
|
||
export let impressionViewableHandler = (globalModuleConfig, slot, event) => { | ||
let respectiveBid = getMatchingWinningBidForGPTSlot(globalModuleConfig, slot); | ||
if (respectiveBid === null) { | ||
logWinningBidNotFound(slot); | ||
} else { | ||
// if config is enabled AND VURL array is present then execute each pixel | ||
fireViewabilityPixels(globalModuleConfig, respectiveBid); | ||
// trigger respective bidder's onBidViewable handler | ||
adapterManager.callBidViewableBidder(respectiveBid.bidder, respectiveBid); | ||
// emit the BID_VIEWABLE event with bid details, this event can be consumed by bidders and analytics pixels | ||
events.emit(EVENTS.BID_VIEWABLE, respectiveBid); | ||
} | ||
}; | ||
|
||
export let init = () => { | ||
events.on(EVENTS.AUCTION_INIT, () => { | ||
// read the config for the module | ||
const globalModuleConfig = config.getConfig(MODULE_NAME) || {}; | ||
// do nothing if module-config.enabled is not set to true | ||
// this way we are adding a way for bidders to know (using pbjs.getConfig('bidViewability').enabled === true) whether this module is added in build and is enabled | ||
if (globalModuleConfig[CONFIG_ENABLED] !== true) { | ||
return; | ||
} | ||
// add the GPT event listener | ||
window.googletag = window.googletag || {}; | ||
window.googletag.cmd = window.googletag.cmd || []; | ||
window.googletag.cmd.push(() => { | ||
window.googletag.pubads().addEventListener(GPT_IMPRESSION_VIEWABLE_EVENT, function(event) { | ||
impressionViewableHandler(globalModuleConfig, event.slot, event); | ||
}); | ||
}); | ||
}); | ||
} | ||
|
||
init() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# Overview | ||
|
||
Module Name: bidViewability | ||
|
||
Purpose: Track when a bid is viewable | ||
|
||
Maintainer: [email protected] | ||
|
||
# Description | ||
- This module, when included, will trigger a BID_VIEWABLE event which can be consumed by Analytics adapters, bidders will need to implement `onBidViewable` method to capture this event | ||
- Bidderes can check if this module is part of the final build and whether it is enabled or not by accessing ```pbjs.getConfig('bidViewability')``` | ||
- GPT API is used to find when a bid is viewable, https://developers.google.com/publisher-tag/reference#googletag.events.impressionviewableevent . This event is fired when an impression becomes viewable, according to the Active View criteria. | ||
Refer: https://support.google.com/admanager/answer/4524488 | ||
- The module does not work with adserver other than GAM with GPT integration | ||
- Logic used to find a matching pbjs-bid for a GPT slot is ``` (slot.getAdUnitPath() === bid.adUnitCode || slot.getSlotElementId() === bid.adUnitCode) ``` this logic can be changed by using param ```customMatchFunction``` | ||
- When a rendered PBJS bid is viewable the module will trigger BID_VIEWABLE event, which can be consumed by bidders and analytics adapters | ||
- For the viewable bid if ```bid.vurls type array``` param is and module config ``` firePixels: true ``` is set then the URLs mentioned in bid.vurls will be executed. Please note that GDPR and USP related parameters will be added to the given URLs | ||
|
||
# Params | ||
- enabled [required] [type: boolean, default: false], when set to true, the module will emit BID_VIEWABLE when applicable | ||
- firePixels [optional] [type: boolean], when set to true, will fire the urls mentioned in bid.vurls which should be array of urls | ||
- customMatchFunction [optional] [type: function(bid, slot)], when passed this function will be used to `find` the matching winning bid for the GPT slot. Default value is ` (bid, slot) => (slot.getAdUnitPath() === bid.adUnitCode || slot.getSlotElementId() === bid.adUnitCode) ` | ||
|
||
# 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({ | ||
bidViewability: { | ||
enabled: true, | ||
firePixels: true, | ||
customMatchFunction: function(bid, slot){ | ||
console.log('using custom match function....'); | ||
return bid.adUnitCode === slot.getAdUnitPath(); | ||
} | ||
} | ||
}); | ||
``` | ||
|
||
# Please Note: | ||
- Doesn't seems to work with Instream Video, https://docs.prebid.org/dev-docs/examples/instream-banner-mix.html as GPT's impressionViewable event is not triggered for instream-video-creative | ||
- Works with Banner, Outsteam, Native creatives | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.