-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PubMatic adapter #1707
PubMatic adapter #1707
Changes from all commits
00b4fb7
aab5071
20267c1
0dbfe6d
4342b12
7b97887
d267afc
d0f23aa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,137 +2,216 @@ var utils = require('src/utils.js'); | |
var bidfactory = require('src/bidfactory.js'); | ||
var bidmanager = require('src/bidmanager.js'); | ||
var adaptermanager = require('src/adaptermanager'); | ||
const constants = require('src/constants.json'); | ||
|
||
/** | ||
* Adapter for requesting bids from Pubmatic. | ||
* | ||
* @returns {{callBids: _callBids}} | ||
* @constructor | ||
*/ | ||
function PubmaticAdapter() { | ||
var bids; | ||
var _pm_pub_id; | ||
var _pm_pub_age; | ||
var _pm_pub_gender; | ||
var _pm_pub_kvs; | ||
var _pm_optimize_adslots = []; | ||
const PubmaticAdapter = function PubmaticAdapter() { | ||
let bids; | ||
let usersync = false; | ||
let _secure = 0; | ||
let _protocol = (window.location.protocol === 'https:' ? (_secure = 1, 'https') : 'http') + '://'; | ||
let iframe; | ||
|
||
function _callBids(params) { | ||
bids = params.bids; | ||
_pm_optimize_adslots = []; | ||
for (var i = 0; i < bids.length; i++) { | ||
var bid = bids[i]; | ||
// bidmanager.pbCallbackMap['' + bid.params.adSlot] = bid; | ||
_pm_pub_id = _pm_pub_id || bid.params.publisherId; | ||
_pm_pub_age = _pm_pub_age || (bid.params.age || ''); | ||
_pm_pub_gender = _pm_pub_gender || (bid.params.gender || ''); | ||
_pm_pub_kvs = _pm_pub_kvs || (bid.params.kvs || ''); | ||
_pm_optimize_adslots.push(bid.params.adSlot); | ||
let dealChannelValues = { | ||
1: 'PMP', | ||
5: 'PREF', | ||
6: 'PMPG' | ||
}; | ||
|
||
let customPars = { | ||
'kadgender': 'gender', | ||
'age': 'kadage', | ||
'dctr': 'dctr', // Custom Targeting | ||
'wiid': 'wiid', // Wrapper Impression ID | ||
'profId': 'profId', // Legacy: Profile ID | ||
'verId': 'verId', // Legacy: version ID | ||
'pmzoneid': { // Zone ID | ||
n: 'pmZoneId', | ||
m: function(zoneId) { | ||
if (utils.isStr(zoneId)) { | ||
return zoneId.split(',').slice(0, 50).join(); | ||
} else { | ||
return undefined; | ||
} | ||
} | ||
} | ||
}; | ||
|
||
function _initConf() { | ||
var conf = {}; | ||
var currTime = new Date(); | ||
|
||
conf.SAVersion = '1100'; | ||
conf.wp = 'PreBid'; | ||
conf.js = 1; | ||
conf.wv = constants.REPO_AND_VERSION; | ||
_secure && (conf.sec = 1); | ||
conf.screenResolution = screen.width + 'x' + screen.height; | ||
conf.ranreq = Math.random(); | ||
conf.inIframe = window != top ? '1' : '0'; | ||
|
||
// istanbul ignore else | ||
if (window.navigator.cookieEnabled === false) { | ||
conf.fpcd = '1'; | ||
} | ||
|
||
// Load pubmatic script in an iframe, because they call document.write | ||
_getBids(); | ||
try { | ||
conf.pageURL = window.top.location.href; | ||
conf.refurl = window.top.document.referrer; | ||
} catch (e) { | ||
conf.pageURL = window.location.href; | ||
conf.refurl = window.document.referrer; | ||
} | ||
|
||
conf.kltstamp = currTime.getFullYear() + | ||
'-' + (currTime.getMonth() + 1) + | ||
'-' + currTime.getDate() + | ||
' ' + currTime.getHours() + | ||
':' + currTime.getMinutes() + | ||
':' + currTime.getSeconds(); | ||
conf.timezone = currTime.getTimezoneOffset() / 60 * -1; | ||
|
||
return conf; | ||
} | ||
|
||
function _getBids() { | ||
// create the iframe | ||
iframe = utils.createInvisibleIframe(); | ||
function _handleCustomParams(params, conf) { | ||
// istanbul ignore else | ||
if (!conf.kadpageurl) { | ||
conf.kadpageurl = conf.pageURL; | ||
} | ||
|
||
var elToAppend = document.getElementsByTagName('head')[0]; | ||
var key, value, entry; | ||
for (key in customPars) { | ||
// istanbul ignore else | ||
if (customPars.hasOwnProperty(key)) { | ||
value = params[key]; | ||
// istanbul ignore else | ||
if (value) { | ||
entry = customPars[key]; | ||
|
||
if (typeof entry === 'object') { | ||
value = entry.m(value, conf); | ||
key = entry.n; | ||
} else { | ||
key = customPars[key]; | ||
} | ||
|
||
if (utils.isStr(value)) { | ||
conf[key] = value; | ||
} else { | ||
utils.logWarn('PubMatic: Ignoring param key: ' + customPars[key] + ', expects string-value, found ' + typeof value); | ||
} | ||
} | ||
} | ||
} | ||
return conf; | ||
} | ||
|
||
// insert the iframe into document | ||
elToAppend.insertBefore(iframe, elToAppend.firstChild); | ||
function _cleanSlot(slotName) { | ||
// istanbul ignore else | ||
if (utils.isStr(slotName)) { | ||
return slotName.replace(/^\s+/g, '').replace(/\s+$/g, ''); | ||
} | ||
return ''; | ||
} | ||
|
||
function _legacyExecution(conf, slots) { | ||
var url = _generateLegacyCall(conf, slots); | ||
iframe = utils.createInvisibleIframe(); | ||
var elToAppend = document.getElementsByTagName('head')[0]; | ||
elToAppend.insertBefore(iframe, elToAppend.firstChild); | ||
var iframeDoc = utils.getIframeDocument(iframe); | ||
iframeDoc.write(_createRequestContent()); | ||
var content = utils.createContentToExecuteExtScriptInFriendlyFrame(url); | ||
content = content.replace(`<!--POST_SCRIPT_TAG_MACRO-->`, `<script>window.parent.$$PREBID_GLOBAL$$.handlePubmaticCallback(window.bidDetailsMap, window.progKeyValueMap);</script>`); | ||
iframeDoc.write(content); | ||
iframeDoc.close(); | ||
} | ||
|
||
function _createRequestContent() { | ||
var content = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"' + | ||
' "http://www.w3.org/TR/html4/loose.dtd"><html><head><base target="_top" /><scr' + | ||
'ipt>inDapIF=true;</scr' + 'ipt></head>'; | ||
content += '<body>'; | ||
content += '<scr' + 'ipt>'; | ||
content += '' + | ||
'window.pm_pub_id = "%%PM_PUB_ID%%";' + | ||
'window.pm_optimize_adslots = [%%PM_OPTIMIZE_ADSLOTS%%];' + | ||
'window.kaddctr = "%%PM_ADDCTR%%";' + | ||
'window.kadgender = "%%PM_GENDER%%";' + | ||
'window.kadage = "%%PM_AGE%%";' + | ||
'window.pm_async_callback_fn = "window.parent.$$PREBID_GLOBAL$$.handlePubmaticCallback";'; | ||
|
||
content += '</scr' + 'ipt>'; | ||
|
||
var map = {}; | ||
map.PM_PUB_ID = _pm_pub_id; | ||
map.PM_ADDCTR = _pm_pub_kvs; | ||
map.PM_GENDER = _pm_pub_gender; | ||
map.PM_AGE = _pm_pub_age; | ||
map.PM_OPTIMIZE_ADSLOTS = _pm_optimize_adslots.map(function (adSlot) { | ||
return "'" + adSlot + "'"; | ||
}).join(','); | ||
|
||
content += '<scr' + 'ipt src="https://ads.pubmatic.com/AdServer/js/gshowad.js"></scr' + 'ipt>'; | ||
content += '<scr' + 'ipt>'; | ||
content += '</scr' + 'ipt>'; | ||
content += '</body></html>'; | ||
content = utils.replaceTokenInString(content, map, '%%'); | ||
|
||
return content; | ||
function _generateLegacyCall(conf, slots) { | ||
var request_url = 'gads.pubmatic.com/AdServer/AdCallAggregator'; | ||
return _protocol + request_url + '?' + utils.parseQueryStringParameters(conf) + 'adslots=' + encodeURIComponent('[' + slots.join(',') + ']'); | ||
} | ||
|
||
$$PREBID_GLOBAL$$.handlePubmaticCallback = function () { | ||
let bidDetailsMap = {}; | ||
let progKeyValueMap = {}; | ||
try { | ||
bidDetailsMap = iframe.contentWindow.bidDetailsMap; | ||
progKeyValueMap = iframe.contentWindow.progKeyValueMap; | ||
} catch (e) { | ||
utils.logError(e, 'Error parsing Pubmatic response'); | ||
function _initUserSync(pubId) { | ||
// istanbul ignore else | ||
if (!usersync) { | ||
var iframe = utils.createInvisibleIframe(); | ||
iframe.src = _protocol + 'ads.pubmatic.com/AdServer/js/showad.js#PIX&kdntuid=1&p=' + pubId; | ||
utils.insertElement(iframe, document); | ||
usersync = true; | ||
} | ||
} | ||
|
||
function _callBids(params) { | ||
var conf = _initConf(); | ||
var slots = []; | ||
|
||
conf.pubId = 0; | ||
bids = params.bids || []; | ||
|
||
for (var i = 0; i < bids.length; i++) { | ||
var bid = bids[i]; | ||
conf.pubId = conf.pubId || bid.params.publisherId; | ||
conf = _handleCustomParams(bid.params, conf); | ||
bid.params.adSlot = _cleanSlot(bid.params.adSlot); | ||
bid.params.adSlot.length && slots.push(bid.params.adSlot); | ||
} | ||
|
||
// istanbul ignore else | ||
if (conf.pubId && slots.length > 0) { | ||
_legacyExecution(conf, slots); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it looks like the legacy path is currently the only supported path (ie it still uses an iframe) is that correct? Is this optimized vs the last version? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Our older implementation used to include an external js library, now we do not include it. |
||
} | ||
|
||
_initUserSync(conf.pubId); | ||
} | ||
|
||
$$PREBID_GLOBAL$$.handlePubmaticCallback = function(bidDetailsMap, progKeyValueMap) { | ||
var i; | ||
var adUnit; | ||
var adUnitInfo; | ||
var bid; | ||
var bidResponseMap = bidDetailsMap || {}; | ||
var bidInfoMap = progKeyValueMap || {}; | ||
var dimensions; | ||
var bidResponseMap = bidDetailsMap; | ||
var bidInfoMap = progKeyValueMap; | ||
|
||
if (!bidResponseMap || !bidInfoMap) { | ||
return; | ||
} | ||
|
||
for (i = 0; i < bids.length; i++) { | ||
var adResponse; | ||
bid = bids[i].params; | ||
|
||
adUnit = bidResponseMap[bid.adSlot] || {}; | ||
|
||
// adUnitInfo example: bidstatus=0;bid=0.0000;bidid=39620189@320x50;wdeal= | ||
|
||
// if using DFP GPT, the params string comes in the format: | ||
// "bidstatus;1;bid;5.0000;bidid;hb_test@468x60;wdeal;" | ||
// the code below detects and handles this. | ||
// istanbul ignore else | ||
if (bidInfoMap[bid.adSlot] && bidInfoMap[bid.adSlot].indexOf('=') === -1) { | ||
bidInfoMap[bid.adSlot] = bidInfoMap[bid.adSlot].replace(/([a-z]+);(.[^;]*)/ig, '$1=$2'); | ||
} | ||
|
||
adUnitInfo = (bidInfoMap[bid.adSlot] || '').split(';').reduce(function (result, pair) { | ||
adUnitInfo = (bidInfoMap[bid.adSlot] || '').split(';').reduce(function(result, pair) { | ||
var parts = pair.split('='); | ||
result[parts[0]] = parts[1]; | ||
return result; | ||
}, {}); | ||
|
||
if (adUnitInfo.bidstatus === '1') { | ||
dimensions = adUnitInfo.bidid.split('@')[1].split('x'); | ||
adResponse = bidfactory.createBid(1); | ||
adResponse.bidderCode = 'pubmatic'; | ||
adResponse.adSlot = bid.adSlot; | ||
adResponse.cpm = Number(adUnitInfo.bid); | ||
adResponse.ad = unescape(adUnit.creative_tag); | ||
adResponse.ad += utils.createTrackPixelIframeHtml(decodeURIComponent(adUnit.tracking_url)); | ||
adResponse.width = dimensions[0]; | ||
adResponse.height = dimensions[1]; | ||
adResponse.width = adUnit.width; | ||
adResponse.height = adUnit.height; | ||
adResponse.dealId = adUnitInfo.wdeal; | ||
adResponse.dealChannel = dealChannelValues[adUnit.deal_channel] || null; | ||
|
||
bidmanager.addBidResponse(bids[i].placementCode, adResponse); | ||
} else { | ||
|
@@ -147,7 +226,7 @@ function PubmaticAdapter() { | |
return { | ||
callBids: _callBids | ||
}; | ||
} | ||
}; | ||
|
||
adaptermanager.registerBidAdapter(new PubmaticAdapter(), 'pubmatic'); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not clear why this is still loaded into an iframe. Can you not use the
ajax
method here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our server response uses global namespace also our server is not supporting CORS.
By calling our server from a friendly-iframe we can get better priority than calling from a script tag thus we are calling our server using an iframe.