prebid.js 6.8.0 → 6.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.eslintrc.js +8 -1
  2. package/integrationExamples/gpt/weboramaRtdProvider_example.html +154 -115
  3. package/integrationExamples/gpt/x-domain/creative.html +13 -6
  4. package/modules/appnexusBidAdapter.js +3 -2
  5. package/modules/asealBidAdapter.js +58 -0
  6. package/modules/asealBidAdapter.md +52 -0
  7. package/modules/conversantBidAdapter.js +7 -0
  8. package/modules/improvedigitalBidAdapter.js +5 -0
  9. package/modules/lunamediahbBidAdapter.js +32 -4
  10. package/modules/oguryBidAdapter.js +6 -13
  11. package/modules/priceFloors.js +2 -1
  12. package/modules/richaudienceBidAdapter.js +7 -2
  13. package/modules/riseBidAdapter.js +17 -6
  14. package/modules/rubiconAnalyticsAdapter.js +5 -0
  15. package/modules/sortableAnalyticsAdapter.js +5 -4
  16. package/modules/weboramaRtdProvider.js +264 -34
  17. package/modules/weboramaRtdProvider.md +110 -40
  18. package/package.json +2 -1
  19. package/src/adloader.js +2 -1
  20. package/src/auction.js +59 -64
  21. package/src/bidderSettings.js +69 -0
  22. package/src/secureCreatives.js +26 -12
  23. package/src/targeting.js +3 -2
  24. package/src/utils.js +0 -7
  25. package/test/spec/auctionmanager_spec.js +33 -1
  26. package/test/spec/modules/asealBidAdapter_spec.js +144 -0
  27. package/test/spec/modules/conversantBidAdapter_spec.js +54 -2
  28. package/test/spec/modules/improvedigitalBidAdapter_spec.js +19 -0
  29. package/test/spec/modules/lunamediahbBidAdapter_spec.js +27 -1
  30. package/test/spec/modules/oguryBidAdapter_spec.js +62 -4
  31. package/test/spec/modules/riseBidAdapter_spec.js +30 -4
  32. package/test/spec/modules/rubiconAnalyticsAdapter_spec.js +31 -1
  33. package/test/spec/modules/sortableAnalyticsAdapter_spec.js +2 -3
  34. package/test/spec/modules/weboramaRtdProvider_spec.js +536 -20
  35. package/test/spec/unit/core/bidderSettings_spec.js +123 -0
  36. package/test/spec/unit/pbjs_api_spec.js +1 -6
  37. package/test/spec/unit/secureCreatives_spec.js +66 -32
@@ -8,6 +8,7 @@ import { getHook } from '../src/hook.js';
8
8
  import { createBid } from '../src/bidfactory.js';
9
9
  import find from 'core-js-pure/features/array/find.js';
10
10
  import { getRefererInfo } from '../src/refererDetection.js';
11
+ import {bidderSettings} from '../src/bidderSettings.js';
11
12
 
12
13
  /**
13
14
  * @summary This Module is intended to provide users with the ability to dynamically set and enforce price floors on a per auction basis.
@@ -147,7 +148,7 @@ function generatePossibleEnumerations(arrayOfFields, delimiter) {
147
148
  * @summary If a the input bidder has a registered cpmadjustment it returns the input CPM after being adjusted
148
149
  */
