-
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.
* freewheelSSPBidAdapter Update the freewheelSSPBidAdapter based on Prebid 3.0 changes, add max and min size limit as per client's requirement * freewheelSSPBidAdapter fix the path issue * freewheelSSPBidAdapter fix the indentation * update the test * rename freewheel bid adapter name * freewheelSSPBidAdapter use bidderRequest.refererInfo instead of utils.getTopWindowUrl(), remove the unused code * freewheelSSPBidAdapter add spec file * freewheelSSPBidAdapter update the gdpr and refererInfo condition check
- Loading branch information
1 parent
28c6dc5
commit c015d79
Showing
3 changed files
with
608 additions
and
3 deletions.
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,371 @@ | ||
import * as utils from '../src/utils'; | ||
import { registerBidder } from '../src/adapters/bidderFactory'; | ||
|
||
const BIDDER_CODE = 'freewheel-ssp'; | ||
|
||
const PROTOCOL = getProtocol(); | ||
const FREEWHEEL_ADSSETUP = PROTOCOL + '://ads.stickyadstv.com/www/delivery/swfIndex.php'; | ||
const MUSTANG_URL = PROTOCOL + '://cdn.stickyadstv.com/mustang/mustang.min.js'; | ||
const PRIMETIME_URL = PROTOCOL + '://cdn.stickyadstv.com/prime-time/'; | ||
const USER_SYNC_URL = PROTOCOL + '://ads.stickyadstv.com/auto-user-sync'; | ||
|
||
function getProtocol() { | ||
return 'https'; | ||
} | ||
|
||
function isValidUrl(str) { | ||
if (!str) { | ||
return false; | ||
} | ||
|
||
// regExp for url validation | ||
var pattern = /^(https?|ftp|file):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/; | ||
return pattern.test(str); | ||
} | ||
|
||
function getBiggerSize(array) { | ||
var result = [0, 0]; | ||
for (var i = 0; i < array.length; i++) { | ||
if (array[i][0] * array[i][1] > result[0] * result[1]) { | ||
result = array[i]; | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
function getBiggerSizeWithLimit(array, minSizeLimit, maxSizeLimit) { | ||
var minSize = minSizeLimit || [0, 0]; | ||
var maxSize = maxSizeLimit || [Number.MAX_VALUE, Number.MAX_VALUE]; | ||
var candidates = []; | ||
|
||
for (var i = 0; i < array.length; i++) { | ||
if (array[i][0] * array[i][1] >= minSize[0] * minSize[1] && array[i][0] * array[i][1] <= maxSize[0] * maxSize[1]) { | ||
candidates.push(array[i]); | ||
} | ||
} | ||
|
||
return getBiggerSize(candidates); | ||
} | ||
|
||
/* | ||
* read the pricing extension with this format: <Extension type='StickyPricing'><Price currency="EUR">1.0000</Price></Extension> | ||
* @return {object} pricing data in format: {currency: "EUR", price:"1.000"} | ||
*/ | ||
function getPricing(xmlNode) { | ||
var pricingExtNode; | ||
var princingData = {}; | ||
|
||
var extensions = xmlNode.querySelectorAll('Extension'); | ||
// Nodelist.forEach is not supported in IE and Edge | ||
// Workaround given here https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/10638731/ | ||
Array.prototype.forEach.call(extensions, function(node) { | ||
if (node.getAttribute('type') === 'StickyPricing') { | ||
pricingExtNode = node; | ||
} | ||
}); | ||
|
||
if (pricingExtNode) { | ||
var priceNode = pricingExtNode.querySelector('Price'); | ||
princingData = { | ||
currency: priceNode.getAttribute('currency'), | ||
price: priceNode.textContent || priceNode.innerText | ||
}; | ||
} else { | ||
utils.logWarn('PREBID - ' + BIDDER_CODE + ': Can\'t get pricing data. Is price awareness enabled?'); | ||
} | ||
|
||
return princingData; | ||
} | ||
|
||
function hashcode(inputString) { | ||
var hash = 0; | ||
var char; | ||
if (inputString.length == 0) return hash; | ||
for (var i = 0; i < inputString.length; i++) { | ||
char = inputString.charCodeAt(i); | ||
hash = ((hash << 5) - hash) + char; | ||
hash = hash & hash; // Convert to 32bit integer | ||
} | ||
return hash; | ||
} | ||
|
||
function getCreativeId(xmlNode) { | ||
var creaId = ''; | ||
var adNodes = xmlNode.querySelectorAll('Ad'); | ||
// Nodelist.forEach is not supported in IE and Edge | ||
// Workaround given here https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/10638731/ | ||
Array.prototype.forEach.call(adNodes, function(el) { | ||
creaId += '[' + el.getAttribute('id') + ']'; | ||
}); | ||
|
||
return creaId; | ||
} | ||
|
||
/** | ||
* returns the top most accessible window | ||
*/ | ||
function getTopMostWindow() { | ||
var res = window; | ||
|
||
try { | ||
while (top !== res) { | ||
if (res.parent.location.href.length) { res = res.parent; } | ||
} | ||
} catch (e) {} | ||
|
||
return res; | ||
} | ||
|
||
function getComponentId(inputFormat) { | ||
var component = 'mustang'; // default component id | ||
|
||
if (inputFormat && inputFormat !== 'inbanner') { | ||
// format identifiers are equals to their component ids. | ||
component = inputFormat; | ||
} | ||
|
||
return component; | ||
} | ||
|
||
function getAPIName(componentId) { | ||
componentId = componentId || ''; | ||
|
||
// remove dash in componentId to get API name | ||
return componentId.replace('-', ''); | ||
} | ||
|
||
function formatAdHTML(bid, size) { | ||
var integrationType = bid.params.format; | ||
|
||
var divHtml = '<div id="freewheelssp_prebid_target" style="width:' + size[0] + 'px;height:' + size[1] + 'px;"></div>'; | ||
|
||
var script = ''; | ||
var libUrl = ''; | ||
if (integrationType && integrationType !== 'inbanner') { | ||
libUrl = PRIMETIME_URL + getComponentId(bid.params.format) + '.min.js'; | ||
script = getOutstreamScript(bid, size); | ||
} else { | ||
libUrl = MUSTANG_URL; | ||
script = getInBannerScript(bid, size); | ||
} | ||
|
||
return divHtml + | ||
'<script type=\'text/javascript\'>' + | ||
'(function() {' + | ||
' var st = document.createElement(\'script\'); st.type = \'text/javascript\'; st.async = true;' + | ||
' st.src = \'' + libUrl + '\';' + | ||
' st.onload = function(){' + | ||
' var vastLoader = new window.com.stickyadstv.vast.VastLoader();' + | ||
' var vast = vastLoader.getVast();' + | ||
// get the top most accessible window | ||
' var topWindow = (function(){var res=window; try{while(top != res){if(res.parent.location.href.length)res=res.parent;}}catch(e){}return res;})();' + | ||
// inject the xml in the Vast object as string | ||
' vast.setXmlString(topWindow.freewheelssp_cache["' + bid.adUnitCode + '"]);' + | ||
// force ad parsing on the given vast xml | ||
' vastLoader.parseAds(vast, {' + | ||
' onSuccess: function() {' + script + ' }' + | ||
' });' + | ||
' };' + | ||
' document.head.appendChild(st);' + | ||
'})();' + | ||
'</script>'; | ||
} | ||
|
||
var getInBannerScript = function(bid, size) { | ||
return 'var config = {' + | ||
' preloadedVast:vast,' + | ||
' autoPlay:true' + | ||
' };' + | ||
' var ad = new window.com.stickyadstv.vpaid.Ad(document.getElementById("freewheelssp_prebid_target"),config);' + | ||
' (new window.com.stickyadstv.tools.ASLoader(' + bid.params.zoneId + ', \'' + getComponentId(bid.params.format) + '\')).registerEvents(ad);' + | ||
' ad.initAd(' + size[0] + ',' + size[1] + ',"",0,"","");'; | ||
}; | ||
|
||
var getOutstreamScript = function(bid) { | ||
var config = bid.params; | ||
|
||
// default placement if no placement is set | ||
if (!config.hasOwnProperty('domId') && !config.hasOwnProperty('auto') && !config.hasOwnProperty('p') && !config.hasOwnProperty('article')) { | ||
if (config.format === 'intext-roll') { | ||
config.iframeMode = 'dfp'; | ||
} else { | ||
config.domId = 'freewheelssp_prebid_target'; | ||
} | ||
} | ||
|
||
var script = 'var config = {' + | ||
' preloadedVast:vast,' + | ||
' ASLoader:new window.com.stickyadstv.tools.ASLoader(' + bid.params.zoneId + ', \'' + getComponentId(bid.params.format) + '\')'; | ||
|
||
for (var key in config) { | ||
// dont' send format parameter | ||
// neither zone nor vastUrlParams value as Vast is already loaded | ||
if (config.hasOwnProperty(key) && key !== 'format' && key !== 'zone' && key !== 'zoneId' && key !== 'vastUrlParams') { | ||
script += ',' + key + ':"' + config[key] + '"'; | ||
} | ||
} | ||
script += '};' + | ||
|
||
'window.com.stickyadstv.' + getAPIName(bid.params.format) + '.start(config);'; | ||
|
||
return script; | ||
}; | ||
|
||
export const spec = { | ||
code: BIDDER_CODE, | ||
supportedMediaTypes: ['banner', 'video'], | ||
aliases: ['stickyadstv'], // former name for freewheel-ssp | ||
/** | ||
* Determines whether or not the given bid request is valid. | ||
* | ||
* @param {object} bid The bid to validate. | ||
* @return boolean True if this is a valid bid, and false otherwise. | ||
*/ | ||
isBidRequestValid: function(bid) { | ||
return !!(bid.params.zoneId); | ||
}, | ||
|
||
/** | ||
* Make a server request from the list of BidRequests. | ||
* | ||
* @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server. | ||
* @return ServerRequest Info describing the request to the server. | ||
*/ | ||
buildRequests: function(bidRequests, bidderRequest) { | ||
// var currency = config.getConfig(currency); | ||
|
||
var currentBidRequest = bidRequests[0]; | ||
if (bidRequests.length > 1) { | ||
utils.logMessage('Prebid.JS - freewheel bid adapter: only one ad unit is required.'); | ||
} | ||
|
||
var zone = currentBidRequest.params.zoneId; | ||
var timeInMillis = new Date().getTime(); | ||
var keyCode = hashcode(zone + '' + timeInMillis); | ||
var requestParams = { | ||
reqType: 'AdsSetup', | ||
protocolVersion: '2.0', | ||
zoneId: zone, | ||
componentId: getComponentId(currentBidRequest.params.format), | ||
timestamp: timeInMillis, | ||
pKey: keyCode | ||
}; | ||
|
||
// Add GDPR flag and consent string | ||
if (bidderRequest && bidderRequest.gdprConsent) { | ||
requestParams._fw_gdpr_consent = bidderRequest.gdprConsent.consentString; | ||
|
||
if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') { | ||
requestParams._fw_gdpr = bidderRequest.gdprConsent.gdprApplies; | ||
} | ||
} | ||
|
||
if (currentBidRequest.params.gdpr_consented_providers) { | ||
requestParams._fw_gdpr_consented_providers = currentBidRequest.params.gdpr_consented_providers; | ||
} | ||
|
||
var vastParams = currentBidRequest.params.vastUrlParams; | ||
if (typeof vastParams === 'object') { | ||
for (var key in vastParams) { | ||
if (vastParams.hasOwnProperty(key)) { | ||
requestParams[key] = vastParams[key]; | ||
} | ||
} | ||
} | ||
|
||
var location = (bidderRequest && bidderRequest.refererInfo) ? bidderRequest.refererInfo.referer : getTopMostWindow().location.href; | ||
if (isValidUrl(location)) { | ||
requestParams.loc = location; | ||
} | ||
|
||
var playerSize = getBiggerSizeWithLimit(currentBidRequest.mediaTypes.banner.sizes, currentBidRequest.mediaTypes.banner.minSizeLimit, currentBidRequest.mediaTypes.banner.maxSizeLimit); | ||
|
||
if (playerSize[0] > 0 || playerSize[1] > 0) { | ||
requestParams.playerSize = playerSize[0] + 'x' + playerSize[1]; | ||
} | ||
|
||
return { | ||
method: 'GET', | ||
url: FREEWHEEL_ADSSETUP, | ||
data: requestParams, | ||
bidRequest: currentBidRequest | ||
}; | ||
}, | ||
|
||
/** | ||
* Unpack the response from the server into a list of bids. | ||
* | ||
* @param {*} serverResponse A successful response from the server. | ||
* @param {object} request: the built request object containing the initial bidRequest. | ||
* @return {Bid[]} An array of bids which were nested inside the server. | ||
*/ | ||
interpretResponse: function(serverResponse, request) { | ||
var bidrequest = request.bidRequest; | ||
var playerSize = getBiggerSizeWithLimit(bidrequest.mediaTypes.banner.sizes, bidrequest.mediaTypes.banner.minSizeLimit, bidrequest.mediaTypes.banner.maxSizeLimit); | ||
|
||
if (typeof serverResponse == 'object' && typeof serverResponse.body == 'string') { | ||
serverResponse = serverResponse.body; | ||
} | ||
|
||
var xmlDoc; | ||
try { | ||
var parser = new DOMParser(); | ||
xmlDoc = parser.parseFromString(serverResponse, 'application/xml'); | ||
} catch (err) { | ||
utils.logWarn('Prebid.js - ' + BIDDER_CODE + ' : ' + err); | ||
return; | ||
} | ||
|
||
const princingData = getPricing(xmlDoc); | ||
const creativeId = getCreativeId(xmlDoc); | ||
|
||
const topWin = getTopMostWindow(); | ||
if (!topWin.freewheelssp_cache) { | ||
topWin.freewheelssp_cache = {}; | ||
} | ||
topWin.freewheelssp_cache[bidrequest.adUnitCode] = serverResponse; | ||
|
||
const bidResponses = []; | ||
|
||
if (princingData.price) { | ||
const bidResponse = { | ||
requestId: bidrequest.bidId, | ||
cpm: princingData.price, | ||
width: playerSize[0], | ||
height: playerSize[1], | ||
creativeId: creativeId, | ||
currency: princingData.currency, | ||
netRevenue: true, | ||
ttl: 360 | ||
}; | ||
|
||
var mediaTypes = bidrequest.mediaTypes || {}; | ||
if (mediaTypes.video) { | ||
// bidResponse.vastXml = serverResponse; | ||
bidResponse.mediaType = 'video'; | ||
|
||
var blob = new Blob([serverResponse], {type: 'application/xml'}); | ||
bidResponse.vastUrl = window.URL.createObjectURL(blob); | ||
} else { | ||
bidResponse.ad = formatAdHTML(bidrequest, playerSize); | ||
} | ||
|
||
bidResponses.push(bidResponse); | ||
} | ||
|
||
return bidResponses; | ||
}, | ||
|
||
getUserSyncs: function(syncOptions) { | ||
if (syncOptions && syncOptions.pixelEnabled) { | ||
return [{ | ||
type: 'image', | ||
url: USER_SYNC_URL | ||
}]; | ||
} else { | ||
return []; | ||
} | ||
}, | ||
|
||
} | ||
registerBidder(spec); |
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.