mk9-prebid
Version:
Header Bidding Management Library
1,107 lines (936 loc) • 34.2 kB
JavaScript
import find from 'core-js-pure/features/array/find.js';
import * as utils from '../src/utils.js';
import { config } from '../src/config.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import { loadExternalScript } from '../src/adloader.js';
import { verify } from 'criteo-direct-rsa-validate/build/verify.js';
import { getStorageManager } from '../src/storageManager.js';
import { getRefererInfo } from '../src/refererDetection.js';
import { createEidsArray } from './userId/eids.js';
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
import { Renderer } from '../src/Renderer.js';
import { OUTSTREAM } from '../src/video.js';
const BIDDER_CODE = 'adagio';
const LOG_PREFIX = 'Adagio:';
const FEATURES_VERSION = '1';
export const ENDPOINT = 'https://mp.4dex.io/prebid';
const SUPPORTED_MEDIA_TYPES = [BANNER, NATIVE, VIDEO];
const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js';
const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript';
const GVLID = 617;
export const storage = getStorageManager(GVLID, 'adagio');
export const RENDERER_URL = 'https://script.4dex.io/outstream-player.js';
const MAX_SESS_DURATION = 30 * 60 * 1000;
const ADAGIO_PUBKEY = 'AL16XT44Sfp+8SHVF1UdC7hydPSMVLMhsYknKDdwqq+0ToDSJrP0+Qh0ki9JJI2uYm/6VEYo8TJED9WfMkiJ4vf02CW3RvSWwc35bif2SK1L8Nn/GfFYr/2/GG/Rm0vUsv+vBHky6nuuYls20Og0HDhMgaOlXoQ/cxMuiy5QSktp';
const ADAGIO_PUBKEY_E = 65537;
const CURRENCY = 'USD';
const DEFAULT_FLOOR = 0.1;
// This provide a whitelist and a basic validation
// of OpenRTB 2.5 options used by the Adagio SSP.
// https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf
export const ORTB_VIDEO_PARAMS = {
'mimes': (value) => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string'),
'minduration': (value) => utils.isInteger(value),
'maxduration': (value) => utils.isInteger(value),
'protocols': (value) => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].indexOf(v) !== -1),
'w': (value) => utils.isInteger(value),
'h': (value) => utils.isInteger(value),
'startdelay': (value) => utils.isInteger(value),
'placement': (value) => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5].indexOf(v) !== -1),
'linearity': (value) => [1, 2].indexOf(value) !== -1,
'skip': (value) => [0, 1].indexOf(value) !== -1,
'skipmin': (value) => utils.isInteger(value),
'skipafter': (value) => utils.isInteger(value),
'sequence': (value) => utils.isInteger(value),
'battr': (value) => Array.isArray(value) && value.every(v => Array.from({length: 17}, (_, i) => i + 1).indexOf(v) !== -1),
'maxextended': (value) => utils.isInteger(value),
'minbitrate': (value) => utils.isInteger(value),
'maxbitrate': (value) => utils.isInteger(value),
'boxingallowed': (value) => [0, 1].indexOf(value) !== -1,
'playbackmethod': (value) => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5, 6].indexOf(v) !== -1),
'playbackend': (value) => [1, 2, 3].indexOf(value) !== -1,
'delivery': (value) => [1, 2, 3].indexOf(value) !== -1,
'pos': (value) => [0, 1, 2, 3, 4, 5, 6, 7].indexOf(value) !== -1,
'api': (value) => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5, 6].indexOf(v) !== -1)
};
let currentWindow;
export const GlobalExchange = (function() {
let features;
let exchangeData = {};
return {
clearFeatures: function() {
features = undefined;
},
clearExchangeData: function() {
exchangeData = {};
},
getOrSetGlobalFeatures: function () {
if (!features) {
features = {
page_dimensions: getPageDimensions().toString(),
viewport_dimensions: getViewPortDimensions().toString(),
user_timestamp: getTimestampUTC().toString(),
dom_loading: getDomLoadingDuration().toString(),
}
}
return features;
},
prepareExchangeData(storageValue) {
const adagioStorage = JSON.parse(storageValue, function(name, value) {
if (name.charAt(0) !== '_' || name === '') {
return value;
}
});
let random = utils.deepAccess(adagioStorage, 'session.rnd');
let newSession = false;
if (internal.isNewSession(adagioStorage)) {
newSession = true;
random = Math.random();
}
const data = {
session: {
new: newSession,
rnd: random
}
}
utils.mergeDeep(exchangeData, adagioStorage, data);
internal.enqueue({
action: 'session',
ts: Date.now(),
data: exchangeData
});
},
getExchangeData() {
return exchangeData
}
};
})();
export function adagioScriptFromLocalStorageCb(ls) {
try {
if (!ls) {
utils.logWarn(`${LOG_PREFIX} script not found.`);
return;
}
const hashRgx = /^(\/\/ hash: (.+)\n)(.+\n)$/;
if (!hashRgx.test(ls)) {
utils.logWarn(`${LOG_PREFIX} no hash found.`);
storage.removeDataFromLocalStorage(ADAGIO_LOCALSTORAGE_KEY);
} else {
const r = ls.match(hashRgx);
const hash = r[2];
const content = r[3];
if (verify(content, hash, ADAGIO_PUBKEY, ADAGIO_PUBKEY_E)) {
utils.logInfo(`${LOG_PREFIX} start script.`);
Function(ls)(); // eslint-disable-line no-new-func
} else {
utils.logWarn(`${LOG_PREFIX} invalid script found.`);
storage.removeDataFromLocalStorage(ADAGIO_LOCALSTORAGE_KEY);
}
}
} catch (err) {
utils.logError(LOG_PREFIX, err);
}
}
export function getAdagioScript() {
storage.getDataFromLocalStorage(ADAGIO_LOCALSTORAGE_KEY, (ls) => {
internal.adagioScriptFromLocalStorageCb(ls);
});
storage.localStorageIsEnabled(isValid => {
if (isValid) {
loadExternalScript(ADAGIO_TAG_URL, BIDDER_CODE);
} else {
// Try-catch to avoid error when 3rd party cookies is disabled (e.g. in privacy mode)
try {
// ensure adagio removing for next time.
// It's an antipattern regarding the TCF2 enforcement logic
// but it's the only way to respect the user choice update.
window.localStorage.removeItem(ADAGIO_LOCALSTORAGE_KEY);
// Extra data from external script.
// This key is removed only if localStorage is not accessible.
window.localStorage.removeItem('adagio');
} catch (e) {
utils.logInfo(`${LOG_PREFIX} unable to clear Adagio scripts from localstorage.`);
}
}
});
}
function canAccessTopWindow() {
try {
if (utils.getWindowTop().location.href) {
return true;
}
} catch (error) {
return false;
}
}
function getCurrentWindow() {
return currentWindow || utils.getWindowSelf();
}
function isSafeFrameWindow() {
const ws = utils.getWindowSelf();
return !!(ws.$sf && ws.$sf.ext);
}
function initAdagio() {
if (canAccessTopWindow()) {
currentWindow = (canAccessTopWindow()) ? utils.getWindowTop() : utils.getWindowSelf();
}
const w = internal.getCurrentWindow();
w.ADAGIO = w.ADAGIO || {};
w.ADAGIO.adUnits = w.ADAGIO.adUnits || {};
w.ADAGIO.pbjsAdUnits = w.ADAGIO.pbjsAdUnits || [];
w.ADAGIO.queue = w.ADAGIO.queue || [];
w.ADAGIO.versions = w.ADAGIO.versions || {};
w.ADAGIO.versions.pbjs = '$prebid.version$';
w.ADAGIO.isSafeFrameWindow = isSafeFrameWindow();
storage.getDataFromLocalStorage('adagio', (storageData) => {
try {
GlobalExchange.prepareExchangeData(storageData);
} catch (e) {
utils.logError(LOG_PREFIX, e);
}
});
getAdagioScript();
}
function enqueue(ob) {
const w = internal.getCurrentWindow();
w.ADAGIO = w.ADAGIO || {};
w.ADAGIO.queue = w.ADAGIO.queue || [];
w.ADAGIO.queue.push(ob);
};
function getPageviewId() {
const w = internal.getCurrentWindow();
w.ADAGIO = w.ADAGIO || {};
w.ADAGIO.pageviewId = w.ADAGIO.pageviewId || utils.generateUUID();
return w.ADAGIO.pageviewId;
};
function getDevice() {
const language = navigator.language ? 'language' : 'userLanguage';
return {
userAgent: navigator.userAgent,
language: navigator[language],
dnt: utils.getDNT() ? 1 : 0,
geo: {},
js: 1
};
};
function getSite(bidderRequest) {
let domain = '';
let page = '';
let referrer = '';
const { refererInfo } = bidderRequest;
if (canAccessTopWindow()) {
const wt = utils.getWindowTop();
domain = wt.location.hostname;
page = wt.location.href;
referrer = wt.document.referrer || '';
} else if (refererInfo.reachedTop) {
const url = utils.parseUrl(refererInfo.referer);
domain = url.hostname;
page = refererInfo.referer;
} else if (refererInfo.stack && refererInfo.stack.length && refererInfo.stack[0]) {
// important note check if refererInfo.stack[0] is 'thruly' because a `null` value
// will be considered as "localhost" by the parseUrl function.
// As the isBidRequestValid returns false when it does not reach the referer
// this should never called.
const url = utils.parseUrl(refererInfo.stack[0]);
domain = url.hostname;
}
return {
domain,
page,
referrer
};
};
function getElementFromTopWindow(element, currentWindow) {
try {
if (utils.getWindowTop() === currentWindow) {
if (!element.getAttribute('id')) {
element.setAttribute('id', `adg-${utils.getUniqueIdentifierStr()}`);
}
return element;
} else {
const frame = currentWindow.frameElement;
const frameClientRect = frame.getBoundingClientRect();
const elementClientRect = element.getBoundingClientRect();
if (frameClientRect.width !== elementClientRect.width || frameClientRect.height !== elementClientRect.height) {
return false;
}
return getElementFromTopWindow(frame, currentWindow.parent);
}
} catch (err) {
utils.logWarn(`${LOG_PREFIX}`, err);
return false;
}
};
function autoDetectAdUnitElementIdFromGpt(adUnitCode) {
const autoDetectedAdUnit = utils.getGptSlotInfoForAdUnitCode(adUnitCode);
if (autoDetectedAdUnit && autoDetectedAdUnit.divId) {
return autoDetectedAdUnit.divId;
}
};
function isRendererPreferredFromPublisher(bidRequest) {
// renderer defined at adUnit level
const adUnitRenderer = utils.deepAccess(bidRequest, 'renderer');
const hasValidAdUnitRenderer = !!(adUnitRenderer && adUnitRenderer.url && adUnitRenderer.render);
// renderer defined at adUnit.mediaTypes level
const mediaTypeRenderer = utils.deepAccess(bidRequest, 'mediaTypes.video.renderer');
const hasValidMediaTypeRenderer = !!(mediaTypeRenderer && mediaTypeRenderer.url && mediaTypeRenderer.render);
return !!(
(hasValidAdUnitRenderer && !(adUnitRenderer.backupOnly === true)) ||
(hasValidMediaTypeRenderer && !(mediaTypeRenderer.backupOnly === true))
);
}
/**
*
* @param {object} adagioStorage
* @returns {boolean}
*/
function isNewSession(adagioStorage) {
const now = Date.now();
const { lastActivityTime, vwSmplg } = utils.deepAccess(adagioStorage, 'session', {});
return (
!utils.isNumber(lastActivityTime) ||
!utils.isNumber(vwSmplg) ||
(now - lastActivityTime) > MAX_SESS_DURATION
)
}
function setPlayerName(bidRequest) {
const playerName = (internal.isRendererPreferredFromPublisher(bidRequest)) ? 'other' : 'adagio';
if (playerName === 'other') {
utils.logWarn(`${LOG_PREFIX} renderer.backupOnly has not been set. Adagio recommends to use its own player to get expected behavior.`);
}
return playerName;
}
export const internal = {
enqueue,
getPageviewId,
getDevice,
getSite,
getElementFromTopWindow,
getRefererInfo,
adagioScriptFromLocalStorageCb,
getCurrentWindow,
canAccessTopWindow,
isRendererPreferredFromPublisher,
isNewSession
};
function _getGdprConsent(bidderRequest) {
if (!utils.deepAccess(bidderRequest, 'gdprConsent')) {
return false;
}
const {
apiVersion,
gdprApplies,
consentString,
allowAuctionWithoutConsent
} = bidderRequest.gdprConsent;
return utils.cleanObj({
apiVersion,
consentString,
consentRequired: gdprApplies ? 1 : 0,
allowAuctionWithoutConsent: allowAuctionWithoutConsent ? 1 : 0
});
}
function _getCoppa() {
return {
required: config.getConfig('coppa') === true ? 1 : 0
};
}
function _getUspConsent(bidderRequest) {
return (utils.deepAccess(bidderRequest, 'uspConsent')) ? { uspConsent: bidderRequest.uspConsent } : false;
}
function _getSchain(bidRequest) {
return utils.deepAccess(bidRequest, 'schain');
}
function _getEids(bidRequest) {
if (utils.deepAccess(bidRequest, 'userId')) {
return createEidsArray(bidRequest.userId);
}
}
function _buildVideoBidRequest(bidRequest) {
const videoAdUnitParams = utils.deepAccess(bidRequest, 'mediaTypes.video', {});
const videoBidderParams = utils.deepAccess(bidRequest, 'params.video', {});
const computedParams = {};
// Special case for playerSize.
// Eeach props will be overrided if they are defined in config.
if (Array.isArray(videoAdUnitParams.playerSize)) {
const tempSize = (Array.isArray(videoAdUnitParams.playerSize[0])) ? videoAdUnitParams.playerSize[0] : videoAdUnitParams.playerSize;
computedParams.w = tempSize[0];
computedParams.h = tempSize[1];
}
const videoParams = {
...computedParams,
...videoAdUnitParams,
...videoBidderParams
};
if (videoParams.context && videoParams.context === OUTSTREAM) {
bidRequest.mediaTypes.video.playerName = setPlayerName(bidRequest);
}
// Only whitelisted OpenRTB options need to be validated.
// Other options will still remain in the `mediaTypes.video` object
// sent in the ad-request, but will be ignored by the SSP.
Object.keys(ORTB_VIDEO_PARAMS).forEach(paramName => {
if (videoParams.hasOwnProperty(paramName)) {
if (ORTB_VIDEO_PARAMS[paramName](videoParams[paramName])) {
bidRequest.mediaTypes.video[paramName] = videoParams[paramName];
} else {
delete bidRequest.mediaTypes.video[paramName];
utils.logWarn(`${LOG_PREFIX} The OpenRTB video param ${paramName} has been skipped due to misformating. Please refer to OpenRTB 2.5 spec.`);
}
}
});
}
function _renderer(bid) {
bid.renderer.push(() => {
if (typeof window.ADAGIO.outstreamPlayer === 'function') {
window.ADAGIO.outstreamPlayer(bid);
} else {
utils.logError(`${LOG_PREFIX} Adagio outstream player is not defined`);
}
});
}
function _parseNativeBidResponse(bid) {
if (!bid.admNative || !Array.isArray(bid.admNative.assets)) {
utils.logError(`${LOG_PREFIX} Invalid native response`);
return;
}
const native = {}
function addAssetDataValue(data) {
const map = {
1: 'sponsoredBy', // sponsored
2: 'body', // desc
3: 'rating',
4: 'likes',
5: 'downloads',
6: 'price',
7: 'salePrice',
8: 'phone',
9: 'address',
10: 'body2', // desc2
11: 'displayUrl',
12: 'cta'
}
if (map.hasOwnProperty(data.type) && typeof data.value === 'string') {
native[map[data.type]] = data.value;
}
}
// assets
bid.admNative.assets.forEach(asset => {
if (asset.title) {
native.title = asset.title.text
} else if (asset.data) {
addAssetDataValue(asset.data)
} else if (asset.img) {
switch (asset.img.type) {
case 1:
native.icon = {
url: asset.img.url,
width: asset.img.w,
height: asset.img.h
};
break;
default:
native.image = {
url: asset.img.url,
width: asset.img.w,
height: asset.img.h
};
break;
}
}
});
if (bid.admNative.link) {
if (bid.admNative.link.url) {
native.clickUrl = bid.admNative.link.url;
}
if (Array.isArray(bid.admNative.link.clicktrackers)) {
native.clickTrackers = bid.admNative.link.clicktrackers
}
}
if (Array.isArray(bid.admNative.eventtrackers)) {
native.impressionTrackers = [];
bid.admNative.eventtrackers.forEach(tracker => {
// Only Impression events are supported. Prebid does not support Viewability events yet.
if (tracker.event !== 1) {
return;
}
// methods:
// 1: image
// 2: js
// note: javascriptTrackers is a string. If there's more than one JS tracker in bid response, the last script will be used.
switch (tracker.method) {
case 1:
native.impressionTrackers.push(tracker.url);
break;
case 2:
native.javascriptTrackers = `<script src=\"${tracker.url}\"></script>`;
break;
}
});
} else {
native.impressionTrackers = Array.isArray(bid.admNative.imptrackers) ? bid.admNative.imptrackers : [];
if (bid.admNative.jstracker) {
native.javascriptTrackers = bid.admNative.jstracker;
}
}
if (bid.admNative.privacy) {
native.privacyLink = bid.admNative.privacy;
}
if (bid.admNative.ext) {
native.ext = {}
if (bid.admNative.ext.bvw) {
native.ext.adagio_bvw = bid.admNative.ext.bvw;
}
}
bid.native = native
}
function _getFloors(bidRequest) {
if (!utils.isFn(bidRequest.getFloor)) {
return false;
}
const floors = [];
const getAndPush = (mediaType, size) => {
const info = bidRequest.getFloor({
currency: CURRENCY,
mediaType,
size: []
});
floors.push(utils.cleanObj({
mt: mediaType,
s: utils.isArray(size) ? `${size[0]}x${size[1]}` : undefined,
f: (!isNaN(info.floor) && info.currency === CURRENCY) ? info.floor : DEFAULT_FLOOR
}));
}
Object.keys(bidRequest.mediaTypes).forEach(mediaType => {
if (SUPPORTED_MEDIA_TYPES.indexOf(mediaType) !== -1) {
const sizeProp = mediaType === VIDEO ? 'playerSize' : 'sizes';
if (bidRequest.mediaTypes[mediaType][sizeProp] && bidRequest.mediaTypes[mediaType][sizeProp].length) {
if (utils.isArray(bidRequest.mediaTypes[mediaType][sizeProp][0])) {
bidRequest.mediaTypes[mediaType][sizeProp].forEach(size => {
getAndPush(mediaType, [size[0], size[1]]);
});
} else {
getAndPush(mediaType, [bidRequest.mediaTypes[mediaType][sizeProp][0], bidRequest.mediaTypes[mediaType][sizeProp][1]]);
}
} else {
getAndPush(mediaType, '*');
}
}
});
return floors;
}
/**
* Try to find the value of `paramName` and set it to adUnit.params if
* it has not already been set.
* This function will check through:
* - bidderSettings object
* - ortb2.site.ext.data FPD…
*
* @param {*} bid
* @param {String} paramName
*/
export function setExtraParam(bid, paramName) {
bid.params = bid.params || {};
// eslint-disable-next-line
if (!!(bid.params[paramName])) {
return;
}
const adgGlobalConf = config.getConfig('adagio') || {};
const ortb2Conf = config.getConfig('ortb2');
const detected = adgGlobalConf[paramName] || utils.deepAccess(ortb2Conf, `site.ext.data.${paramName}`, null);
if (detected) {
bid.params[paramName] = detected;
}
}
function autoFillParams(bid) {
// adUnitElementId …
const adgGlobalConf = config.getConfig('adagio') || {};
bid.params = bid.params || {};
// adgGlobalConf.siteId is a shortcut to facilitate the integration for publisher.
if (adgGlobalConf.siteId) {
bid.params.organizationId = adgGlobalConf.siteId.split(':')[0];
bid.params.site = adgGlobalConf.siteId.split(':')[1];
}
// Edge case. Useful when Prebid Manager cannot handle properly params setting…
if (adgGlobalConf.useAdUnitCodeAsPlacement === true || bid.params.useAdUnitCodeAsPlacement === true) {
bid.params.placement = bid.adUnitCode;
}
bid.params.adUnitElementId = utils.deepAccess(bid, 'ortb2Imp.ext.data.elementId', null) || bid.params.adUnitElementId;
if (!bid.params.adUnitElementId) {
if (adgGlobalConf.useAdUnitCodeAsAdUnitElementId === true || bid.params.useAdUnitCodeAsAdUnitElementId === true) {
bid.params.adUnitElementId = bid.adUnitCode;
} else {
bid.params.adUnitElementId = autoDetectAdUnitElementIdFromGpt(bid.adUnitCode);
}
}
// extra params
setExtraParam(bid, 'environment');
setExtraParam(bid, 'pagetype');
setExtraParam(bid, 'category');
setExtraParam(bid, 'subcategory');
}
function getPageDimensions() {
if (isSafeFrameWindow() || !canAccessTopWindow()) {
return '';
}
// the page dimension can be computed on window.top only.
const wt = utils.getWindowTop();
const body = wt.document.querySelector('body');
if (!body) {
return '';
}
const html = wt.document.documentElement;
const pageWidth = Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth);
const pageHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
return `${pageWidth}x${pageHeight}`;
}
/**
* @todo Move to prebid Core as Utils.
* @returns
*/
function getViewPortDimensions() {
if (!isSafeFrameWindow() && !canAccessTopWindow()) {
return '';
}
const viewportDims = { w: 0, h: 0 };
if (isSafeFrameWindow()) {
const ws = utils.getWindowSelf();
if (typeof ws.$sf.ext.geom !== 'function') {
utils.logWarn(LOG_PREFIX, 'Unable to compute from safeframe api.');
return '';
}
const sfGeom = ws.$sf.ext.geom();
if (!sfGeom || !sfGeom.win) {
utils.logWarn(LOG_PREFIX, 'Unable to compute from safeframe api. Missing `geom().win` property');
return '';
}
viewportDims.w = Math.round(sfGeom.w);
viewportDims.h = Math.round(sfGeom.h);
} else {
// window.top based computing
const wt = utils.getWindowTop();
viewportDims.w = wt.innerWidth;
viewportDims.h = wt.innerHeight;
}
return `${viewportDims.w}x${viewportDims.h}`;
}
function getSlotPosition(adUnitElementId) {
if (!adUnitElementId) {
return '';
}
if (!isSafeFrameWindow() && !canAccessTopWindow()) {
return '';
}
const position = { x: 0, y: 0 };
if (isSafeFrameWindow()) {
const ws = utils.getWindowSelf();
if (typeof ws.$sf.ext.geom !== 'function') {
utils.logWarn(LOG_PREFIX, 'Unable to compute from safeframe api.');
return '';
}
const sfGeom = ws.$sf.ext.geom();
if (!sfGeom || !sfGeom.self) {
utils.logWarn(LOG_PREFIX, 'Unable to compute from safeframe api. Missing `geom().self` property');
return '';
}
position.x = Math.round(sfGeom.t);
position.y = Math.round(sfGeom.l);
} else if (canAccessTopWindow()) {
// window.top based computing
const wt = utils.getWindowTop();
const d = wt.document;
let domElement;
if (utils.inIframe() === true) {
const ws = utils.getWindowSelf();
const currentElement = ws.document.getElementById(adUnitElementId);
domElement = internal.getElementFromTopWindow(currentElement, ws);
} else {
domElement = wt.document.getElementById(adUnitElementId);
}
if (!domElement) {
return '';
}
let box = domElement.getBoundingClientRect();
const docEl = d.documentElement;
const body = d.body;
const clientTop = d.clientTop || body.clientTop || 0;
const clientLeft = d.clientLeft || body.clientLeft || 0;
const scrollTop = wt.pageYOffset || docEl.scrollTop || body.scrollTop;
const scrollLeft = wt.pageXOffset || docEl.scrollLeft || body.scrollLeft;
const elComputedStyle = wt.getComputedStyle(domElement, null);
const elComputedDisplay = elComputedStyle.display || 'block';
const mustDisplayElement = elComputedDisplay === 'none';
if (mustDisplayElement) {
domElement.style = domElement.style || {};
domElement.style.display = 'block';
box = domElement.getBoundingClientRect();
domElement.style.display = elComputedDisplay;
}
position.x = Math.round(box.left + scrollLeft - clientLeft);
position.y = Math.round(box.top + scrollTop - clientTop);
} else {
return '';
}
return `${position.x}x${position.y}`;
}
function getTimestampUTC() {
// timestamp returned in seconds
return Math.floor(new Date().getTime() / 1000) - new Date().getTimezoneOffset() * 60;
}
function getPrintNumber(adUnitCode, bidderRequest) {
if (!bidderRequest.bids || !bidderRequest.bids.length) {
return 1;
}
const adagioBid = find(bidderRequest.bids, bid => bid.adUnitCode === adUnitCode);
return adagioBid.bidRequestsCount || 1;
}
/**
* domLoading feature is computed on window.top if reachable.
*/
function getDomLoadingDuration() {
let domLoadingDuration = -1;
let performance;
performance = (canAccessTopWindow()) ? utils.getWindowTop().performance : utils.getWindowSelf().performance;
if (performance && performance.timing && performance.timing.navigationStart > 0) {
const val = performance.timing.domLoading - performance.timing.navigationStart;
if (val > 0) {
domLoadingDuration = val;
}
}
return domLoadingDuration;
}
function storeRequestInAdagioNS(bidRequest) {
const w = getCurrentWindow();
// Store adUnits config.
// If an adUnitCode has already been stored, it will be replaced.
w.ADAGIO = w.ADAGIO || {};
w.ADAGIO.pbjsAdUnits = w.ADAGIO.pbjsAdUnits.filter((adUnit) => adUnit.code !== bidRequest.adUnitCode);
let printNumber
if (bidRequest.features && bidRequest.features.print_number) {
printNumber = bidRequest.features.print_number;
} else if (bidRequest.params.features && bidRequest.params.features.print_number) {
printNumber = bidRequest.params.features.print_number;
}
w.ADAGIO.pbjsAdUnits.push({
code: bidRequest.adUnitCode,
mediaTypes: bidRequest.mediaTypes || {},
sizes: (bidRequest.mediaTypes && bidRequest.mediaTypes.banner && Array.isArray(bidRequest.mediaTypes.banner.sizes)) ? bidRequest.mediaTypes.banner.sizes : bidRequest.sizes,
bids: [{
bidder: bidRequest.bidder,
params: bidRequest.params // use the updated bid.params object with auto-detected params
}],
auctionId: bidRequest.auctionId,
pageviewId: internal.getPageviewId(),
printNumber
});
// (legacy) Store internal adUnit information
w.ADAGIO.adUnits[bidRequest.adUnitCode] = {
auctionId: bidRequest.auctionId,
pageviewId: internal.getPageviewId(),
printNumber,
};
}
export const spec = {
code: BIDDER_CODE,
gvlid: GVLID,
supportedMediaTypes: SUPPORTED_MEDIA_TYPES,
isBidRequestValid(bid) {
bid.params = bid.params || {};
autoFillParams(bid);
if (!internal.getRefererInfo().reachedTop) {
utils.logWarn(`${LOG_PREFIX} the main page url is unreachabled.`);
// internal.enqueue(debugData());
return false;
}
if (!(bid.params.organizationId && bid.params.site && bid.params.placement)) {
utils.logWarn(`${LOG_PREFIX} at least one required param is missing.`);
// internal.enqueue(debugData());
return false;
}
return true;
},
buildRequests(validBidRequests, bidderRequest) {
const secure = (location.protocol === 'https:') ? 1 : 0;
const device = internal.getDevice();
const site = internal.getSite(bidderRequest);
const pageviewId = internal.getPageviewId();
const gdprConsent = _getGdprConsent(bidderRequest) || {};
const uspConsent = _getUspConsent(bidderRequest) || {};
const coppa = _getCoppa();
const schain = _getSchain(validBidRequests[0]);
const eids = _getEids(validBidRequests[0]) || [];
const adUnits = utils._map(validBidRequests, (bidRequest) => {
const globalFeatures = GlobalExchange.getOrSetGlobalFeatures();
const features = {
...globalFeatures,
print_number: getPrintNumber(bidRequest.adUnitCode, bidderRequest).toString(),
adunit_position: getSlotPosition(bidRequest.params.adUnitElementId) // adUnitElementId à déplacer ???
};
Object.keys(features).forEach((prop) => {
if (features[prop] === '') {
delete features[prop];
}
});
bidRequest.features = features;
internal.enqueue({
action: 'features',
ts: Date.now(),
data: {
features: bidRequest.features,
params: bidRequest.params,
adUnitCode: bidRequest.adUnitCode
}
});
// Handle priceFloors module
bidRequest.floors = _getFloors(bidRequest);
if (utils.deepAccess(bidRequest, 'mediaTypes.video')) {
_buildVideoBidRequest(bidRequest);
}
storeRequestInAdagioNS(bidRequest);
return bidRequest;
});
// Group ad units by organizationId
const groupedAdUnits = adUnits.reduce((groupedAdUnits, adUnit) => {
const adUnitCopy = utils.deepClone(adUnit);
adUnitCopy.params.organizationId = adUnitCopy.params.organizationId.toString();
// remove useless props
delete adUnitCopy.floorData;
delete adUnitCopy.params.siteId;
groupedAdUnits[adUnitCopy.params.organizationId] = groupedAdUnits[adUnitCopy.params.organizationId] || [];
groupedAdUnits[adUnitCopy.params.organizationId].push(adUnitCopy);
return groupedAdUnits;
}, {});
// Build one request per organizationId
const requests = utils._map(Object.keys(groupedAdUnits), organizationId => {
return {
method: 'POST',
url: ENDPOINT,
data: {
id: utils.generateUUID(),
organizationId: organizationId,
secure: secure,
device: device,
site: site,
pageviewId: pageviewId,
adUnits: groupedAdUnits[organizationId],
data: GlobalExchange.getExchangeData(),
regs: {
gdpr: gdprConsent,
coppa: coppa,
ccpa: uspConsent
},
schain: schain,
user: {
eids: eids
},
prebidVersion: '$prebid.version$',
featuresVersion: FEATURES_VERSION
},
options: {
contentType: 'text/plain'
}
};
});
return requests;
},
interpretResponse(serverResponse, bidRequest) {
let bidResponses = [];
try {
const response = serverResponse.body;
if (response) {
if (response.data) {
internal.enqueue({
action: 'ssp-data',
ts: Date.now(),
data: response.data
});
}
if (response.bids) {
response.bids.forEach(bidObj => {
const bidReq = (find(bidRequest.data.adUnits, bid => bid.bidId === bidObj.requestId));
if (bidReq) {
bidObj.meta = utils.deepAccess(bidObj, 'meta', {});
bidObj.meta.mediaType = bidObj.mediaType;
bidObj.meta.advertiserDomains = (Array.isArray(bidObj.aDomain) && bidObj.aDomain.length) ? bidObj.aDomain : [];
if (bidObj.mediaType === VIDEO) {
const mediaTypeContext = utils.deepAccess(bidReq, 'mediaTypes.video.context');
// Adagio SSP returns a `vastXml` only. No `vastUrl` nor `videoCacheKey`.
if (!bidObj.vastUrl && bidObj.vastXml) {
bidObj.vastUrl = 'data:text/xml;charset=utf-8;base64,' + btoa(bidObj.vastXml.replace(/\\"/g, '"'));
}
if (mediaTypeContext === OUTSTREAM) {
bidObj.renderer = Renderer.install({
id: bidObj.requestId,
adUnitCode: bidObj.adUnitCode,
url: bidObj.urlRenderer || RENDERER_URL,
config: {
...utils.deepAccess(bidReq, 'mediaTypes.video'),
...utils.deepAccess(bidObj, 'outstream', {})
}
});
bidObj.renderer.setRender(_renderer);
}
}
if (bidObj.mediaType === NATIVE) {
_parseNativeBidResponse(bidObj);
}
bidObj.site = bidReq.params.site;
bidObj.placement = bidReq.params.placement;
bidObj.pagetype = bidReq.params.pagetype;
bidObj.category = bidReq.params.category;
bidObj.subcategory = bidReq.params.subcategory;
bidObj.environment = bidReq.params.environment;
}
bidResponses.push(bidObj);
});
}
}
} catch (err) {
utils.logError(err);
}
return bidResponses;
},
getUserSyncs(syncOptions, serverResponses) {
if (!serverResponses.length || serverResponses[0].body === '' || !serverResponses[0].body.userSyncs) {
return false;
}
const syncs = serverResponses[0].body.userSyncs.map(sync => ({
type: sync.t === 'p' ? 'image' : 'iframe',
url: sync.u
}));
return syncs;
},
/**
* Handle custom logic in s2s context
*
* @param {*} params
* @param {boolean} isOrtb Is an s2s context
* @param {*} adUnit
* @param {*} bidRequests
* @returns {object} updated params
*/
transformBidParams(params, isOrtb, adUnit, bidRequests) {
const adagioBidderRequest = find(bidRequests, bidRequest => bidRequest.bidderCode === 'adagio');
const adagioBid = find(adagioBidderRequest.bids, bid => bid.adUnitCode === adUnit.code);
if (isOrtb) {
autoFillParams(adagioBid);
adagioBid.params.auctionId = utils.deepAccess(adagioBidderRequest, 'auctionId');
const globalFeatures = GlobalExchange.getOrSetGlobalFeatures();
adagioBid.params.features = {
...globalFeatures,
print_number: getPrintNumber(adagioBid.adUnitCode, adagioBidderRequest).toString(),
adunit_position: getSlotPosition(adagioBid.params.adUnitElementId) // adUnitElementId à déplacer ???
}
adagioBid.params.pageviewId = internal.getPageviewId();
adagioBid.params.prebidVersion = '$prebid.version$';
adagioBid.params.data = GlobalExchange.getExchangeData();
if (utils.deepAccess(adagioBid, 'mediaTypes.video.context') === OUTSTREAM) {
adagioBid.params.playerName = setPlayerName(adagioBid);
}
storeRequestInAdagioNS(adagioBid);
}
return adagioBid.params;
}
};
initAdagio();
registerBidder(spec);