• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prebid / Prebid.js / #302

11 Jun 2025 02:18AM UTC coverage: 90.359% (+0.1%) from 90.216%
#302

push

travis-ci

prebidjs-release
Prebid 9.49.1 release

43098 of 54130 branches covered (79.62%)

63790 of 70596 relevant lines covered (90.36%)

175.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.79
/modules/intentIqIdSystem.js
1
/**
1✔
2
 * This module adds IntentIqId to the User ID module
3
 * The {@link module:modules/userId} module is required
4
 * @module modules/intentIqIdSystem
5
 * @requires module:modules/userId
6
 */
7

8
import {logError, isPlainObject, isStr, isNumber, getWinDimensions} from '../src/utils.js';
9
import {ajax} from '../src/ajax.js';
10
import {submodule} from '../src/hook.js'
11
import AES from 'crypto-js/aes.js';
12
import Utf8 from 'crypto-js/enc-utf8.js';
13
import {detectBrowser} from '../libraries/intentIqUtils/detectBrowserUtils.js';
14
import {appendSPData} from '../libraries/intentIqUtils/urlUtils.js';
15
import {appendVrrefAndFui} from '../libraries/intentIqUtils/getRefferer.js';
16
import { getCmpData } from '../libraries/intentIqUtils/getCmpData.js';
17
import {readData, storeData, defineStorageType, removeDataByKey, tryParse} from '../libraries/intentIqUtils/storageUtils.js';
18
import {
19
  FIRST_PARTY_KEY,
20
  WITH_IIQ, WITHOUT_IIQ,
21
  NOT_YET_DEFINED,
22
  CLIENT_HINTS_KEY,
23
  EMPTY,
24
  GVLID,
25
  VERSION, INVALID_ID, SCREEN_PARAMS, SYNC_REFRESH_MILL, META_DATA_CONSTANT, PREBID,
26
  HOURS_24
27
} from '../libraries/intentIqConstants/intentIqConstants.js';
28
import {SYNC_KEY} from '../libraries/intentIqUtils/getSyncKey.js';
29
import {iiqPixelServerAddress, iiqServerAddress} from '../libraries/intentIqUtils/intentIqConfig.js';
30
import { handleAdditionalParams } from '../libraries/intentIqUtils/handleAdditionalParams.js';
31

32
/**
33
 * @typedef {import('../modules/userId/index.js').Submodule} Submodule
34
 * @typedef {import('../modules/userId/index.js').SubmoduleConfig} SubmoduleConfig
35
 * @typedef {import('../modules/userId/index.js').IdResponse} IdResponse
36
 */
37

38
const MODULE_NAME = 'intentIqId';
1✔
39

40
const encoderCH = {
1✔
41
  brands: 0,
42
  mobile: 1,
43
  platform: 2,
44
  architecture: 3,
45
  bitness: 4,
46
  model: 5,
47
  platformVersion: 6,
48
  wow64: 7,
49
  fullVersionList: 8
50
};
51
let sourceMetaData;
52
let sourceMetaDataExternal;
53

54
let FIRST_PARTY_KEY_FINAL = FIRST_PARTY_KEY;
1✔
55
let PARTNER_DATA_KEY;
56
let callCount = 0;
1✔
57
let failCount = 0;
1✔
58
let noDataCount = 0;
1✔
59

60
export let firstPartyData;
61

62
/**
63
 * Generate standard UUID string
64
 * @return {string}
65
 */
66
function generateGUID() {
67
  let d = new Date().getTime();
47✔
68
  const guid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
47✔
69
    const r = (d + Math.random() * 16) % 16 | 0;
1,457✔
70
    d = Math.floor(d / 16);
1,457✔
71
    return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
1,457✔
72
  });
73
  return guid;
47✔
74
}
75

76
/**
77
 * Encrypts plaintext.
78
 * @param {string} plainText The plaintext to encrypt.
79
 * @returns {string} The encrypted text as a base64 string.
80
 */
81
export function encryptData(plainText) {
82
  return AES.encrypt(plainText, MODULE_NAME).toString();
8✔
83
}
84

