Skip to content

Commit b038669

Browse files
Merge pull request #15 from pm-azhar-mulla/kk-chromeAiModule
Kk chrome ai module
2 parents 436c969 + 149b5d2 commit b038669

File tree

2 files changed

+208
-9
lines changed

2 files changed

+208
-9
lines changed

modules/pubmaticBidAdapter.js

+84-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { logWarn, isStr, isArray, deepAccess, deepSetValue, isBoolean, isInteger, logInfo, logError, deepClone, uniques, generateUUID, isPlainObject, isFn } from '../src/utils.js';
1+
import { logWarn, isStr, isArray, deepAccess, deepSetValue, isBoolean, isInteger, logInfo, logError, deepClone, uniques, generateUUID, isPlainObject, isFn, getWindowTop } from '../src/utils.js';
22
import { registerBidder } from '../src/adapters/bidderFactory.js';
33
import { BANNER, VIDEO, NATIVE, ADPOD } from '../src/mediaTypes.js';
44
import { config } from '../src/config.js';
55
import { Renderer } from '../src/Renderer.js';
66
import { bidderSettings } from '../src/bidderSettings.js';
77
import { ortbConverter } from '../libraries/ortbConverter/converter.js';
88
import { NATIVE_ASSET_TYPES, NATIVE_IMAGE_TYPES, PREBID_NATIVE_DATA_KEYS_TO_ORTB, NATIVE_KEYS_THAT_ARE_NOT_ASSETS, NATIVE_KEYS } from '../src/constants.js';
9+
import { percentInView } from '../libraries/percentInView/percentInView.js';
910

