mk9-prebid
Version:
Header Bidding Management Library
484 lines (424 loc) • 13.7 kB
JavaScript
import * as utils from '../src/utils.js';
import { getGlobal } from '../src/prebidGlobal.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { getStorageManager } from '../src/storageManager.js';
export const storage = getStorageManager();
const BIDDER_CODE = 'eplanning';
const rnd = Math.random();
const DEFAULT_SV = 'ads.us.e-planning.net';
const DEFAULT_ISV = 'i.e-planning.net';
const PARAMS = ['ci', 'sv', 't', 'ml', 'sn'];
const DOLLARS = 'USD';
const NET_REVENUE = true;
const TTL = 120;
const NULL_SIZE = '1x1';
const FILE = 'file';
const STORAGE_RENDER_PREFIX = 'pbsr_';
const STORAGE_VIEW_PREFIX = 'pbvi_';
const mobileUserAgent = isMobileUserAgent();
const PRIORITY_ORDER_FOR_MOBILE_SIZES_ASC = ['1x1', '300x50', '320x50', '300x250'];
const PRIORITY_ORDER_FOR_DESKTOP_SIZES_ASC = ['1x1', '970x90', '970x250', '160x600', '300x600', '728x90', '300x250'];
export const spec = {
code: BIDDER_CODE,
isBidRequestValid: function(bid) {
return Boolean(bid.params.ci) || Boolean(bid.params.t);
},
buildRequests: function(bidRequests, bidderRequest) {
const method = 'GET';
const dfpClientId = '1';
const sec = 'ROS';
let url;
let params;
const urlConfig = getUrlConfig(bidRequests);
const pcrs = getCharset();
const spaces = getSpaces(bidRequests, urlConfig.ml);
const pageUrl = bidderRequest.refererInfo.referer;
const getDomain = (url) => {
let anchor = document.createElement('a');
anchor.href = url;
return anchor.hostname;
}
if (urlConfig.t) {
url = 'https://' + urlConfig.isv + '/layers/t_pbjs_2.json';
params = {};
} else {
url = 'https://' + (urlConfig.sv || DEFAULT_SV) + '/pbjs/1/' + urlConfig.ci + '/' + dfpClientId + '/' + getDomain(pageUrl) + '/' + sec;
const referrerUrl = bidderRequest.refererInfo.referer.reachedTop ? window.top.document.referrer : bidderRequest.refererInfo.referer;
if (storage.hasLocalStorage()) {
registerViewabilityAllBids(bidRequests);
}
params = {
rnd: rnd,
e: spaces.str,
ur: pageUrl || FILE,
pbv: '$prebid.version$',
ncb: '1',
vs: spaces.vs
};
if (pcrs) {
params.crs = pcrs;
}
if (referrerUrl) {
params.fr = referrerUrl;
}
if (bidderRequest && bidderRequest.gdprConsent) {
if (typeof bidderRequest.gdprConsent.gdprApplies !== 'undefined') {
params.gdpr = bidderRequest.gdprConsent.gdprApplies ? '1' : '0';
if (typeof bidderRequest.gdprConsent.consentString !== 'undefined') {
params.gdprcs = bidderRequest.gdprConsent.consentString;
}
}
}
if (bidderRequest && bidderRequest.uspConsent) {
params.ccpa = bidderRequest.uspConsent;
}
if ((getGlobal()).getUserIds && typeof (getGlobal()).getUserIds === 'function') {
const userIds = (getGlobal()).getUserIds();
for (var id in userIds) {
params['e_' + id] = (typeof userIds[id] === 'object') ? encodeURIComponent(JSON.stringify(userIds[id])) : encodeURIComponent(userIds[id]);
}
}
}
return {
method: method,
url: url,
data: params,
adUnitToBidId: spaces.map,
};
},
interpretResponse: function(serverResponse, request) {
const response = serverResponse.body;
let bidResponses = [];
if (response && !utils.isEmpty(response.sp)) {
response.sp.forEach(space => {
if (!utils.isEmpty(space.a)) {
space.a.forEach(ad => {
const bidResponse = {
requestId: request.adUnitToBidId[space.k],
cpm: ad.pr,
width: ad.w,
height: ad.h,
ad: ad.adm,
ttl: TTL,
creativeId: ad.crid,
netRevenue: NET_REVENUE,
currency: DOLLARS,
};
if (ad.adom) {
bidResponse.meta = {
advertiserDomains: ad.adom
};
}
bidResponses.push(bidResponse);
});
}
});
}
return bidResponses;
},
getUserSyncs: function(syncOptions, serverResponses) {
const syncs = [];
const response = !utils.isEmpty(serverResponses) && serverResponses[0].body;
if (response && !utils.isEmpty(response.cs)) {
const responseSyncs = response.cs;
responseSyncs.forEach(sync => {
if (typeof sync === 'string' && syncOptions.pixelEnabled) {
syncs.push({
type: 'image',
url: sync,
});
} else if (typeof sync === 'object' && sync.ifr && syncOptions.iframeEnabled) {
syncs.push({
type: 'iframe',
url: sync.u,
})
}
});
}
return syncs;
},
}
function getUserAgent() {
return window.navigator.userAgent;
}
function getInnerWidth() {
return utils.getWindowSelf().innerWidth;
}
function isMobileUserAgent() {
return getUserAgent().match(/(mobile)|(ip(hone|ad))|(android)|(blackberry)|(nokia)|(phone)|(opera\smini)/i);
}
function isMobileDevice() {
return (getInnerWidth() <= 1024) || window.orientation || mobileUserAgent;
}
function getUrlConfig(bidRequests) {
if (isTestRequest(bidRequests)) {
return getTestConfig(bidRequests.filter(br => br.params.t));
}
let config = {};
bidRequests.forEach(bid => {
PARAMS.forEach(param => {
if (bid.params[param] && !config[param]) {
config[param] = bid.params[param];
}
});
});
return config;
}
function isTestRequest(bidRequests) {
for (let i = 0; i < bidRequests.length; i++) {
if (bidRequests[i].params.t) {
return true;
}
}
return false;
}
function getTestConfig(bidRequests) {
let isv;
bidRequests.forEach(br => isv = isv || br.params.isv);
return {
t: true,
isv: (isv || DEFAULT_ISV)
};
}
function compareSizesByPriority(size1, size2) {
var priorityOrderForSizesAsc = isMobileDevice() ? PRIORITY_ORDER_FOR_MOBILE_SIZES_ASC : PRIORITY_ORDER_FOR_DESKTOP_SIZES_ASC;
var index1 = priorityOrderForSizesAsc.indexOf(size1);
var index2 = priorityOrderForSizesAsc.indexOf(size2);
if (index1 > -1) {
if (index2 > -1) {
return (index1 < index2) ? 1 : -1;
} else {
return -1;
}
} else {
return (index2 > -1) ? 1 : 0;
}
}
function getSizesSortedByPriority(sizes) {
return utils.parseSizesInput(sizes).sort(compareSizesByPriority);
}
function getSize(bid, first) {
var arraySizes = bid.sizes && bid.sizes.length ? getSizesSortedByPriority(bid.sizes) : [];
if (arraySizes.length) {
return first ? arraySizes[0] : arraySizes.join(',');
} else {
return NULL_SIZE;
}
}
function getSpacesStruct(bids) {
let e = {};
bids.forEach(bid => {
let size = getSize(bid, true);
e[size] = e[size] ? e[size] : [];
e[size].push(bid);
});
return e;
}
function cleanName(name) {
return name.replace(/_|\.|-|\//g, '').replace(/\)\(|\(|\)|:/g, '_').replace(/^_+|_+$/g, '');
}
function getSpaces(bidRequests, ml) {
let spacesStruct = getSpacesStruct(bidRequests);
let es = {str: '', vs: '', map: {}};
es.str = Object.keys(spacesStruct).map(size => spacesStruct[size].map((bid, i) => {
es.vs += getVs(bid);
let name;
if (ml) {
name = cleanName(bid.adUnitCode);
} else {
name = (bid.params && bid.params.sn) || (getSize(bid, true) + '_' + i);
}
es.map[name] = bid.bidId;
return name + ':' + getSize(bid);
}).join('+')).join('+');
return es;
}
function getVs(bid) {
let s;
let vs = '';
if (storage.hasLocalStorage()) {
s = getViewabilityData(bid);
vs += s.render >= 4 ? s.ratio.toString(16) : 'F';
} else {
vs += 'F';
}
return vs;
}
function getViewabilityData(bid) {
let r = storage.getDataFromLocalStorage(STORAGE_RENDER_PREFIX + bid.adUnitCode) || 0;
let v = storage.getDataFromLocalStorage(STORAGE_VIEW_PREFIX + bid.adUnitCode) || 0;
let ratio = r > 0 ? (v / r) : 0;
return {
render: r,
ratio: window.parseInt(ratio * 10, 10)
};
}
function getCharset() {
try {
return window.top.document.charset || window.top.document.characterSet;
} catch (e) {
return document.charset || document.characterSet;
}
}
function waitForElementsPresent(elements) {
const observer = new MutationObserver(function (mutationList, observer) {
if (mutationList && Array.isArray(mutationList)) {
mutationList.forEach(mr => {
if (mr && mr.addedNodes && Array.isArray(mr.addedNodes)) {
mr.addedNodes.forEach(ad => {
let index = elements.indexOf(ad.id);
if (index >= 0) {
registerViewability(ad);
elements.splice(index, 1);
if (!elements.length) {
observer.disconnect();
}
}
});
}
});
}
});
document.addEventListener('DOMContentLoaded', function (event) {
var config = {
childList: true,
subtree: true,
characterData: true
};
observer.observe(document.body, config);
});
}
function registerViewability(div) {
visibilityHandler({
name: div.id,
div: div
});
}
function registerViewabilityAllBids(bids) {
let elementsNotPresent = [];
bids.forEach(bid => {
let div = document.getElementById(bid.adUnitCode);
if (div) {
registerViewability(div);
} else {
elementsNotPresent.push(bid.adUnitCode);
}
});
if (elementsNotPresent.length) {
waitForElementsPresent(elementsNotPresent);
}
}
function getViewabilityTracker() {
let TIME_PARTITIONS = 5;
let VIEWABILITY_TIME = 1000;
let VIEWABILITY_MIN_RATIO = 0.5;
let publicApi;
let context;
function segmentIsOutsideTheVisibleRange(visibleRangeEnd, p1, p2) {
return p1 > visibleRangeEnd || p2 < 0;
}
function segmentBeginsBeforeTheVisibleRange(p1) {
return p1 < 0;
}
function segmentEndsAfterTheVisibleRange(visibleRangeEnd, p2) {
return p2 < visibleRangeEnd;
}
function axialVisibilityRatio(visibleRangeEnd, p1, p2) {
let visibilityRatio = 0;
if (!segmentIsOutsideTheVisibleRange(visibleRangeEnd, p1, p2)) {
if (segmentBeginsBeforeTheVisibleRange(p1)) {
visibilityRatio = p2 / (p2 - p1);
} else {
visibilityRatio = segmentEndsAfterTheVisibleRange(visibleRangeEnd, p2) ? 1 : (visibleRangeEnd - p1) / (p2 - p1);
}
}
return visibilityRatio;
}
function isNotHiddenByNonFriendlyIframe() {
try { return (window === window.top) || window.frameElement; } catch (e) {}
}
function defineContext(e) {
try {
context = e && window.document.body.contains(e) ? window : (window.top.document.body.contains(e) ? top : undefined);
} catch (err) {}
return context;
}
function getContext(e) {
return context;
}
function verticalVisibilityRatio(position) {
return axialVisibilityRatio(getContext().innerHeight, position.top, position.bottom);
}
function horizontalVisibilityRatio(position) {
return axialVisibilityRatio(getContext().innerWidth, position.left, position.right);
}
function itIsNotHiddenByBannerAreaPosition(e) {
let position = e.getBoundingClientRect();
return (verticalVisibilityRatio(position) * horizontalVisibilityRatio(position)) > VIEWABILITY_MIN_RATIO;
}
function itIsNotHiddenByDisplayStyleCascade(e) {
return e.offsetHeight > 0 && e.offsetWidth > 0;
}
function itIsNotHiddenByOpacityStyleCascade(e) {
let s = e.style;
let p = e.parentNode;
return !(s && parseFloat(s.opacity) === 0) && (!p || itIsNotHiddenByOpacityStyleCascade(p));
}
function itIsNotHiddenByVisibilityStyleCascade(e) {
return getContext().getComputedStyle(e).visibility !== 'hidden';
}
function itIsNotHiddenByTabFocus() {
try { return getContext().top.document.hasFocus(); } catch (e) {}
}
function isDefined(e) {
return (e !== null) && (typeof e !== 'undefined');
}
function itIsNotHiddenByOrphanBranch() {
return isDefined(getContext());
}
function isContextInAnIframe() {
return isDefined(getContext().frameElement);
}
function processIntervalVisibilityStatus(elapsedVisibleIntervals, element, callback) {
let visibleIntervals = isVisible(element) ? (elapsedVisibleIntervals + 1) : 0;
if (visibleIntervals === TIME_PARTITIONS) {
callback();
} else {
setTimeout(processIntervalVisibilityStatus.bind(this, visibleIntervals, element, callback), VIEWABILITY_TIME / TIME_PARTITIONS);
}
}
function isVisible(element) {
defineContext(element);
return isNotHiddenByNonFriendlyIframe() &&
itIsNotHiddenByOrphanBranch() &&
itIsNotHiddenByTabFocus() &&
itIsNotHiddenByDisplayStyleCascade(element) &&
itIsNotHiddenByVisibilityStyleCascade(element) &&
itIsNotHiddenByOpacityStyleCascade(element) &&
itIsNotHiddenByBannerAreaPosition(element) &&
(!isContextInAnIframe() || isVisible(getContext().frameElement));
}
publicApi = {
isVisible: isVisible,
onView: processIntervalVisibilityStatus.bind(this, 0)
};
return publicApi;
};
function visibilityHandler(obj) {
if (obj.div) {
registerAuction(STORAGE_RENDER_PREFIX + obj.name);
getViewabilityTracker().onView(obj.div, registerAuction.bind(undefined, STORAGE_VIEW_PREFIX + obj.name));
}
}
function registerAuction(storageID) {
let value;
try {
value = storage.getDataFromLocalStorage(storageID);
value = value ? window.parseInt(value, 10) + 1 : 1;
storage.setDataInLocalStorage(storageID, value);
} catch (exc) {
return false;
}
return true;
}
registerBidder(spec);