85
/**
86
 * Decrypts ciphertext.
87
 * @param {string} encryptedText The encrypted text as a base64 string.
88
 * @returns {string} The decrypted plaintext.
89
 */
90
export function decryptData(encryptedText) {
91
  const bytes = AES.decrypt(encryptedText, MODULE_NAME);
3✔
92
  return bytes.toString(Utf8);
3✔
93
}
94

95
function collectDeviceInfo() {
96
  const windowDimensions = getWinDimensions();
15✔
97
  return {
15✔
98
    windowInnerHeight: windowDimensions.innerHeight,
99
    windowInnerWidth: windowDimensions.innerWidth,
100
    devicePixelRatio: windowDimensions.devicePixelRatio,
101
    windowScreenHeight: windowDimensions.screen.height,
102
    windowScreenWidth: windowDimensions.screen.width,
103
    language: navigator.language
104
  }
105
}
106

107
function addUniquenessToUrl(url) {
108
  url += '&tsrnd=' + Math.floor(Math.random() * 1000) + '_' + new Date().getTime();
15✔
109
  return url;
15✔
110
}
111

112
function appendDeviceInfoToUrl(url, deviceInfo) {
113
  const screenParamsString = Object.entries(SCREEN_PARAMS)
15✔
114
    .map(([index, param]) => {
115
      const value = (deviceInfo)[param];
90✔
116
      return `${index}:${value}`;
90✔
117
    })
118
    .join(',');
119

120
  url += `&cz=${encodeURIComponent(screenParamsString)}`;
15✔
121
  url += `&dw=${deviceInfo.windowScreenWidth}&dh=${deviceInfo.windowScreenHeight}&dpr=${deviceInfo.devicePixelRatio}&lan=${deviceInfo.language}`;
15✔
122
  return url;
15✔
123
}
124

125
function appendFirstPartyData (url, firstPartyData, partnerData) {
126
  url += firstPartyData.pid ? '&pid=' + encodeURIComponent(firstPartyData.pid) : '';
57!
127
  url += firstPartyData.pcid ? '&iiqidtype=2&iiqpcid=' + encodeURIComponent(firstPartyData.pcid) : '';
57✔
128
  url += firstPartyData.pcidDate ? '&iiqpciddate=' + encodeURIComponent(firstPartyData.pcidDate) : '';
57✔
129
  return url
57✔
130
}
131

132
function verifyIdType(value) {
133
  if (value === 0 || value === 1 || value === 3 || value === 4) return value;
23!
134
  return -1;
×
135
}
136

137
function appendPartnersFirstParty (url, configParams) {
138
  let partnerClientId = typeof configParams.partnerClientId === 'string' ? encodeURIComponent(configParams.partnerClientId) : '';
57✔
139
  let partnerClientIdType = typeof configParams.partnerClientIdType === 'number' ? verifyIdType(configParams.partnerClientIdType) : -1;
57✔
140

141
  if (partnerClientIdType === -1) return url;
57✔
142
  if (partnerClientId !== '') {
23✔
143
      url = url + '&pcid=' + partnerClientId;
21✔
144
      url = url + '&idtype=' + partnerClientIdType;
21✔
145
  }
146
  return url;
23✔
147
}
148

149
function appendCMPData (url, cmpData) {
150
  url += cmpData.uspString ? '&us_privacy=' + encodeURIComponent(cmpData.uspString) : '';
57✔
151
  url += cmpData.gppString ? '&gpp=' + encodeURIComponent(cmpData.gppString) : '';
57✔
152
  url += cmpData.gdprApplies
57✔
153
    ? '&gdpr_consent=' + encodeURIComponent(cmpData.gdprString) + '&gdpr=1'
154
    : '&gdpr=0';
155
  return url
57✔
156
}
157

158
function appendCounters (url) {
159
  url += '&jaesc=' + encodeURIComponent(callCount);
42✔
160
  url += '&jafc=' + encodeURIComponent(failCount);
42✔
161
  url += '&jaensc=' + encodeURIComponent(noDataCount);
42✔
162
  return url
42✔
163
}
164

