Skip to content
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
20 changes: 19 additions & 1 deletion libraries/intentIqConstants/intentIqConstants.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const FIRST_PARTY_KEY = '_iiq_fdata';

export const SUPPORTED_TYPES = ['html5', 'cookie']

export const WITH_IIQ = 'A';
Expand All @@ -8,4 +9,21 @@ export const BLACK_LIST = 'L';
export const CLIENT_HINTS_KEY = '_iiq_ch';
export const EMPTY = 'EMPTY';
export const GVLID = '1323';
export const VERSION = 0.27;
export const VERSION = 0.28;

export const VR_ENDPOINT = 'https://api.intentiq.com';
export const GDPR_ENDPOINT = 'https://api-gdpr.intentiq.com';
export const INVALID_ID = 'INVALID_ID';

export const SYNC_ENDPOINT = 'https://sync.intentiq.com'
export const GDPR_SYNC_ENDPOINT = 'https://sync-gdpr.intentiq.com'
export const SCREEN_PARAMS = {
0: 'windowInnerHeight',
1: 'windowInnerWidth',
2: 'devicePixelRatio',
3: 'windowScreenHeight',
4: 'windowScreenWidth',
5: 'language'
};

export const SYNC_REFRESH_MILL = 3600000;
15 changes: 10 additions & 5 deletions libraries/intentIqUtils/getRefferer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ import { getWindowTop, logError, getWindowLocation, getWindowSelf } from '../../
*/
export function getReferrer() {
try {
if (getWindowSelf() === getWindowTop()) {
return encodeURIComponent(getWindowLocation().href);
} else {
return encodeURIComponent(getWindowTop().location.href);
const url = getWindowSelf() === getWindowTop()
? getWindowLocation().href
: getWindowTop().location.href;

if (url.length >= 50) {
const { origin } = new URL(url);
return origin;
}

return url;
} catch (error) {
logError(`Error accessing location: ${error}`);
return '';
Expand All @@ -26,7 +31,7 @@ export function getReferrer() {
* @return {string} The modified URL with appended `vrref` or `fui` parameters.
*/
export function appendVrrefAndFui(url, domainName) {
const fullUrl = getReferrer();
const fullUrl = encodeURIComponent(getReferrer());
if (fullUrl) {
return (url += '&vrref=' + getRelevantRefferer(domainName, fullUrl));
}
Expand Down
1 change: 1 addition & 0 deletions libraries/intentIqUtils/getSyncKey.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const SYNC_KEY = (partner) => '_iiq_sync' + '_' + partner
67 changes: 46 additions & 21 deletions modules/intentIqAnalyticsAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ const PARAMS_NAMES = {
prebidVersion: 'pbjsver',
partnerId: 'partnerId',
firstPartyId: 'pcid',
placementId: 'placementId'
placementId: 'placementId',
adType: 'adType'
};

let iiqAnalyticsAnalyticsAdapter = Object.assign(adapter({defaultUrl: REPORT_ENDPOINT, analyticsType}), {
Expand Down Expand Up @@ -129,7 +130,7 @@ function initReadLsIds() {
iiqAnalyticsAnalyticsAdapter.initOptions.terminationCause = pData.terminationCause
iiqAnalyticsAnalyticsAdapter.initOptions.dataInLs = pData.data;
iiqAnalyticsAnalyticsAdapter.initOptions.eidl = pData.eidl || -1;
iiqAnalyticsAnalyticsAdapter.initOptions.ct = pData.ct || null;
iiqAnalyticsAnalyticsAdapter.initOptions.clientType = pData.clientType || null;
iiqAnalyticsAnalyticsAdapter.initOptions.siteId = pData.siteId || null;
iiqAnalyticsAnalyticsAdapter.initOptions.wsrvcll = pData.wsrvcll || false;
iiqAnalyticsAnalyticsAdapter.initOptions.rrtt = pData.rrtt || null;
Expand Down Expand Up @@ -188,7 +189,7 @@ export function preparePayload(data) {
result[PARAMS_NAMES.referrer] = getReferrer();
result[PARAMS_NAMES.terminationCause] = iiqAnalyticsAnalyticsAdapter.initOptions.terminationCause;
result[PARAMS_NAMES.abTestGroup] = iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup;
result[PARAMS_NAMES.clientType] = iiqAnalyticsAnalyticsAdapter.initOptions.ct;
result[PARAMS_NAMES.clientType] = iiqAnalyticsAnalyticsAdapter.initOptions.clientType;
result[PARAMS_NAMES.siteId] = iiqAnalyticsAnalyticsAdapter.initOptions.siteId;
result[PARAMS_NAMES.wasServerCalled] = iiqAnalyticsAnalyticsAdapter.initOptions.wsrvcll;
result[PARAMS_NAMES.requestRtt] = iiqAnalyticsAnalyticsAdapter.initOptions.rrtt;
Expand All @@ -214,6 +215,8 @@ function fillEidsData(result) {
}

function prepareData (data, result) {
const adTypeValue = data.adType || data.mediaType;

if (data.bidderCode) {
result.bidderCode = data.bidderCode;
}
Expand All @@ -235,30 +238,52 @@ function prepareData (data, result) {
if (data.auctionId) {
result.prebidAuctionId = data.auctionId;
}
if (data.placementId) {
result.placementId = data.placementId;
} else {
// Simplified placementId determination
let placeIdFound = false;
if (data.params && Array.isArray(data.params)) {
for (let i = 0; i < data.params.length; i++) {
const param = data.params[i];
if (param.placementId) {
result.placementId = param.placementId;
placeIdFound = true;
break;
}
}
}
if (!placeIdFound && data.adUnitCode) {
result.placementId = data.adUnitCode;
}
if (adTypeValue) {
result[PARAMS_NAMES.adType] = adTypeValue;
}
const iiqConfig = getIntentIqConfig();
const adUnitConfig = iiqConfig.params?.adUnitConfig;

switch (adUnitConfig) {
case 1:
// adUnitCode or placementId
result.placementId = data.adUnitCode || extractPlacementId(data) || '';
break;
case 2:
// placementId or adUnitCode
result.placementId = extractPlacementId(data) || data.adUnitCode || '';
break;
case 3:
// Only adUnitCode
result.placementId = data.adUnitCode || '';
break;
case 4:
// Only placementId
result.placementId = extractPlacementId(data) || '';
break;
default:
// Default (like in case #1)
result.placementId = data.adUnitCode || extractPlacementId(data) || '';
}

result.biddingPlatformId = 1;
result.partnerAuctionId = 'BW';
}

function extractPlacementId(data) {
if (data.placementId) {
return data.placementId;
}
if (data.params && Array.isArray(data.params)) {
for (let i = 0; i < data.params.length; i++) {
if (data.params[i].placementId) {
return data.params[i].placementId;
}
}
}
return null;
}

function getDefaultDataObject() {
return {
'inbbl': false,
Expand Down
4 changes: 3 additions & 1 deletion modules/intentIqAnalyticsAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ currency: 'USD', // Currency for the CPM value.
originalCpm: 1.5, // Original CPM value.
originalCurrency: 'USD', // Original currency.
status: 'rendered', // Auction status, e.g., 'rendered'.
placementId: 'div-1' // ID of the ad placement.
placementId: 'div-1', // ID of the ad placement.
adType: 'banner' // Specifies the type of ad served
}
```

Expand All @@ -76,6 +77,7 @@ placementId: 'div-1' // ID of the ad placement.
| originalCurrency | String | Currency of the original auction | USD | No |
| status | String | Status of the impression. Leave empty or undefined if Prebid is not the bidding platform | rendered | No |
| placementId | String | Unique identifier of the ad unit on the webpage that showed this ad | div-1 | No |
| adType | String | Specifies the type of ad served. Possible values: “banner“, “video“, “native“, “audio“. | banner | No |


To report the auction win, call the function as follows:
Expand Down
124 changes: 102 additions & 22 deletions modules/intentIqIdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import {
CLIENT_HINTS_KEY,
EMPTY,
GVLID,
VERSION,
VERSION, INVALID_ID, GDPR_ENDPOINT, VR_ENDPOINT, SYNC_ENDPOINT, SCREEN_PARAMS, GDPR_SYNC_ENDPOINT, SYNC_REFRESH_MILL
} from '../libraries/intentIqConstants/intentIqConstants.js';
import {SYNC_KEY} from '../libraries/intentIqUtils/getSyncKey.js';

/**
* @typedef {import('../modules/userId/index.js').Submodule} Submodule
Expand All @@ -44,9 +45,7 @@ const encoderCH = {
wow64: 7,
fullVersionList: 8
};
const INVALID_ID = 'INVALID_ID';
const ENDPOINT = 'https://api.intentiq.com';
const GDPR_ENDPOINT = 'https://api-gdpr.intentiq.com';

export let firstPartyData;

/**
Expand Down Expand Up @@ -82,6 +81,89 @@ export function decryptData(encryptedText) {
return bytes.toString(Utf8);
}

function collectDeviceInfo() {
return {
windowInnerHeight: window.innerHeight,
windowInnerWidth: window.innerWidth,
devicePixelRatio: window.devicePixelRatio,
windowScreenHeight: window.screen.height,
windowScreenWidth: window.screen.width,
language: navigator.language
}
}

function addUniquenessToUrl(url) {
url += '&tsrnd=' + Math.floor(Math.random() * 1000) + '_' + new Date().getTime();
return url;
}

function appendDeviceInfoToUrl(url, deviceInfo) {
const screenParamsString = Object.entries(SCREEN_PARAMS)
.map(([index, param]) => {
const value = (deviceInfo)[param];
return `${index}:${value}`;
})
.join(',');

url += `&cz=${encodeURIComponent(screenParamsString)}`;
url += `&dw=${deviceInfo.windowScreenWidth}&dh=${deviceInfo.windowScreenHeight}&dpr=${deviceInfo.devicePixelRatio}&lan=${deviceInfo.language}`;
return url;
}

function appendFirstPartyData (url, firstPartyData, partnerData) {
url += firstPartyData.pid ? '&pid=' + encodeURIComponent(firstPartyData.pid) : '';
url += firstPartyData.pcid ? '&iiqidtype=2&iiqpcid=' + encodeURIComponent(firstPartyData.pcid) : '';
url += firstPartyData.pcidDate ? '&iiqpciddate=' + encodeURIComponent(firstPartyData.pcidDate) : '';
return url
}

function appendCMPData (url, cmpData) {
url += cmpData.uspString ? '&us_privacy=' + encodeURIComponent(cmpData.uspString) : '';
url += cmpData.gppString ? '&gpp=' + encodeURIComponent(cmpData.gppString) : '';
url += cmpData.gdprApplies
? '&gdpr_consent=' + encodeURIComponent(cmpData.gdprString) + '&gdpr=1'
: '&gdpr=0';
return url
}

export function createPixelUrl(firstPartyData, clientHints, configParams, partnerData, cmpData) {
const deviceInfo = collectDeviceInfo()

let url = cmpData.gdprString ? GDPR_SYNC_ENDPOINT : SYNC_ENDPOINT;
url += '/profiles_engine/ProfilesEngineServlet?at=20&mi=10&secure=1'
url += '&dpi=' + configParams.partner;
url = appendFirstPartyData(url, firstPartyData, partnerData);
url = addUniquenessToUrl(url);
url += partnerData?.clientType ? '&idtype=' + partnerData.clientType : '';
if (deviceInfo) url = appendDeviceInfoToUrl(url, deviceInfo)
url += VERSION ? '&jsver=' + VERSION : '';
if (clientHints) url += '&uh=' + encodeURIComponent(clientHints);
url = appendVrrefAndFui(url, configParams.domainName);
url = appendCMPData(url, cmpData)
return url;
}

function sendSyncRequest(allowedStorage, url, partner, firstPartyData, newUser) {
const lastSyncDate = Number(readData(SYNC_KEY(partner) || '', allowedStorage)) || false;
const lastSyncElapsedTime = Date.now() - lastSyncDate

if (firstPartyData.isOptedOut) {
const needToDoSync = (Date.now() - (firstPartyData?.date || firstPartyData?.sCal || Date.now())) > SYNC_REFRESH_MILL
if (newUser || needToDoSync) {
ajax(url, () => {
}, undefined, {method: 'GET', withCredentials: true});
if (firstPartyData?.date) {
firstPartyData.date = Date.now()
storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
}
}
} else if (!lastSyncDate || lastSyncElapsedTime > SYNC_REFRESH_MILL) {
storeData(SYNC_KEY(partner), Date.now() + '', allowedStorage);
ajax(url, () => {
}, undefined, {method: 'GET', withCredentials: true});
}
}

/**
* Parse json if possible, else return null
* @param data
Expand Down Expand Up @@ -161,6 +243,7 @@ export const intentIqIdSubmodule = {
decode(value) {
return value && value != '' && INVALID_ID != value ? {'intentIqId': value} : undefined;
},

/**
* performs action to obtain id and return a value in the callback's response argument
* @function
Expand Down Expand Up @@ -210,13 +293,7 @@ export const intentIqIdSubmodule = {

const currentBrowserLowerCase = detectBrowser();
const browserBlackList = typeof configParams.browserBlackList === 'string' ? configParams.browserBlackList.toLowerCase() : '';

// Check if current browser is in blacklist
if (browserBlackList?.includes(currentBrowserLowerCase)) {
logError('User ID - intentIqId submodule: browser is in blacklist!');
if (configParams.callback) configParams.callback('', BLACK_LIST);
return;
}
let newUser = false;

if (!firstPartyData?.pcid) {
const firstPartyId = generateGUID();
Expand All @@ -230,6 +307,7 @@ export const intentIqIdSubmodule = {
gdprString: EMPTY,
date: Date.now()
};
newUser = true;
storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
} else if (!firstPartyData.pcidDate) {
firstPartyData.pcidDate = Date.now();
Expand Down Expand Up @@ -284,7 +362,6 @@ export const intentIqIdSubmodule = {
firstPartyData.uspString = cmpData.uspString;
firstPartyData.gppString = cmpData.gppString;
firstPartyData.gdprString = cmpData.gdprString;
firstPartyData.date = Date.now();
shouldCallServer = true;
storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
storeData(FIRST_PARTY_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
Expand All @@ -297,26 +374,29 @@ export const intentIqIdSubmodule = {
firePartnerCallback()
}

// Check if current browser is in blacklist
if (browserBlackList?.includes(currentBrowserLowerCase)) {
logError('User ID - intentIqId submodule: browser is in blacklist! Data will be not provided.');
if (configParams.callback) configParams.callback('', BLACK_LIST);
const url = createPixelUrl(firstPartyData, clientHints, configParams, partnerData, cmpData)
sendSyncRequest(allowedStorage, url, configParams.partner, firstPartyData, newUser)
return
}

if (!shouldCallServer) {
if (isGroupB) runtimeEids = { eids: [] };
firePartnerCallback();
return { id: runtimeEids.eids };
}

// use protocol relative urls for http or https
let url = `${gdprDetected ? GDPR_ENDPOINT : ENDPOINT}/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`;
let url = `${gdprDetected ? GDPR_ENDPOINT : VR_ENDPOINT}/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`;
url += configParams.pcid ? '&pcid=' + encodeURIComponent(configParams.pcid) : '';
url += configParams.pai ? '&pai=' + encodeURIComponent(configParams.pai) : '';
url += firstPartyData.pcid ? '&iiqidtype=2&iiqpcid=' + encodeURIComponent(firstPartyData.pcid) : '';
url += firstPartyData.pid ? '&pid=' + encodeURIComponent(firstPartyData.pid) : '';
url = appendFirstPartyData(url, firstPartyData, partnerData);
url += (partnerData.cttl) ? '&cttl=' + encodeURIComponent(partnerData.cttl) : '';
url += (partnerData.rrtt) ? '&rrtt=' + encodeURIComponent(partnerData.rrtt) : '';
url += firstPartyData.pcidDate ? '&iiqpciddate=' + encodeURIComponent(firstPartyData.pcidDate) : '';
url += cmpData.uspString ? '&us_privacy=' + encodeURIComponent(cmpData.uspString) : '';
url += cmpData.gppString ? '&gpp=' + encodeURIComponent(cmpData.gppString) : '';
url += cmpData.gdprApplies
? '&gdpr_consent=' + encodeURIComponent(cmpData.gdprString) + '&gdpr=1'
: '&gdpr=0';
url = appendCMPData(url, cmpData)
url += clientHints ? '&uh=' + encodeURIComponent(clientHints) : '';
url += VERSION ? '&jsver=' + VERSION : '';
url += firstPartyData?.group ? '&testGroup=' + encodeURIComponent(firstPartyData.group) : '';
Expand Down Expand Up @@ -403,7 +483,7 @@ export const intentIqIdSubmodule = {
}

if ('ct' in respJson) {
partnerData.ct = respJson.ct;
partnerData.clientType = respJson.ct;
}

if ('sid' in respJson) {
Expand Down
Loading