Skip to content

Commit

Permalink
dynamicAdBoostModule: New module (#10377)
Browse files Browse the repository at this point in the history
  • Loading branch information
adxpremium committed Oct 30, 2023
1 parent 5bea607 commit 56292c4
Show file tree
Hide file tree
Showing 3 changed files with 231 additions and 0 deletions.
114 changes: 114 additions & 0 deletions modules/dynamicAdBoostRtdProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* The {@link module:modules/realTimeData} module is required
* @module modules/dynamicAdBoost
* @requires module:modules/realTimeData
*/

import { submodule } from '../src/hook.js'
import { loadExternalScript } from '../src/adloader.js';
import { getGlobal } from '../src/prebidGlobal.js';
import { deepAccess, deepSetValue, isEmptyStr } from '../src/utils.js';

const MODULE_NAME = 'dynamicAdBoost';
const SCRIPT_URL = 'https://adxbid.info';
const CLIENT_SUPPORTS_IO = window.IntersectionObserver && window.IntersectionObserverEntry && window.IntersectionObserverEntry.prototype &&
'intersectionRatio' in window.IntersectionObserverEntry.prototype;
// Options for the Intersection Observer
const dabOptions = {
threshold: 0.5 // Trigger callback when 50% of the element is visible
};
let observer;
let dabStartDate;
let dabStartTime;

// Array of div IDs to track
let dynamicAdBoostAdUnits = {};

function init(config, userConsent) {
dabStartDate = new Date();
dabStartTime = dabStartDate.getTime();
if (!CLIENT_SUPPORTS_IO) {
return false;
}
// Create an Intersection Observer instance
observer = new IntersectionObserver(dabHandleIntersection, dabOptions);
if (config.params.keyId) {
let keyId = config.params.keyId;
if (keyId && !isEmptyStr(keyId)) {
let dabDivIdsToTrack = config.params.adUnits;
let dabInterval = setInterval(function() {
// Observe each div by its ID
dabDivIdsToTrack.forEach(divId => {
let div = document.getElementById(divId);
if (div) {
observer.observe(div);
}
});

let dabDateNow = new Date();
let dabTimeNow = dabDateNow.getTime();
let dabElapsedSeconds = Math.floor((dabTimeNow - dabStartTime) / 1000);
let elapsedThreshold = 30;
if (config.params.threshold) {
elapsedThreshold = config.params.threshold;
}
if (dabElapsedSeconds >= elapsedThreshold) {
clearInterval(dabInterval); // Stop
loadLmScript(keyId);
}
}, 1000);

return true;
}
}
return false;
}

function loadLmScript(keyId) {
let viewableAdUnits = Object.keys(dynamicAdBoostAdUnits);
let viewableAdUnitsCSV = viewableAdUnits.join(',');
const scriptUrl = `${SCRIPT_URL}/${keyId}.js?viewableAdUnits=${viewableAdUnitsCSV}`;
loadExternalScript(scriptUrl, MODULE_NAME);
observer.disconnect();
}

function getBidRequestData(reqBidsConfigObj, callback, config, userConsent) {
const reqAdUnits = reqBidsConfigObj.adUnits || getGlobal().adUnits;

if (Array.isArray(reqAdUnits)) {
reqAdUnits.forEach(adunit => {
let gptCode = deepAccess(adunit, 'code');
if (dynamicAdBoostAdUnits.hasOwnProperty(gptCode)) {
// AdUnits has reached target viewablity at some point
deepSetValue(adunit, `ortb2Imp.ext.data.${MODULE_NAME}.${gptCode}`, dynamicAdBoostAdUnits[gptCode]);
}
});
}
callback();
}

let markViewed = (entry, observer) => {
return () => {
observer.unobserve(entry.target);
}
}

// Callback function when an observed element becomes visible
function dabHandleIntersection(entries) {
entries.forEach(entry => {
if (entry.isIntersecting && entry.intersectionRatio > 0.5) {
dynamicAdBoostAdUnits[entry.target.id] = entry.intersectionRatio;
markViewed(entry, observer)
}
});
}

/** @type {RtdSubmodule} */
export const subModuleObj = {
name: MODULE_NAME,
init,
getBidRequestData,
markViewed
};

submodule('realTimeData', subModuleObj);
40 changes: 40 additions & 0 deletions modules/dynamicAdBoostRtdProvider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Overview

Module Name: Dynamic Ad Boost
Module Type: Track when a adunit is viewable
Maintainer: [email protected]

# Description

Enhance your revenue with the cutting-edge DynamicAdBoost module! By seamlessly integrating the powerful LuponMedia technology, our module retrieves adunits viewability data, providing publishers with valuable insights to optimize their revenue streams. To unlock the full potential of this technology, we provide a customized LuponMedia module tailored to your specific site requirements. Boost your ad revenue and gain unprecedented visibility into your performance with our advanced solution.

In order to utilize this module, it is essential to collaborate with [LuponMedia](https://www.luponmedia.com/) to create an account and obtain detailed guidelines on configuring your sites. Working hand in hand with LuponMedia will ensure a smooth integration process, enabling you to fully leverage the capabilities of this module on your website. Take the first step towards optimizing your ad revenue and enhancing your site's performance by partnering with LuponMedia for a seamless experience.
Contact [email protected] for information.

## Building Prebid with Real-time Data Support

First, make sure to add the Dynamic AdBoost submodule to your Prebid.js package with:

`gulp build --modules=rtdModule,dynamicAdBoostRtdProvider`

The following configuration parameters are available:

```
pbjs.setConfig(
...
realTimeData: {
auctionDelay: 2000,
dataProviders: [
{
name: "dynamicAdBoost",
params: {
keyId: "[PROVIDED_KEY]", // Your provided Dynamic AdBoost keyId
adUnits: ["allowedAdUnit1", "allowedAdUnit2"],
threshold: 35 // optional
}
}
]
}
...
}
```
77 changes: 77 additions & 0 deletions test/spec/modules/dynamicAdBoostRtdProvider_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { subModuleObj as rtdProvider } from 'modules/dynamicAdBoostRtdProvider.js';
import { loadExternalScript } from '../../../src/adloader.js';
import { expect } from 'chai';

const configWithParams = {
params: {
keyId: 'dynamic',
adUnits: ['gpt-123'],
threshold: 1
}
};

const configWithoutRequiredParams = {
params: {
keyId: ''
}
};

describe('dynamicAdBoost', function() {
let clock;
let sandbox;
beforeEach(function () {
sandbox = sinon.sandbox.create();
clock = sandbox.useFakeTimers(Date.now());
});
afterEach(function () {
sandbox.restore();
});
describe('init', function() {
describe('initialize without expected params', function() {
it('fails initalize when keyId is not present', function() {
expect(rtdProvider.init(configWithoutRequiredParams)).to.be.false;
})
})

describe('initialize with expected params', function() {
it('successfully initialize with load script', function() {
expect(rtdProvider.init(configWithParams)).to.be.true;
clock.tick(1000);
expect(loadExternalScript.called).to.be.true;
})
});
});
})

describe('markViewed tests', function() {
let sandbox;
const mockObserver = {
unobserve: sinon.spy()
};
const makeElement = (id) => {
const el = document.createElement('div');
el.setAttribute('id', id);
return el;
}
const mockEntry = {
target: makeElement('target_id')
};

beforeEach(function() {
sandbox = sinon.sandbox.create();
})

afterEach(function() {
sandbox.restore()
})

it('markViewed returns a function', function() {
expect(rtdProvider.markViewed(mockEntry, mockObserver)).to.be.a('function')
});

it('markViewed unobserves', function() {
const func = rtdProvider.markViewed(mockEntry, mockObserver);
func();
expect(mockObserver.unobserve.calledOnce).to.be.true;
});
})

0 comments on commit 56292c4

Please sign in to comment.