165
/**
166
 * Translate and validate sourceMetaData
167
 */
168
export function translateMetadata(data) {
169
  try {
22✔
170
    const d = data.split('.');
22✔
171
    return (
22✔
172
      ((+d[0] * META_DATA_CONSTANT + +d[1]) * META_DATA_CONSTANT + +d[2]) * META_DATA_CONSTANT +
173
      +d[3]
174
    );
175
  } catch (e) {
176
    return NaN;
×
177
  }
178
}
179

180
/**
181
 * Add sourceMetaData to URL if valid
182
 */
183
function addMetaData(url, data) {
184
  if (typeof data !== 'number' || isNaN(data)) {
57✔
185
    return url;
38✔
186
  }
187
  return url + '&fbp=' + data;
19✔
188
}
189

190
export function createPixelUrl(firstPartyData, clientHints, configParams, partnerData, cmpData) {
191
  const deviceInfo = collectDeviceInfo();
15✔
192
  const browser = detectBrowser();
15✔
193

194
  let url = iiqPixelServerAddress(configParams, cmpData.gdprString);
15✔
195
  url += '/profiles_engine/ProfilesEngineServlet?at=20&mi=10&secure=1';
15✔
196
  url += '&dpi=' + configParams.partner;
15✔
197
  url = appendFirstPartyData(url, firstPartyData, partnerData);
15✔
198
  url = appendPartnersFirstParty(url, configParams);
15✔
199
  url = addUniquenessToUrl(url);
15✔
200
  url += partnerData?.clientType ? '&idtype=' + partnerData.clientType : '';
15!
201
  if (deviceInfo) url = appendDeviceInfoToUrl(url, deviceInfo);
15!
202
  url += VERSION ? '&jsver=' + VERSION : '';
15!
203
  if (clientHints) url += '&uh=' + encodeURIComponent(clientHints);
15✔
204
  url = appendVrrefAndFui(url, configParams.domainName);
15✔
205
  url = appendCMPData(url, cmpData);
15✔
206
  url = addMetaData(url, sourceMetaDataExternal || sourceMetaData);
15✔
207
  url = handleAdditionalParams(browser, url, 0, configParams.additionalParams);
15✔
208
  url = appendSPData(url, firstPartyData)
15✔
209
  url += '&source=' + PREBID;
15✔
210
  return url;
15✔
211
}
212

213
function sendSyncRequest(allowedStorage, url, partner, firstPartyData, newUser) {
214
  const lastSyncDate = Number(readData(SYNC_KEY(partner) || '', allowedStorage)) || false;
13!
215
  const lastSyncElapsedTime = Date.now() - lastSyncDate
13✔
216

217
  if (firstPartyData.isOptedOut) {
13!
218
    const needToDoSync = (Date.now() - (firstPartyData?.date || firstPartyData?.sCal || Date.now())) > SYNC_REFRESH_MILL
×
219
    if (newUser || needToDoSync) {
×
220
      ajax(url, () => {
×
221
      }, undefined, {method: 'GET', withCredentials: true});
222
      if (firstPartyData?.date) {
×
223
        firstPartyData.date = Date.now()
×
224
        storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
×
225
      }
226
    }
227
  } else if (!lastSyncDate || lastSyncElapsedTime > SYNC_REFRESH_MILL) {
13!
228
    storeData(SYNC_KEY(partner), Date.now() + '', allowedStorage);
13✔
229
    ajax(url, () => {
13✔
230
    }, undefined, {method: 'GET', withCredentials: true});
231
  }
232
}
233

234
/**
235
 * Configures and updates A/B testing group in Google Ad Manager (GAM).
236
 *
237
 * @param {object} gamObjectReference - Reference to the GAM object, expected to have a `cmd` queue and `pubads()` API.
238
 * @param {string} gamParameterName - The name of the GAM targeting parameter where the group value will be stored.
239
 * @param {string} userGroup - The A/B testing group assigned to the user (e.g., 'A', 'B', or a custom value).
240
 */