149
150
  export function getBiddersCpmAdjustment(bidderName, inputCpm, bid = {}) {
150
- const adjustmentFunction = deepAccess(getGlobal(), `bidderSettings.${bidderName}.bidCpmAdjustment`) || deepAccess(getGlobal(), 'bidderSettings.standard.bidCpmAdjustment');
151
+ const adjustmentFunction = bidderSettings.get(bidderName, 'bidCpmAdjustment');
151
152
  if (adjustmentFunction) {
152
153
  return parseFloat(adjustmentFunction(inputCpm, {...bid, cpm: inputCpm}));
153
154
  }
@@ -59,10 +59,15 @@ export const spec = {
59
59
  REFERER = (typeof bidderRequest.refererInfo.referer != 'undefined' ? encodeURIComponent(bidderRequest.refererInfo.referer) : null)
60
60
 
61
61
  payload.gdpr_consent = '';
62
- payload.gdpr = bidderRequest.gdprConsent.gdprApplies;
62
+ payload.gdpr = false;
63
63
 
64
64
  if (bidderRequest && bidderRequest.gdprConsent) {
65
- payload.gdpr_consent = bidderRequest.gdprConsent.consentString;
65
+ if (typeof bidderRequest.gdprConsent.gdprApplies != 'undefined') {
66
+ payload.gdpr = bidderRequest.gdprConsent.gdprApplies;
67
+ }
68
+ if (typeof bidderRequest.gdprConsent.consentString != 'undefined') {
69
+ payload.gdpr_consent = bidderRequest.gdprConsent.consentString;
70
+ }
66
71
  }
67
72
 
68
73
  var payloadString = JSON.stringify(payload);
@@ -1,4 +1,4 @@
1
- import { logWarn, isArray, isFn, deepAccess, isEmpty, contains, timestamp, getBidIdParameter } from '../src/utils.js';
1
+ import { logWarn, logInfo, isArray, isFn, deepAccess, isEmpty, contains, timestamp, getBidIdParameter, triggerPixel } from '../src/utils.js';
2
2
  import {registerBidder} from '../src/adapters/bidderFactory.js';
3
3
  import {VIDEO} from '../src/mediaTypes.js';
4
4
  import {config} from '../src/config.js';
@@ -23,7 +23,7 @@ export const spec = {
23
23
  gvlid: 1043,
24
24
  version: ADAPTER_VERSION,
25
25
  supportedMediaTypes: SUPPORTED_AD_TYPES,
26
- isBidRequestValid: function(bidRequest) {
26
+ isBidRequestValid: function (bidRequest) {
27
27
  if (!bidRequest.params) {
28
28
  logWarn('no params have been set to Rise adapter');
29
29
  return false;
@@ -49,7 +49,7 @@ export const spec = {
49
49
 
50
50
  return requests;
51
51
  },
52
- interpretResponse: function({body}) {
52
+ interpretResponse: function ({body}) {
53
53
  const bidResponses = [];
54
54
 
55
55
  const bidResponse = {
@@ -62,6 +62,7 @@ export const spec = {
62
62
  netRevenue: body.netRevenue,
63
63
  ttl: body.ttl || TTL,
64
64
  vastXml: body.vastXml,
65
+ nurl: body.nurl,
65
66
  mediaType: VIDEO
66
67
  };
67
68
 
@@ -73,7 +74,7 @@ export const spec = {
73
74
 
74
75
  return bidResponses;
75
76
  },
76
- getUserSyncs: function(syncOptions, serverResponses) {
77
+ getUserSyncs: function (syncOptions, serverResponses) {
77
78
  const syncs = [];
78
79
  for (const response of serverResponses) {
79
80
  if (syncOptions.iframeEnabled && response.body.userSyncURL) {
@@ -93,8 +94,18 @@ export const spec = {
93
94
  }
94
95
  }
95
96
  return syncs;
96
- }
97
- };
97
+ },
98
+ onBidWon: function (bid) {
99
+ if (bid == null) {
100
+ return;
101
+ }
102
+
103
+ logInfo('onBidWon:', bid);
104
+ if (bid.hasOwnProperty('nurl') && bid.nurl.length > 0) {
105
+ triggerPixel(bid.nurl);
106
+ }
107
+ },
108
+ }
98
109
 
99
110
  registerBidder(spec);
100
111
 
@@ -234,6 +234,9 @@ function sendMessage(auctionId, bidWonId, trigger) {
234
234
 
235
235
  let auction = {
236
236
  clientTimeoutMillis: auctionCache.timeout,
237
+ auctionStart: auctionCache.timestamp,
238
+ auctionEnd: auctionCache.endTs,
239
+ bidderOrder: auctionCache.bidderOrder,
237
240
  samplingFactor,
238
241
  accountId,
239
242
  adUnits: Object.keys(adUnitMap).map(i => adUnitMap[i]),
@@ -583,6 +586,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, {
583
586
  cacheEntry.bids = {};
584
587
  cacheEntry.bidsWon = {};
585
588
  cacheEntry.gamHasRendered = {};
589
+ cacheEntry.bidderOrder = [];
586
590
  cacheEntry.referrer = deepAccess(args, 'bidderRequests.0.refererInfo.referer');
587
591
  const floorData = deepAccess(args, 'bidderRequests.0.bids.0.floorData');
588
592
  if (floorData) {
@@ -608,6 +612,7 @@ let rubiconAdapter = Object.assign({}, baseAdapter, {
608
612
  }
609
613
  break;
610
614
  case BID_REQUESTED:
615
+ cache.auctions[args.auctionId].bidderOrder.push(args.bidderCode);
611
616
  Object.assign(cache.auctions[args.auctionId].bids, args.bids.reduce((memo, bid) => {
612
617
  // mark adUnits we expect bidWon events for
613
618
  cache.auctions[args.auctionId].bidsWon[bid.adUnitCode] = false;
@@ -5,6 +5,7 @@ import adapterManager from '../src/adapterManager.js';
5
5
  import {ajax} from '../src/ajax.js';
6
6
  import {getGlobal} from '../src/prebidGlobal.js';
7
7
  import { config } from '../src/config.js';
8
+ import {bidderSettings} from '../src/bidderSettings.js';
8
9
 
9
10
  const DEFAULT_PROTOCOL = 'https';
10
11
  const DEFAULT_HOST = 'pa.deployads.com';
@@ -182,11 +183,11 @@ function getFactor(bidder) {
182
183
  }
183
184
 
184
185
  function getBiddersFactors() {
185
- const pb = getGlobal();
186
186
  const result = {};
187
- if (pb && pb.bidderSettings) {
188
- Object.keys(pb.bidderSettings).forEach(bidderKey => {
189
- const bidder = pb.bidderSettings[bidderKey];
187
+ const settings = bidderSettings.getSettings();
188
+ if (settings) {
189
+ Object.keys(settings).forEach(bidderKey => {
190
+ const bidder = settings[bidderKey];
190
191
  const factor = getFactor(bidder);
191
192
  if (factor !== null) {
192
193
  result[bidderKey] = factor;
@@ -7,27 +7,42 @@
7
7
  * @requires module:modules/realTimeData
8
8
  */
9
9
 
10
+ /** onData callback type
11
+ * @callback dataCallback
12
+ * @param {Object} data profile data
13
+ * @param {Boolean} site true if site, else it is user
14
+ * @returns {void}
15
+ */
16
+
10
17
  /**
11
18
  * @typedef {Object} ModuleParams
12
- * @property {WeboCtxConf} weboCtxConf
13
- * @property {WeboUserDataConf} weboUserDataConf
19
+ * @property {?Boolean} setPrebidTargeting if true, will set the GAM targeting (default undefined)
20
+ * @property {?Boolean} sendToBidders if true, will send the contextual profile to all bidders (default undefined)
21
+ * @property {?dataCallback} onData callback
22
+ * @property {?WeboCtxConf} weboCtxConf
23
+ * @property {?WeboUserDataConf} weboUserDataConf
14
24
  */
15
25
 
16
26
  /**
17
27
  * @typedef {Object} WeboCtxConf
18
28
  * @property {string} token required token to be used on bigsea contextual API requests
19
29
  * @property {?string} targetURL specify the target url instead use the referer
20
- * @property {?Boolean} setPrebidTargeting if true will set the GAM targeting (default true)
21
- * @property {?Boolean} sendToBidders if true, will send the contextual profile to all bidders (default true)
30
+ * @property {?Boolean} setPrebidTargeting if true, will set the GAM targeting (default params.setPrebidTargeting or true)
31
+ * @property {?Boolean} sendToBidders if true, will send the contextual profile to all bidders (default params.sendToBidders or true)
32
+ * @property {?dataCallback} onData callback
22
33
  * @property {?object} defaultProfile to be used if the profile is not found
34
+ * @property {?Boolean} enabled if false, will ignore this configuration
23
35
  */
24
36
 
25
37
  /**
26
38
  * @typedef {Object} WeboUserDataConf
27
- * @property {?string} localStorageProfileKey can be used to customize the local storage key (default is 'webo_wam2gam_entry')
28
- * @property {?Boolean} setPrebidTargeting if true will set the GAM targeting (default true)
29
- * @property {?Boolean} sendToBidders if true, will send the contextual profile to all bidders (default true)
39
+ * @property {?number} accountId wam account id
40
+ * @property {?Boolean} setPrebidTargeting if true, will set the GAM targeting (default params.setPrebidTargeting or true)
41
+ * @property {?Boolean} sendToBidders if true, will send the user-centric profile to all bidders (default params.sendToBidders or true)
30
42
  * @property {?object} defaultProfile to be used if the profile is not found
43
+ * @property {?dataCallback} onData callback
44
+ * @property {?string} localStorageProfileKey can be used to customize the local storage key (default is 'webo_wam2gam_entry')
45
+ * @property {?Boolean} enabled if false, will ignore this configuration
31
46
  */
32
47
 
33
48
  import {
@@ -39,8 +54,10 @@ import {
39
54
  isEmpty,
40
55
  mergeDeep,
41
56
  logError,
57
+ logWarn,
42
58
  tryAppendQueryString,
43
- logMessage
59
+ logMessage,
60
+ isFn
44
61
  } from '../src/utils.js';
45
62
  import {
46
63
  submodule
@@ -86,40 +103,88 @@ let _weboUserDataInitialized = false;
86
103
  function init(moduleConfig) {
87
104
  moduleConfig = moduleConfig || {};
88
105
  const moduleParams = moduleConfig.params || {};
89
- const weboCtxConf = moduleParams.weboCtxConf || {};
106
+ const weboCtxConf = moduleParams.weboCtxConf;
90
107
  const weboUserDataConf = moduleParams.weboUserDataConf;
91
108
 
92
- _weboCtxInitialized = initWeboCtx(weboCtxConf);
93
- _weboUserDataInitialized = initWeboUserData(weboUserDataConf);
109
+ _weboCtxInitialized = initWeboCtx(moduleParams, weboCtxConf);
110
+ _weboUserDataInitialized = initWeboUserData(moduleParams, weboUserDataConf);
94
111
 
95
112
  return _weboCtxInitialized || _weboUserDataInitialized;
96
113
  }
97
114
 
98
115
  /** Initialize contextual sub module
116
+ * @param {ModuleParams} moduleParams
99
117
  * @param {WeboCtxConf} weboCtxConf
100
118
  * @return {Boolean} true if sub module was initialized with success
101
119
  */
102
- function initWeboCtx(weboCtxConf) {
120
+ function initWeboCtx(moduleParams, weboCtxConf) {
121
+ if (!weboCtxConf || weboCtxConf.enabled === false) {
122
+ moduleParams.weboCtxConf = null;
123
+
124
+ return false
125
+ }
126
+
127
+ normalizeConf(moduleParams, weboCtxConf);
128
+
103
129
  _weboCtxInitialized = false;
104
130
  _weboContextualProfile = null;
105
131
 
106
132
  if (!weboCtxConf.token) {
107
- logError('missing param "token" for weborama contextual sub module initialization');
133
+ logWarn('missing param "token" for weborama contextual sub module initialization');
108
134
  return false;
109
135
  }
110
136
 
137
+ logMessage('weborama contextual intialized with success');
138
+
111
139
  return true;
112
140
  }
113
141
 
114
142
  /** Initialize weboUserData sub module
143
+ * @param {ModuleParams} moduleParams
115
144
  * @param {WeboUserDataConf} weboUserDataConf
116
145
  * @return {Boolean} true if sub module was initialized with success
117
146
  */
118
- function initWeboUserData(weboUserDataConf) {
147
+ function initWeboUserData(moduleParams, weboUserDataConf) {
148
+ if (!weboUserDataConf || weboUserDataConf.enabled === false) {
149
+ moduleParams.weboUserDataConf = null;
150
+
151
+ return false;
152
+ }
153
+
154
+ normalizeConf(moduleParams, weboUserDataConf);
155
+
119
156
  _weboUserDataInitialized = false;
120
157
  _weboUserDataUserProfile = null;
121
158
 
122
- return !!weboUserDataConf;
159
+ let message = 'weborama user-centric intialized with success';
160
+ if (weboUserDataConf.hasOwnProperty('accountId')) {
161
+ message = `weborama user-centric intialized with success for account: ${weboUserDataConf.accountId}`;
162
+ }
163
+
164
+ logMessage(message);
165
+
166
+ return true;
167
+ }
168
+
169
+ /** @type {Object} */
170
+ const globalDefaults = {
171
+ setPrebidTargeting: true,
172
+ sendToBidders: true,
173
+ onData: (data, kind, def) => logMessage('onData(data,kind,default)', data, kind, def),
174
+ }
175
+
176
+ /** normalize submodule configuration
177
+ * @param {ModuleParams} moduleParams
178
+ * @param {WeboCtxConf|WeboUserDataConf} submoduleParams
179
+ * @return {void}
180
+ */
181
+ function normalizeConf(moduleParams, submoduleParams) {
182
+ Object.entries(globalDefaults).forEach(([propertyName, globalDefaultValue]) => {
183
+ if (!submoduleParams.hasOwnProperty(propertyName)) {
184
+ const hasModuleParam = moduleParams.hasOwnProperty(propertyName);
185
+ submoduleParams[propertyName] = (hasModuleParam) ? moduleParams[propertyName] : globalDefaultValue;
186
+ }
187
+ })
123
188
  }
124
189
 
125
190
  /** function that provides ad server targeting data to RTD-core
@@ -132,8 +197,8 @@ function getTargetingData(adUnitsCodes, moduleConfig) {
132
197
  const moduleParams = moduleConfig.params || {};
133
198
  const weboCtxConf = moduleParams.weboCtxConf || {};
134
199
  const weboUserDataConf = moduleParams.weboUserDataConf || {};
135
- const weboCtxConfTargeting = weboCtxConf.setPrebidTargeting !== false;
136
- const weboUserDataConfTargeting = weboUserDataConf.setPrebidTargeting !== false;
200
+ const weboCtxConfTargeting = weboCtxConf.setPrebidTargeting;
201
+ const weboUserDataConfTargeting = weboUserDataConf.setPrebidTargeting;
137
202
 
138
203
  try {
139
204
  const profile = getCompleteProfile(moduleParams, weboCtxConfTargeting, weboUserDataConfTargeting);
@@ -250,21 +315,79 @@ export function getBidRequestData(reqBidsConfigObj, onDone, moduleConfig) {
250
315
  function handleBidRequestData(adUnits, moduleParams) {
251
316
  const weboCtxConf = moduleParams.weboCtxConf || {};
252
317
  const weboUserDataConf = moduleParams.weboUserDataConf || {};
253
- const weboCtxConfTargeting = weboCtxConf.sendToBidders !== false;
254
- const weboUserDataConfTargeting = weboUserDataConf.sendToBidders !== false;
255
- const profile = getCompleteProfile(moduleParams, weboCtxConfTargeting, weboUserDataConfTargeting);
318
+ const weboCtxConfTargeting = weboCtxConf.sendToBidders;
319
+ const weboUserDataConfTargeting = weboUserDataConf.sendToBidders;
256
320
 
257
- if (isEmpty(profile)) {
258
- return;
321
+ if (weboCtxConfTargeting) {
322
+ const contextualProfile = getContextualProfile(weboCtxConf);
323
+ if (!isEmpty(contextualProfile)) {
324
+ setBidRequestProfile(adUnits, contextualProfile, true);
325
+ }
326
+ }
327
+
328
+ if (weboUserDataConfTargeting) {
329
+ const weboUserDataProfile = getWeboUserDataProfile(weboUserDataConf);
330
+ if (!isEmpty(weboUserDataProfile)) {
331
+ setBidRequestProfile(adUnits, weboUserDataProfile, false);
332
+ }
259
333
  }
260
334
 
335
+ handleOnData(weboCtxConf, weboUserDataConf);
336
+ }
337
+
338
+ /** function that handle with onData callbacks
339
+ * @param {WeboCtxConf} weboCtxConf
340
+ * @param {WeboUserDataConf} weboUserDataConf
341
+ */
342
+
343
+ function handleOnData(weboCtxConf, weboUserDataConf) {
344
+ const callbacks = [{
345
+ onData: weboCtxConf.onData,
346
+ fetchData: () => getContextualProfile(weboCtxConf),
347
+ site: true,
348
+ }, {
349
+ onData: weboUserDataConf.onData,
350
+ fetchData: () => getWeboUserDataProfile(weboUserDataConf),
351
+ site: false,
352
+ }];
353
+
354
+ callbacks.filter(obj => isFn(obj.onData)).forEach(obj => {
355
+ try {
356
+ const data = obj.fetchData();
357
+ obj.onData(data, obj.site);
358
+ } catch (e) {
359
+ const kind = (obj.site) ? 'site' : 'user';
360
+ logError(`error while executure onData callback with ${kind}-based data:`, e);
361
+ }
362
+ });
363
+ }
364
+
365
+ /** function that set bid request data on each segment (site or user centric)
366
+ * @param {Object[]} adUnits
367
+ * @param {Object} profile
368
+ * @param {Boolean} site true if site centric, else it is user centric
369
+ * @returns {void}
370
+ */
371
+ function setBidRequestProfile(adUnits, profile, site) {
372
+ setGlobalOrtb2(profile, site);
373
+
261
374
  adUnits.forEach(adUnit => {
262
375
  if (adUnit.hasOwnProperty('bids')) {
263
- adUnit.bids.forEach(bid => handleBid(adUnit, profile, bid));
376
+ const adUnitCode = adUnit.code || 'no code';
377
+ adUnit.bids.forEach(bid => handleBid(adUnitCode, profile, site, bid));
264
378
  }
265
379
  });
266
380
  }
267
381
 
382
+ /** @type {string} */
383
+ const APPNEXUS = 'appnexus';
384
+
385
+ /** @type {string} */
386
+ const PUBMATIC = 'pubmatic';
387
+
388
+ /** @type {string} */
389
+ const RUBICON = 'rubicon';
390
+
268
391
  /** @type {string} */
269
392
  const SMARTADSERVER = 'smartadserver';
270
393
 
@@ -272,35 +395,143 @@ const SMARTADSERVER = 'smartadserver';
272
395
  const bidderAliasRegistry = adapterManager.aliasRegistry || {};
273
396
 
274
397
  /** handle individual bid
275
- * @param {Object} adUnit
398
+ * @param {string} adUnitCode
276
399
  * @param {Object} profile
400
+ * @param {Boolean} site true if site centric, else it is user centric
277
401
  * @param {Object} bid
278
402
  * @returns {void}
279
403
  */
280
- function handleBid(adUnit, profile, bid) {
404
+ function handleBid(adUnitCode, profile, site, bid) {
281
405
  const bidder = bidderAliasRegistry[bid.bidder] || bid.bidder;
282
406
 
283
- logMessage('handle bidder', bidder, bid);
407
+ logMessage(`handling on adunit '${adUnitCode}', bidder '${bidder}' and bid`, bid);
284
408
 
285
409
  switch (bidder) {
410
+ case APPNEXUS:
411
+ handleAppnexusBid(profile, bid);
412
+
413
+ break;
414
+
415
+ case PUBMATIC:
416
+ handlePubmaticBid(profile, bid);
417
+
418
+ break;
419
+
286
420
  case SMARTADSERVER:
287
- handleSmartadserverBid(adUnit, profile, bid);
421
+ handleSmartadserverBid(profile, bid);
288
422
 
289
423
  break;
424
+ case RUBICON:
425
+ handleRubiconBid(profile, site, bid);
426
+
427
+ break;
428
+ default:
429
+ logMessage(`unsupported bidder '${bidder}', trying via bidder ortb2 fpd`);
430
+ const section = ((site) ? 'site' : 'user');
431
+ const base = `ortb2.${section}.ext.data`;
432
+
433
+ assignProfileToObject(bid, base, profile);
434
+ }
435
+ }
436
+
437
+ /**
438
+ * set ortb2 global data
439
+ * @param {Object} profile
440
+ * @param {Boolean} site
441
+ * @returns {void}
442
+ */
443
+ function setGlobalOrtb2(profile, site) {
444
+ const section = ((site) ? 'site' : 'user');
445
+ const base = `${section}.ext.data`;
446
+ const addOrtb2 = {};
447
+
448
+ assignProfileToObject(addOrtb2, base, profile);
449
+
450
+ if (!isEmpty(addOrtb2)) {
451
+ const testGlobal = getGlobal().getConfig('ortb2') || {};
452
+ const ortb2 = {
453
+ ortb2: mergeDeep({}, testGlobal, addOrtb2)
454
+ };
455
+ getGlobal().setConfig(ortb2);
456
+ }
457
+ }
458
+
459
+ /**
460
+ * assign profile to object
461
+ * @param {Object} destination
462
+ * @param {string} base
463
+ * @param {Object} profile
464
+ * @returns {void}
465
+ */
466
+ function assignProfileToObject(destination, base, profile) {
467
+ Object.keys(profile).forEach(key => {
468
+ const path = `${base}.${key}`;
469
+ deepSetValue(destination, path, profile[key])
470
+ })
471
+ }
472
+
473
+ /** handle rubicon bid
474
+ * @param {Object} profile
475
+ * @param {Boolean} site
476
+ * @param {Object} bid
477
+ * @returns {void}
478
+ */
479
+ function handleRubiconBid(profile, site, bid) {
480
+ const section = (site) ? 'inventory' : 'visitor';
481
+ const base = `params.${section}`;
482
+ assignProfileToObject(bid, base, profile);
483
+ }
484
+
485
+ /** handle appnexus/xandr bid
486
+ * @param {Object} profile
487
+ * @param {Object} bid
488
+ * @returns {void}
489
+ */
490
+ function handleAppnexusBid(profile, bid) {
491
+ const base = 'params.keywords';
492
+ assignProfileToObject(bid, base, profile);
493
+ }
494
+
495
+ /** handle pubmatic bid
496
+ * @param {Object} profile
497
+ * @param {Object} bid
498
+ * @returns {void}
499
+ */
500
+ function handlePubmaticBid(profile, bid) {
501
+ const sep = '|';
502
+ const subsep = ',';
503
+ const bidKey = 'params.dctr';
504
+ const target = [];
505
+
506
+ const data = deepAccess(bid, bidKey);
507
+ if (data) {
508
+ data.split(sep).forEach(t => target.push(t));
290
509
  }
510
+
511
+ Object.keys(profile).forEach(key => {
512
+ const value = profile[key].join(subsep);
513
+ const keyword = `${key}=${value}`;
514
+ if (target.indexOf(keyword) === -1) {
515
+ target.push(keyword);
516
+ }
517
+ });
518
+
519
+ deepSetValue(bid, bidKey, target.join(sep));
291
520
  }
292
521
 
293
522
  /** handle smartadserver bid
294
- * @param {Object} adUnit
295
523
  * @param {Object} profile
296
524
  * @param {Object} bid
297
525
  * @returns {void}
298
526
  */
299
- function handleSmartadserverBid(adUnit, profile, bid) {
527
+ function handleSmartadserverBid(profile, bid) {
528
+ const sep = ';';
529
+ const bidKey = 'params.target';
300
530
  const target = [];
301
531
 
302
- if (deepAccess(bid, 'params.target')) {
303
- target.push(bid.params.target.split(';'));
532
+ const data = deepAccess(bid, bidKey);
533
+ if (data) {
534
+ data.split(sep).forEach(t => target.push(t));
304
535
  }
305
536
 
306
537
  Object.keys(profile).forEach(key => {
@@ -311,8 +542,7 @@ function handleSmartadserverBid(adUnit, profile, bid) {
311
542
  }
312
543
  });
313
544
  });
314
-
315
- deepSetValue(bid, 'params.target', target.join(';'));
545
+ deepSetValue(bid, bidKey, target.join(sep));
316
546
  }
317
547
 
318
548
  /** set bigsea contextual profile on module state
@@ -350,7 +580,7 @@ function fetchContextualProfile(weboCtxConf, onSuccess, onDone) {
350
580
  queryString = tryAppendQueryString(queryString, 'token', token);
351
581
  queryString = tryAppendQueryString(queryString, 'url', targetURL);
352
582
 
353
- const url = 'https://ctx.weborama.com/api/profile?' + queryString;
583
+ const url = `https://ctx.weborama.com/api/profile?${queryString}`;
354
584
 
355
585
  ajax(url, {
356
586
  success: function(response, req) {