1011
/**
1112
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
@@ -64,19 +65,22 @@ const converter = ortbConverter({
6465
const { kadfloor, currency, adSlot = '', deals, dctr, pmzoneid, hashedKey } = bidRequest.params;
6566
const { adUnitCode, mediaTypes, rtd } = bidRequest;
6667
const imp = buildImp(bidRequest, context);
68+
69+
// Check if the imp object does not have banner, video, or native
70+
71+
if (!imp.hasOwnProperty('banner') && !imp.hasOwnProperty('video') && !imp.hasOwnProperty('native')) {
72+
return null;
73+
}
6774
if (deals) addPMPDeals(imp, deals);
6875
if (dctr) addDealCustomTargetings(imp, dctr);
6976
if (rtd?.jwplayer) addJWPlayerSegmentData(imp, rtd.jwplayer);
7077
imp.bidfloor = _parseSlotParam('kadfloor', kadfloor);
7178
imp.bidfloorcur = currency ? _parseSlotParam('currency', currency) : DEFAULT_CURRENCY;
7279
setFloorInImp(imp, bidRequest);
7380
if (imp.hasOwnProperty('banner')) updateBannerImp(imp.banner, adSlot);
74-
if (imp.hasOwnProperty('video')) updateVideoImp(imp.video, mediaTypes?.video, adUnitCode, imp);
81+
if (imp.hasOwnProperty('video')) updateVideoImp(mediaTypes?.video, adUnitCode, imp);
7582
if (imp.hasOwnProperty('native')) updateNativeImp(imp, mediaTypes?.native);
76-
// Check if the imp object does not have banner, video, or native
77-
if (!imp.hasOwnProperty('banner') && !imp.hasOwnProperty('video') && !imp.hasOwnProperty('native')) {
78-
return null;
79-
}
83+
if (imp.hasOwnProperty('banner') || imp.hasOwnProperty('video')) addViewabilityToImp(imp, adUnitCode, bidRequest?.sizes);
8084
if (pmzoneid) imp.ext.pmZoneId = pmzoneid;
8185
setImpTagId(imp, adSlot.trim(), hashedKey);
8286
setImpFields(imp);
@@ -343,13 +347,15 @@ const updateNativeImp = (imp, nativeParams) => {
343347
}
344348
}
345349

346-
const updateVideoImp = (videoImp, videoParams, adUnitCode, imp) => {
350+
const updateVideoImp = (videoParams, adUnitCode, imp) => {
351+
const videoImp = imp.video;
347352
if (!deepAccess(videoParams, 'plcmt')) {
348353
logWarn(MSG_VIDEO_PLCMT_MISSING + ' for ' + adUnitCode);
349354
};
350355
if (!videoParams || (!videoImp.w && !videoImp.h)) {
351356
delete imp.video;
352357
logWarn(`${LOG_WARN_PREFIX}Error: Missing ${!videoParams ? 'video config params' : 'video size params (playersize or w&h)'} for adunit: ${adUnitCode} with mediaType set as video. Ignoring video impression in the adunit.`);
358+
return;
353359
}
354360
}
355361

@@ -640,6 +646,77 @@ const _handleCustomParams = (params, conf) => {
640646
return conf;
641647
};
642648

649+
function _isIframe() {
650+
try {
651+
return window.self !== window.top;
652+
} catch (e) {
653+
return true;
654+
}
655+
}
656+
657+
/**
658+
* Checks if viewability can be measured for an element
659+
* @param {HTMLElement} element - DOM element to check
660+
* @returns {boolean} True if viewability is measurable
661+
*/
662+
export function _isViewabilityMeasurable(element) {
663+
return !_isIframe() && element !== null;
664+
}
665+
666+
/**
667+
* Gets the viewability percentage of an element
668+
* @param {HTMLElement} element - DOM element to measure
669+
* @param {Window} topWin - Top window object
670+
* @param {Object} size - Size object with width and height
671+
* @returns {number|string} Viewability percentage or 0 if not visible
672+
*/
673+
export function _getViewability(element, topWin, { w, h } = {}) {
674+
return topWin.document.visibilityState === 'visible'
675+
? percentInView(element, { w, h })
676+
: 0;
677+
}
678+
679+
/**
680+
* Gets the minimum size from an array of sizes
681+
* @param {Array} sizes - Array of size objects with w and h properties
682+
* @returns {Object} The smallest size object
683+
*/
684+
function _getMinSize(sizes) {
685+
return (!sizes || !sizes.length ? { w: 0, h: 0 } : sizes.reduce((min, size) => size.h * size.w < min.h * min.w ? size : min, sizes[0]));
686+
}
687+
688+
/**
689+
* Measures viewability for an element and adds it to the imp object at the ext level
690+
* @param {Object} imp - The impression object
691+
* @param {string} adUnitCode - The ad unit code for element identification
692+
* @param {Object} size - Size object with width and height properties
693+
*/
694+
export const addViewabilityToImp = (imp, adUnitCode, sizes) => {
695+
let elementSize = { w: 0, h: 0 };
696+
697+
if (imp.video?.w > 0 && imp.video?.h > 0) {
698+
elementSize.w = imp.video.w;
699+
elementSize.h = imp.video.h;
700+
} else {
701+
elementSize = _getMinSize(sizes);
702+
}
703+
const element = document.getElementById(adUnitCode);
704+
if (!element) return;
705+
706+
const viewabilityAmount = _isViewabilityMeasurable(element)
707+
? _getViewability(element, getWindowTop(), elementSize)
708+
: 'na';
709+
710+
if (!imp.ext) {
711+
imp.ext = {};
712+
}
713+
714+
// Add viewability data at the imp.ext level
715+
imp.ext.viewability = {
716+
amount: isNaN(viewabilityAmount) ? viewabilityAmount : Math.round(viewabilityAmount)
717+
};
718+
};
719+
643720
export const spec = {
644721
code: BIDDER_CODE,
645722
gvlid: 76,

test/spec/modules/pubmaticBidAdapter_spec.js

+124-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import * as pubmaticBidAdapter from 'modules/pubmaticBidAdapter.js';
12
import { expect } from 'chai';
2-
import { spec, cpmAdjustment } from 'modules/pubmaticBidAdapter.js';
3+
import { spec, cpmAdjustment, _getViewability, addViewabilityToImp, _isViewabilityMeasurable } from 'modules/pubmaticBidAdapter.js';
34
import * as utils from 'src/utils.js';
45
import { bidderSettings } from 'src/bidderSettings.js';
56

@@ -276,7 +277,7 @@ describe('PubMatic adapter', () => {
276277
const { imp } = request?.data;
277278
expect(imp).to.be.an('array');
278279
expect(imp[0]).to.have.property('banner').to.have.property('format');
279-
expect(imp[0]).to.have.property('banner').to.have.property('format').with.lengthOf(2);
280+
expect(imp[0]).to.have.property('banner').to.have.property('format').to.be.an('array');
280281
});
281282

282283
it('should add pmZoneId in ext if pmzoneid is present in parameters', () => {
@@ -1061,3 +1062,124 @@ describe('PubMatic adapter', () => {
10611062
}
10621063
})
10631064
})
1065+
1066+
describe('_getViewability', () => {
1067+
let element;
1068+
let getBoundingClientRectStub;
1069+
let topWinMock;
1070+
1071+
beforeEach(() => {
1072+
element = document.createElement('div');
1073+
getBoundingClientRectStub = sinon.stub(element, 'getBoundingClientRect');
1074+
topWinMock = {
1075+
innerWidth: 1000,
1076+
innerHeight: 800,
1077+
document: { visibilityState: 'visible' }
1078+
};
1079+
});
1080+
1081+
afterEach(() => {
1082+
getBoundingClientRectStub.restore();
1083+
});
1084+
1085+
it('should return 100 when element is fully in viewport', () => {
1086+
getBoundingClientRectStub.returns({ top: 0, left: 0, width: 300, height: 250, right: 300, bottom: 250 });
1087+
const result = _getViewability(element, topWinMock, { w: 300, h: 250 });
1088+
expect(result).to.equal(100);
1089+
});
1090+
1091+
it('should return 0 if document is not visible', () => {
1092+
topWinMock.document.visibilityState = 'hidden';
1093+
getBoundingClientRectStub.returns({ top: 0, left: 0, width: 300, height: 250, right: 300, bottom: 250 });
1094+
const result = _getViewability(element, topWinMock, { w: 300, h: 250 });
1095+
expect(result).to.equal(0);
1096+
});
1097+
1098+
it('should return 0 if element is not in viewport at all', () => {
1099+
getBoundingClientRectStub.returns({ top: 2000, left: 2000, width: 300, height: 250, right: 2300, bottom: 2250 });
1100+
const result = _getViewability(element, topWinMock, { w: 300, h: 250 });
1101+
expect(result).to.equal(0);
1102+
});
1103+
1104+
it('should handle missing size object gracefully', () => {
1105+
getBoundingClientRectStub.returns({ top: 0, left: 0, width: 300, height: 250, right: 300, bottom: 250 });
1106+
const result = _getViewability(element, topWinMock);
1107+
expect(result).to.be.a('number');
1108+
});
1109+
1110+
it('should return the correct percentage if the element is partially in view', () => {
1111+
// Only 100x100 of the 200x200 element is visible in the viewport
1112+
const boundingBox = { left: 700, top: 500, right: 900, bottom: 700, width: 200, height: 200 };
1113+
getBoundingClientRectStub.returns(boundingBox);
1114+
1115+
// Stub getWinDimensions to simulate a viewport that only covers 800x600 (so only a quarter of the element is visible)
1116+
const getWinDimensionsStub = sinon.stub(utils, 'getWinDimensions');
1117+
getWinDimensionsStub.returns({ innerWidth: 800, innerHeight: 600 });
1118+
1119+
// Call with the real window or a mock, as your percentInView implementation expects
1120+
const result = _getViewability(element, window, { w: 200, h: 200 });
1121+
1122+
expect(result).to.equal(25); // 100x100 / 200x200 = 0.25 -> 25%
1123+
getWinDimensionsStub.restore();
1124+
});
1125+
});
1126+
1127+
describe('addViewabilityToImp', () => {
1128+
let imp;
1129+
let element;
1130+
let originalGetElementById;
1131+
let originalVisibilityState;
1132+
let sandbox;
1133+
1134+
beforeEach(() => {
1135+
sandbox = sinon.createSandbox();
1136+
imp = { ext: {} };
1137+
element = document.createElement('div');
1138+
element.id = 'Div1';
1139+
document.body.appendChild(element);
1140+
originalGetElementById = document.getElementById;
1141+
sandbox.stub(document, 'getElementById').callsFake(id => id === 'Div1' ? element : null);
1142+
originalVisibilityState = document.visibilityState;
1143+
Object.defineProperty(document, 'visibilityState', {
1144+
value: 'visible',
1145+
configurable: true
1146+
});
1147+
sandbox.stub(require('src/utils.js'), 'getWindowTop').returns(window);
1148+
});
1149+
1150+
afterEach(() => {
1151+
sandbox.restore();
1152+
document.body.removeChild(element);
1153+
Object.defineProperty(document, 'visibilityState', {
1154+
value: originalVisibilityState,
1155+
configurable: true
1156+
});
1157+
document.getElementById = originalGetElementById;
1158+
});
1159+
1160+
it('should add viewability to imp.ext when measurable', () => {
1161+
addViewabilityToImp(imp, 'Div1', { w: 300, h: 250 });
1162+
expect(imp.ext).to.have.property('viewability');
1163+
});
1164+
1165+
it('should set viewability amount to "na" if not measurable (e.g., in iframe)', () => {
1166+
sandbox.stub(pubmaticBidAdapter, '_isViewabilityMeasurable').returns(false);
1167+
pubmaticBidAdapter.addViewabilityToImp(imp, 'Div1', { w: 300, h: 250 });
1168+
expect(imp.ext).to.have.property('viewability');
1169+
expect(imp.ext.viewability.amount).to.equal('na');
1170+
});
1171+
1172+
it('should not add viewability if element is not found', () => {
1173+
document.getElementById.restore();
1174+
sandbox.stub(document, 'getElementById').returns(null);
1175+
addViewabilityToImp(imp, 'Div1', { w: 300, h: 250 });
1176+
expect(imp.ext).to.not.have.property('viewability');
1177+
});
1178+
1179+
it('should create imp.ext if not present', () => {
1180+
imp = {};
1181+
addViewabilityToImp(imp, 'Div1', { w: 300, h: 250 });
1182+
expect(imp.ext).to.exist;
1183+
expect(imp.ext).to.have.property('viewability');
1184+
});
1185+
});

0 commit comments

Comments
 (0)