241
export function setGamReporting(gamObjectReference, gamParameterName, userGroup) {
242
  if (isPlainObject(gamObjectReference) && gamObjectReference.cmd) {
59✔
243
    gamObjectReference.cmd.push(() => {
25✔
244
      gamObjectReference
3✔
245
        .pubads()
246
        .setTargeting(gamParameterName, userGroup || NOT_YET_DEFINED);
5✔
247
    });
248
  }
249
}
250

251
/**
252
 * Processes raw client hints data into a structured format.
253
 * @param {object} clientHints - Raw client hints data
254
 * @return {string} A JSON string of processed client hints or an empty string if no hints
255
 */
256
export function handleClientHints(clientHints) {
257
  const chParams = {};
57✔
258
  for (const key in clientHints) {
57✔
259
    if (clientHints.hasOwnProperty(key) && clientHints[key] !== '') {
513✔
260
      if (['brands', 'fullVersionList'].includes(key)) {
456✔
261
        let handledParam = '';
114✔
262
        clientHints[key].forEach((element, index) => {
114✔
263
          const isNotLast = index < clientHints[key].length - 1;
274✔
264
          handledParam += `"${element.brand}";v="${element.version}"${isNotLast ? ', ' : ''}`;
274✔
265
        });
266
        chParams[encoderCH[key]] = handledParam;
114✔
267
      } else if (typeof clientHints[key] === 'boolean') {
342✔
268
        chParams[encoderCH[key]] = `?${clientHints[key] ? 1 : 0}`;
114!
269
      } else {
270
        chParams[encoderCH[key]] = `"${clientHints[key]}"`;
228✔
271
      }
272
    }
273
  }
274
  return Object.keys(chParams).length ? JSON.stringify(chParams) : '';
57!
275
}
276

277
export function isCMPStringTheSame(fpData, cmpData) {
278
  const firstPartyDataCPString = `${fpData.gdprString}${fpData.gppString}${fpData.uspString}`;
64✔
279
  const cmpDataString = `${cmpData.gdprString}${cmpData.gppString}${cmpData.uspString}`;
64✔
280
  return firstPartyDataCPString === cmpDataString;
64✔
281
}
282

283
function updateCountersAndStore(runtimeEids, allowedStorage, partnerData) {
284
  if (!runtimeEids?.eids?.length) {
19✔
285
    noDataCount++;
13✔
286
  } else {
287
    callCount++;
6✔
288
  }
289
  storeCounters(allowedStorage, partnerData);
19✔
290
}
291

292
function clearCountersAndStore(allowedStorage, partnerData) {
293
  callCount = 0;
39✔
294
  failCount = 0;
39✔
295
  noDataCount = 0;
39✔
296
  storeCounters(allowedStorage, partnerData);
39✔
297
}
298

299
function storeCounters(storage, partnerData) {
300
  partnerData.callCount = callCount;
58✔
301
  partnerData.failCount = failCount;
58✔
302
  partnerData.noDataCounter = noDataCount;
58✔
303
  storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), storage, firstPartyData);
58✔
304
}
305

