Skip to content
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

Viewability #12

Merged
merged 7 commits into from
Sep 12, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 121 additions & 14 deletions modules/33acrossBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { uniques } from 'src/utils';
import * as utils from 'src/utils';

const { registerBidder } = require('../src/adapters/bidderFactory');
const { config } = require('../src/config');

const BIDDER_CODE = '33across';
const END_POINT = 'https://ssc.33across.com/api/v1/hb';
const SYNC_ENDPOINT = 'https://de.tynt.com/deb/v2?m=xch&rt=html';
Expand Down Expand Up @@ -28,21 +30,28 @@ function _createBidResponse(response) {
function _createServerRequest(bidRequest, gdprConsent) {
const ttxRequest = {};
const params = bidRequest.params;
const element = document.getElementById(bidRequest.adUnitCode);
const sizes = _transformSizes(bidRequest.sizes);
const minSize = _getMinSize(sizes);

const contributeViewability = ViewabilityContributor(
_getPercentInView(element, window.top, minSize)
);

/*
* Infer data for the request payload
*/
ttxRequest.imp = [];
ttxRequest.imp[0] = {
banner: {
format: bidRequest.sizes.map(_getFormatSize)
format: sizes.map(size => Object.assign(size, {ext: {}}))
},
ext: {
ttx: {
prod: params.productId
}
}
}
};
ttxRequest.site = { id: params.siteId };

// Go ahead send the bidId in request to 33exchange so it's kept track of in the bid response and
Expand All @@ -54,19 +63,18 @@ function _createServerRequest(bidRequest, gdprConsent) {
ext: {
consent: gdprConsent.consentString
}
}
};
ttxRequest.regs = {
ext: {
gdpr: (gdprConsent.gdprApplies === true) ? 1 : 0
}
}
};

// Finally, set the openRTB 'test' param if this is to be a test bid
if (params.test === 1) {
ttxRequest.test = 1;
}


/*
* Now construct the full server request
*/
Expand All @@ -82,7 +90,7 @@ function _createServerRequest(bidRequest, gdprConsent) {
return {
'method': 'POST',
'url': url,
'data': JSON.stringify(ttxRequest),
'data': JSON.stringify(contributeViewability(ttxRequest)),
'options': options
}
}
Expand All @@ -98,12 +106,111 @@ function _createSync(siteId) {
}
}

function _getFormatSize(sizeArr) {
function _getSize(size) {
return {
w: sizeArr[0],
h: sizeArr[1],
ext: {}
w: parseInt(size[0], 10),
h: parseInt(size[1], 10)
}
}

function _getMinSize(sizes) {
return sizes.reduce((min, size) => size.h * size.w < min.h * min.w ? size : min);
}

function _getBoundingBox(element, { w, h } = {}) {
let { width, height, left, top, right, bottom } = element.getBoundingClientRect();

if ((width === 0 || height === 0) && w && h) {
width = w;
height = h;
right = left + w;
bottom = top + h;
}

return { width, height, left, top, right, bottom };
}

function _transformSizes(sizes) {
if (utils.isArray(sizes) && sizes.length === 2 && !utils.isArray(sizes[0])) {
return [_getSize(sizes)];
}

return sizes.map(_getSize);
}

function _getIntersectionOfRects(rects) {
const bbox = {
left: rects[0].left,
right: rects[0].right,
top: rects[0].top,
bottom: rects[0].bottom
};

for (let i = 1; i < rects.length; ++i) {
bbox.left = Math.max(bbox.left, rects[i].left);
bbox.right = Math.min(bbox.right, rects[i].right);

if (bbox.left >= bbox.right) {
return null;
}

bbox.top = Math.max(bbox.top, rects[i].top);
bbox.bottom = Math.min(bbox.bottom, rects[i].bottom);

if (bbox.top >= bbox.bottom) {
return null;
}
}

bbox.width = bbox.right - bbox.left;
bbox.height = bbox.bottom - bbox.top;

return bbox;
}

function _getPercentInView(element, topWin, { w, h } = {}) {
const elementBoundingBox = _getBoundingBox(element, { w, h });

// Obtain the intersection of the element and the viewport
const elementInViewBoundingBox = _getIntersectionOfRects([ {
left: 0,
top: 0,
right: topWin.innerWidth,
bottom: topWin.innerHeight
}, elementBoundingBox ]);

let elementInViewArea, elementTotalArea;

if (elementInViewBoundingBox !== null) {
// Some or all of the element is in view
elementInViewArea = elementInViewBoundingBox.width * elementInViewBoundingBox.height;
elementTotalArea = elementBoundingBox.width * elementBoundingBox.height;

return ((elementInViewArea / elementTotalArea) * 100);
}

// No overlap between element and the viewport; therefore, the element
// lies completely out of view
return 0;
}

/**
* Viewability contribution to request..
*/
function ViewabilityContributor(viewabilityAmount) {
function contributeViewability(ttxRequest) {
const req = Object.assign({}, ttxRequest);
const imp = req.imp = req.imp.map(impItem => Object.assign({}, impItem));
const banner = imp[0].banner = Object.assign({}, imp[0].banner);
const ext = banner.ext = Object.assign({}, banner.ext);
const ttx = ext.ttx = Object.assign({}, ext.ttx);

ttx.viewability = { amount: Math.round(viewabilityAmount) };

return req;
}

return contributeViewability;
}

function isBidRequestValid(bid) {
Expand All @@ -123,9 +230,9 @@ function isBidRequestValid(bid) {
// - the server, at this point, also doesn't need the consent string to handle gdpr compliance. So passing
// value whether set or not, for the sake of future dev.
function buildRequests(bidRequests, bidderRequest) {
const gdprConsent = Object.assign({ consentString: undefined, gdprApplies: false }, bidderRequest && bidderRequest.gdprConsent)
const gdprConsent = Object.assign({ consentString: undefined, gdprApplies: false }, bidderRequest && bidderRequest.gdprConsent);

adapterState.uniqueSiteIds = bidRequests.map(req => req.params.siteId).filter(uniques);
adapterState.uniqueSiteIds = bidRequests.map(req => req.params.siteId).filter(utils.uniques);

return bidRequests.map((req) => {
return _createServerRequest(req, gdprConsent);
Expand Down Expand Up @@ -160,7 +267,7 @@ const spec = {
buildRequests,
interpretResponse,
getUserSyncs
}
};

registerBidder(spec);

Expand Down
Loading