306
/** @type {Submodule} */
307
export const intentIqIdSubmodule = {
1✔
308
  /**
309
   * used to link submodule with config
310
   * @type {string}
311
   */
312
  name: MODULE_NAME,
313
  gvlid: GVLID,
314
  /**
315
   * decode the stored id value for passing to bid requests
316
   * @function
317
   * @param {{string}} value
318
   * @returns {{intentIqId: {string}}|undefined}
319
   */
320
  decode(value) {
321
    return value && value != '' && INVALID_ID != value ? {'intentIqId': value} : undefined;
3!
322
  },
323

324
  /**
325
   * performs action to obtain id and return a value in the callback's response argument
326
   * @function
327
   * @param {SubmoduleConfig} [config]
328
   * @returns {IdResponse|undefined}
329
   */
330
  getId(config) {
331
    const configParams = (config?.params) || {};
59!
332

333
    const firePartnerCallback = () => {
59✔
334
      if (configParams.callback && !callbackFired) {
67✔
335
        callbackFired = true;
2✔
336
        if (callbackTimeoutID) clearTimeout(callbackTimeoutID);
2!
337
        if (isGroupB) runtimeEids = { eids: [] };
2!
338
        configParams.callback(runtimeEids);
2✔
339
      }
340
    }
341

342
    if (typeof configParams.partner !== 'number') {
59✔
343
      logError('User ID - intentIqId submodule requires a valid partner to be defined');
3✔
344
      firePartnerCallback()
3✔
345
      return;
3✔
346
    }
347

348
    let decryptedData, callbackTimeoutID;
349
    let callbackFired = false;
56✔
350
    let runtimeEids = { eids: [] };
56✔
351

352
    let gamObjectReference = isPlainObject(configParams.gamObjectReference) ? configParams.gamObjectReference : undefined;
56✔
353
    let gamParameterName = configParams.gamParameterName ? configParams.gamParameterName : 'intent_iq_group';
56✔
354
    let groupChanged = typeof configParams.groupChanged === 'function' ? configParams.groupChanged : undefined;
56✔
355
    let siloEnabled = typeof configParams.siloEnabled === 'boolean' ? configParams.siloEnabled : false;
56✔
356
    sourceMetaData = isStr(configParams.sourceMetaData) ? translateMetadata(configParams.sourceMetaData) : '';
56✔
357
    sourceMetaDataExternal = isNumber(configParams.sourceMetaDataExternal) ? configParams.sourceMetaDataExternal : undefined;
56✔
358
    let additionalParams = configParams.additionalParams ? configParams.additionalParams : undefined;
56✔
359
    PARTNER_DATA_KEY = `${FIRST_PARTY_KEY}_${configParams.partner}`;
56✔
360

361
    const allowedStorage = defineStorageType(config.enabledStorageTypes);
56✔
362

363
    let rrttStrtTime = 0;
56✔
364
    let partnerData = {};
56✔
365
    let shouldCallServer = false;
56✔
366
    FIRST_PARTY_KEY_FINAL = `${FIRST_PARTY_KEY}${siloEnabled ? '_p_' + configParams.partner : ''}`;
56✔
367
    const cmpData = getCmpData();
56✔
368
    const gdprDetected = cmpData.gdprString;
56✔
369
    firstPartyData = tryParse(readData(FIRST_PARTY_KEY_FINAL, allowedStorage));
56✔
370
    const isGroupB = firstPartyData?.group === WITHOUT_IIQ;
56✔
371
    setGamReporting(gamObjectReference, gamParameterName, firstPartyData?.group);
56✔
372

373
    if (groupChanged) groupChanged(firstPartyData?.group || NOT_YET_DEFINED);
56✔
374

375
    callbackTimeoutID = setTimeout(() => {
56✔
376
      firePartnerCallback();
37✔
377
    }, configParams.timeoutInMillis || 500
112✔
378
    );
379

380
    const currentBrowserLowerCase = detectBrowser();
56✔
381
    const browserBlackList = typeof configParams.browserBlackList === 'string' ? configParams.browserBlackList.toLowerCase() : '';
56✔
382
    let newUser = false;
56✔
383

384
    if (!firstPartyData?.pcid) {
56✔
385
      const firstPartyId = generateGUID();
47✔
386
      firstPartyData = {
47✔
387
        pcid: firstPartyId,
388
        pcidDate: Date.now(),
389
        group: NOT_YET_DEFINED,
390
        uspString: EMPTY,
391
        gppString: EMPTY,
392
        gdprString: EMPTY,
393
        date: Date.now()
394
      };
395
      newUser = true;
47✔
396
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
47✔
397
    } else if (!firstPartyData.pcidDate) {
9✔
398
      firstPartyData.pcidDate = Date.now();
7✔
399
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
7✔
400
    }
401

402
    if (gdprDetected && !('isOptedOut' in firstPartyData)) {
56✔
403
      firstPartyData.isOptedOut = true;
4✔
404
    }
405

406
    // Read client hints from storage
407
    let clientHints = readData(CLIENT_HINTS_KEY, allowedStorage);
56✔
408

409
    // Get client hints and save to storage
410
    if (navigator?.userAgentData?.getHighEntropyValues) {
56!
411
      navigator.userAgentData
56✔
412
        .getHighEntropyValues([
413
          'brands',
414
          'mobile',
415
          'bitness',
416
          'wow64',
417
          'architecture',
418
          'model',
419
          'platform',
420
          'platformVersion',
421
          'fullVersionList'
422
        ])
423
        .then(ch => {
424
          clientHints = handleClientHints(ch);
56✔
425
          storeData(CLIENT_HINTS_KEY, clientHints, allowedStorage, firstPartyData)
56✔
426
        });
427
    }
428

429
    const savedData = tryParse(readData(PARTNER_DATA_KEY, allowedStorage))
56✔
430
    if (savedData) {
56✔
431
      partnerData = savedData;
6✔
432

433
      if (typeof partnerData.callCount === 'number') callCount = partnerData.callCount;
6✔
434
      if (typeof partnerData.failCount === 'number') failCount = partnerData.failCount;
6✔
435
      if (typeof partnerData.noDataCounter === 'number') noDataCount = partnerData.noDataCounter;
6✔
436

437
      if (partnerData.wsrvcll) {
6!
438
        partnerData.wsrvcll = false;
×
439
        storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
×
440
      }
441
    }
442

443
    if (partnerData.data) {
56✔
444
      if (partnerData.data.length) { // encrypted data
1!
445
        decryptedData = tryParse(decryptData(partnerData.data));
1✔
446
        runtimeEids = decryptedData;
1✔
447
      }
448
    }
449

450
    if (!isCMPStringTheSame(firstPartyData, cmpData) ||
56!
451
          !firstPartyData.sCal ||
452
          (savedData && (!partnerData.cttl || !partnerData.date || Date.now() - partnerData.date > partnerData.cttl))) {
453
      firstPartyData.uspString = cmpData.uspString;
54✔
454
      firstPartyData.gppString = cmpData.gppString;
54✔
455
      firstPartyData.gdprString = cmpData.gdprString;
54✔
456
      shouldCallServer = true;
54✔
457
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
54✔
458
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
54✔
459
    }
460
    if (!shouldCallServer) {
56✔
461
      if (!savedData && !firstPartyData.isOptedOut) {
2✔
462
        shouldCallServer = true;
1✔
463
      } else shouldCallServer = Date.now() > firstPartyData.sCal + HOURS_24;
1✔
464
    }
465

466
    if (firstPartyData.isOptedOut) {
56✔
467
      partnerData.data = runtimeEids = { eids: [] };
5✔
468
      firePartnerCallback()
5✔
469
    }
470

471
    if (firstPartyData.group === WITHOUT_IIQ || (firstPartyData.group !== WITHOUT_IIQ && runtimeEids?.eids?.length)) {
56✔
472
      firePartnerCallback()
1✔
473
    }
474

475
    // Check if current browser is in blacklist
476
    if (browserBlackList?.includes(currentBrowserLowerCase)) {
56✔
477
      logError('User ID - intentIqId submodule: browser is in blacklist! Data will be not provided.');
13✔
478
      if (configParams.callback) configParams.callback('');
13✔
479
      const url = createPixelUrl(firstPartyData, clientHints, configParams, partnerData, cmpData)
13✔
480
      sendSyncRequest(allowedStorage, url, configParams.partner, firstPartyData, newUser)
13✔
481
      return
13✔
482
    }
483

484
    if (!shouldCallServer) {
43✔
485
      if (isGroupB) runtimeEids = { eids: [] };
1!
486
      firePartnerCallback();
1✔
487
      updateCountersAndStore(runtimeEids, allowedStorage, partnerData);
1✔
488
      return { id: runtimeEids.eids };
1✔
489
    }
490

491
    // use protocol relative urls for http or https
492
    let url = `${iiqServerAddress(configParams, gdprDetected)}/profiles_engine/ProfilesEngineServlet?at=39&mi=10&dpi=${configParams.partner}&pt=17&dpn=1`;
42✔
493
    url += configParams.pai ? '&pai=' + encodeURIComponent(configParams.pai) : '';
42✔
494
    url = appendFirstPartyData(url, firstPartyData, partnerData);
42✔
495
    url = appendPartnersFirstParty(url, configParams);
42✔
496
    url += (partnerData.cttl) ? '&cttl=' + encodeURIComponent(partnerData.cttl) : '';
42✔
497
    url += (partnerData.rrtt) ? '&rrtt=' + encodeURIComponent(partnerData.rrtt) : '';
42✔
498
    url = appendCMPData(url, cmpData);
42✔
499
    url += '&japs=' + encodeURIComponent(configParams.siloEnabled === true);
42✔
500
    url = appendCounters(url);
42✔
501
    url += clientHints ? '&uh=' + encodeURIComponent(clientHints) : '';
42✔
502
    url += VERSION ? '&jsver=' + VERSION : '';
42!
503
    url += firstPartyData?.group ? '&testGroup=' + encodeURIComponent(firstPartyData.group) : '';
42✔
504
    url = addMetaData(url, sourceMetaDataExternal || sourceMetaData);
42✔
505
    url = handleAdditionalParams(currentBrowserLowerCase, url, 1, additionalParams);
42✔
506
    url = appendSPData(url, firstPartyData)
42✔
507
    url += '&source=' + PREBID;
42✔
508

509
    // Add vrref and fui to the URL
510
    url = appendVrrefAndFui(url, configParams.domainName);
42✔
511

512
    const storeFirstPartyData = () => {
42✔
513
      partnerData.eidl = runtimeEids?.eids?.length || -1
18✔
514
      storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
18✔
515
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
18✔
516
    }
517

518
    const resp = function (callback) {
42✔
519
      const callbacks = {
39✔
520
        success: response => {
521
          let respJson = tryParse(response);
20✔
522
          // If response is a valid json and should save is true
523
          if (respJson) {
20✔
524
            partnerData.date = Date.now();
19✔
525
            firstPartyData.sCal = Date.now();
19✔
526
            const defineEmptyDataAndFireCallback = () => {
19✔
527
              respJson.data = partnerData.data = runtimeEids = { eids: [] };
2✔
528
              storeFirstPartyData()
2✔
529
              firePartnerCallback()
2✔
530
              callback(runtimeEids)
2✔
531
            }
532
            if (callbackTimeoutID) clearTimeout(callbackTimeoutID)
19!
533
            if ('cttl' in respJson) {
19!
534
              partnerData.cttl = respJson.cttl;
×
535
            } else partnerData.cttl = HOURS_24;
19✔
536

537
            if ('tc' in respJson) {
19✔
538
              partnerData.terminationCause = respJson.tc;
4✔
539
              if (respJson.tc == 41) {
4✔
540
                firstPartyData.group = WITHOUT_IIQ;
1✔
541
                storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
1✔
542
                if (groupChanged) groupChanged(firstPartyData.group);
1!
543
                defineEmptyDataAndFireCallback();
1✔
544
                if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, firstPartyData.group);
1!
545
                return
1✔
546
              } else {
547
                firstPartyData.group = WITH_IIQ;
3✔
548
                if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, firstPartyData.group);
3✔
549
                if (groupChanged) groupChanged(firstPartyData.group);
3✔
550
              }
551
            }
552
            if ('isOptedOut' in respJson) {
18✔
553
              if (respJson.isOptedOut !== firstPartyData.isOptedOut) {
4✔
554
                firstPartyData.isOptedOut = respJson.isOptedOut;
3✔
555
              }
556
              if (respJson.isOptedOut === true) {
4✔
557
                respJson.data = partnerData.data = runtimeEids = { eids: [] };
1✔
558

559
                const keysToRemove = [
1✔
560
                  PARTNER_DATA_KEY,
561
                  CLIENT_HINTS_KEY
562
                ];
563

564
                keysToRemove.forEach(key => removeDataByKey(key, allowedStorage));
2✔
565

566
                storeData(FIRST_PARTY_KEY_FINAL, JSON.stringify(firstPartyData), allowedStorage, firstPartyData);
1✔
567
                firePartnerCallback();
1✔
568
                callback(runtimeEids);
1✔
569
                return
1✔
570
              }
571
            }
572
            if ('pid' in respJson) {
17✔
573
              firstPartyData.pid = respJson.pid;
5✔
574
            }
575
            if ('dbsaved' in respJson) {
17✔
576
              firstPartyData.dbsaved = respJson.dbsaved;
1✔
577
            }
578
            if ('ls' in respJson) {
17✔
579
              if (respJson.ls === false) {
8✔
580
                defineEmptyDataAndFireCallback()
1✔
581
                return
1✔
582
              }
583
              // If data is empty, means we should save as INVALID_ID
584
              if (respJson.data == '') {
7!
585
                respJson.data = INVALID_ID;
×
586
              } else {
587
                // If data is a single string, assume it is an id with source intentiq.com
588
                if (respJson.data && typeof respJson.data === 'string') {
7✔
589
                  respJson.data = {eids: [respJson.data]}
5✔
590
                }
591
              }
592
              partnerData.data = respJson.data;
7✔
593
            }
594

595
            if ('ct' in respJson) {
16✔
596
              partnerData.clientType = respJson.ct;
1✔
597
            }
598

599
            if ('sid' in respJson) {
16!
600
              partnerData.siteId = respJson.sid;
×
601
            }
602

603
            if ('spd' in respJson) {
16✔
604
              // server provided data
605
              firstPartyData.spd = respJson.spd;
1✔
606
            }
607

608
            if (rrttStrtTime && rrttStrtTime > 0) {
16!
609
              partnerData.rrtt = Date.now() - rrttStrtTime;
16✔
610
            }
611

612
            if (respJson.data?.eids) {
16✔
613
              runtimeEids = respJson.data
8✔
614
              callback(respJson.data.eids);
8✔
615
              firePartnerCallback()
8✔
616
              const encryptedData = encryptData(JSON.stringify(respJson.data))
8✔
617
              partnerData.data = encryptedData;
8✔
618
            } else {
619
              callback(runtimeEids);
8✔
620
              firePartnerCallback()
8✔
621
            }
622
            updateCountersAndStore(runtimeEids, allowedStorage, partnerData);
16✔
623
            storeFirstPartyData();
16✔
624
          } else {
625
            callback(runtimeEids);
1✔
626
            firePartnerCallback()
1✔
627
          }
628
        },
629
        error: error => {
630
          logError(MODULE_NAME + ': ID fetch encountered an error', error);
2✔
631
          failCount++;
2✔
632
          updateCountersAndStore(runtimeEids, allowedStorage, partnerData);
2✔
633
          callback(runtimeEids);
2✔
634
        }
635
      };
636
      rrttStrtTime = Date.now();
39✔
637

638
      partnerData.wsrvcll = true;
39✔
639
      storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), allowedStorage, firstPartyData);
39✔
640
      clearCountersAndStore(allowedStorage, partnerData);
39✔
641

642
      ajax(url, callbacks, undefined, {method: 'GET', withCredentials: true});
39✔
643
    };
644
    const respObj = {callback: resp};
42✔
645

646
    if (runtimeEids?.eids?.length) respObj.id = runtimeEids.eids;
42✔
647
    return respObj
42✔
648
  },
649
  eids: {
650
    'intentIqId': {
651
      source: 'intentiq.com',
652
      atype: 1,
653
      getSource: function (data) {
654
        return data.source;
×
655
      },
656
      getValue: function (data) {
657
        if (data?.uids?.length) {
×
658
          return data.uids[0].id
×
659
        }
660
        return null
×
661
      },
662
      getUidExt: function (data) {
663
        if (data?.uids?.length) {
×
664
          return data.uids[0].ext;
×
665
        }
666
        return null
×
667
      }
668
    },
669
  }
670
};
671

672
submodule('userId', intentIqIdSubmodule);
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc