prebid.js 7.27.0 → 7.28.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 (147) hide show
  1. package/.github/workflows/issue_tracker.yml +1 -1
  2. package/dist/33acrossBidAdapter.js +1 -1
  3. package/dist/33acrossIdSystem.js +1 -1
  4. package/dist/adagioBidAdapter.js +1 -1
  5. package/dist/adbookpspBidAdapter.js +1 -1
  6. package/dist/adgenerationBidAdapter.js +1 -1
  7. package/dist/adhashBidAdapter.js +1 -1
  8. package/dist/adrelevantisBidAdapter.js +1 -1
  9. package/dist/adtrgtmeBidAdapter.js +1 -1
  10. package/dist/adxcgBidAdapter.js +1 -1
  11. package/dist/ajaBidAdapter.js +1 -1
  12. package/dist/amxBidAdapter.js +1 -1
  13. package/dist/amxIdSystem.js +1 -1
  14. package/dist/appierAnalyticsAdapter.js +1 -1
  15. package/dist/appnexusBidAdapter.js +1 -1
  16. package/dist/asoBidAdapter.js +1 -1
  17. package/dist/axonixBidAdapter.js +1 -1
  18. package/dist/bidglassBidAdapter.js +1 -1
  19. package/dist/big-richmediaBidAdapter.js +1 -1
  20. package/dist/bridgewellBidAdapter.js +1 -1
  21. package/dist/brightMountainMediaBidAdapter.js +1 -1
  22. package/dist/carodaBidAdapter.js +1 -1
  23. package/dist/chtnwBidAdapter.js +1 -1
  24. package/dist/concertBidAdapter.js +1 -1
  25. package/dist/connectadBidAdapter.js +1 -1
  26. package/dist/consumableBidAdapter.js +1 -1
  27. package/dist/conversantBidAdapter.js +1 -1
  28. package/dist/craftBidAdapter.js +1 -1
  29. package/dist/criteoBidAdapter.js +1 -1
  30. package/dist/discoveryBidAdapter.js +1 -1
  31. package/dist/dspxBidAdapter.js +1 -1
  32. package/dist/eplanningBidAdapter.js +1 -1
  33. package/dist/finativeBidAdapter.js +1 -1
  34. package/dist/glimpseBidAdapter.js +1 -1
  35. package/dist/gmosspBidAdapter.js +1 -1
  36. package/dist/goldbachBidAdapter.js +1 -1
  37. package/dist/gridBidAdapter.js +1 -1
  38. package/dist/gridNMBidAdapter.js +1 -1
  39. package/dist/gumgumBidAdapter.js +1 -1
  40. package/dist/h12mediaBidAdapter.js +1 -1
  41. package/dist/id5IdSystem.js +1 -1
  42. package/dist/improvedigitalBidAdapter.js +1 -1
  43. package/dist/inmarBidAdapter.js +1 -1
  44. package/dist/insticatorBidAdapter.js +1 -1
  45. package/dist/ixBidAdapter.js +1 -1
  46. package/dist/justpremiumBidAdapter.js +1 -1
  47. package/dist/kargoBidAdapter.js +1 -1
  48. package/dist/konduitAnalyticsAdapter.js +1 -1
  49. package/dist/kueezBidAdapter.js +1 -1
  50. package/dist/kueezRtbBidAdapter.js +1 -1
  51. package/dist/lassoBidAdapter.js +1 -1
  52. package/dist/lifestreetBidAdapter.js +1 -1
  53. package/dist/liveIntentIdSystem.js +1 -1
  54. package/dist/liveyieldAnalyticsAdapter.js +1 -1
  55. package/dist/logicadBidAdapter.js +1 -1
  56. package/dist/loglyliftBidAdapter.js +1 -1
  57. package/dist/magniteAnalyticsAdapter.js +1 -1
  58. package/dist/malltvAnalyticsAdapter.js +1 -1
  59. package/dist/marsmediaBidAdapter.js +1 -1
  60. package/dist/mediafuseBidAdapter.js +1 -1
  61. package/dist/mediagoBidAdapter.js +1 -1
  62. package/dist/mediasquareBidAdapter.js +1 -1
  63. package/dist/mgidBidAdapter.js +1 -1
  64. package/dist/minutemediaBidAdapter.js +1 -1
  65. package/dist/nexx360BidAdapter.js +1 -1
  66. package/dist/not-for-prod/prebid.js +118 -118
  67. package/dist/oguryBidAdapter.js +1 -1
  68. package/dist/onetagBidAdapter.js +1 -1
  69. package/dist/ooloAnalyticsAdapter.js +1 -1
  70. package/dist/outbrainBidAdapter.js +1 -1
  71. package/dist/parrableIdSystem.js +1 -1
  72. package/dist/pixfutureBidAdapter.js +1 -1
  73. package/dist/prebid-core.js +2 -2
  74. package/dist/publinkIdSystem.js +1 -1
  75. package/dist/pubmaticBidAdapter.js +1 -1
  76. package/dist/pubwiseAnalyticsAdapter.js +1 -1
  77. package/dist/pxyzBidAdapter.js +1 -1
  78. package/dist/quantcastBidAdapter.js +1 -1
  79. package/dist/readpeakBidAdapter.js +1 -1
  80. package/dist/relaidoBidAdapter.js +1 -1
  81. package/dist/rhythmoneBidAdapter.js +1 -1
  82. package/dist/riseBidAdapter.js +1 -1
  83. package/dist/rubiconAnalyticsAdapter.js +1 -1
  84. package/dist/rubiconBidAdapter.js +1 -1
  85. package/dist/seedingAllianceBidAdapter.js +1 -1
  86. package/dist/seedtagBidAdapter.js +1 -1
  87. package/dist/sharethroughAnalyticsAdapter.js +1 -1
  88. package/dist/sharethroughBidAdapter.js +1 -1
  89. package/dist/shinezBidAdapter.js +1 -1
  90. package/dist/smaatoBidAdapter.js +1 -1
  91. package/dist/smartadserverBidAdapter.js +1 -1
  92. package/dist/smartxBidAdapter.js +1 -1
  93. package/dist/smilewantedBidAdapter.js +1 -1
  94. package/dist/sonobiBidAdapter.js +1 -1
  95. package/dist/sovrnAnalyticsAdapter.js +1 -1
  96. package/dist/sovrnBidAdapter.js +1 -1
  97. package/dist/sspBCBidAdapter.js +1 -1
  98. package/dist/sublimeBidAdapter.js +1 -1
  99. package/dist/synacormediaBidAdapter.js +1 -1
  100. package/dist/taboolaBidAdapter.js +1 -1
  101. package/dist/targetVideoBidAdapter.js +1 -1
  102. package/dist/teadsBidAdapter.js +1 -1
  103. package/dist/trionBidAdapter.js +1 -1
  104. package/dist/tripleliftBidAdapter.js +1 -1
  105. package/dist/ttdBidAdapter.js +1 -1
  106. package/dist/ucfunnelAnalyticsAdapter.js +1 -1
  107. package/dist/underdogmediaBidAdapter.js +1 -1
  108. package/dist/undertoneBidAdapter.js +1 -1
  109. package/dist/vidazooBidAdapter.js +1 -1
  110. package/dist/videobyteBidAdapter.js +1 -1
  111. package/dist/visxBidAdapter.js +1 -1
  112. package/dist/vuukleBidAdapter.js +1 -1
  113. package/dist/widespaceBidAdapter.js +1 -1
  114. package/dist/winrBidAdapter.js +1 -1
  115. package/dist/yahoosspBidAdapter.js +1 -1
  116. package/dist/yieldmoBidAdapter.js +1 -1
  117. package/dist/yieldoneAnalyticsAdapter.js +1 -1
  118. package/libraries/ortb2.5StrictTranslator/dsl.js +54 -0
  119. package/libraries/ortb2.5StrictTranslator/spec.js +81 -0
  120. package/libraries/ortb2.5StrictTranslator/translator.js +37 -0
  121. package/libraries/ortb2.5Translator/translator.js +82 -0
  122. package/modules/33acrossIdSystem.js +3 -0
  123. package/modules/adhashBidAdapter.js +115 -30
  124. package/modules/adhashBidAdapter.md +1 -3
  125. package/modules/discoveryBidAdapter.js +3 -0
  126. package/modules/kargoBidAdapter.js +1 -1
  127. package/modules/lassoBidAdapter.js +8 -4
  128. package/modules/liveIntentIdSystem.js +1 -2
  129. package/modules/mediagoBidAdapter.js +2 -1
  130. package/modules/openxBidAdapter.md +15 -14
  131. package/modules/taboolaBidAdapter.js +20 -7
  132. package/modules/vidazooBidAdapter.js +20 -7
  133. package/package.json +2 -2
  134. package/src/auction.js +11 -1
  135. package/src/constants.json +2 -1
  136. package/test/helpers/refererDetectionHelper.js +88 -0
  137. package/test/spec/auctionmanager_spec.js +33 -1
  138. package/test/spec/modules/33acrossIdSystem_spec.js +4 -1
  139. package/test/spec/modules/adhashBidAdapter_spec.js +65 -11
  140. package/test/spec/modules/taboolaBidAdapter_spec.js +39 -0
  141. package/test/spec/modules/ttdBidAdapter_spec.js +36 -9
  142. package/test/spec/modules/vidazooBidAdapter_spec.js +134 -3
  143. package/test/spec/ortb2.5StrictTranslator/dsl_spec.js +137 -0
  144. package/test/spec/ortb2.5StrictTranslator/spec_spec.js +358 -0
  145. package/test/spec/ortb2.5StrictTranslator/translator_spec.js +16 -0
  146. package/test/spec/ortb2.5Translator/translator_spec.js +64 -0
  147. package/test/spec/refererDetection_spec.js +1 -88
@@ -1 +1 @@
1
- (self.pbjsChunk=self.pbjsChunk||[]).push([[4572],{83519:function(e,t,n){var a=n(64358),i=n(48928),o=n(64563),r=n(5644),d=n(51039),s=n(18621),c=n(78653),u={},v={},g={},f={};f[r.FP.BID_ADJUSTMENT]=!0,f[r.FP.BIDDER_DONE]=!0,f[r.FP.AUCTION_END]=!0;var l="",p="https://pool.tsukiji.iponweb.net/hba",b="";function I(e,t){e.adUnitCode&&t[e.adUnitCode]&&(e.adUnitName=t[e.adUnitCode]),(0,a.isArray)(e.adUnits)&&e.adUnits.forEach((function(e){e.code&&t[e.code]&&(e.name=t[e.code])})),(0,a.isArray)(e.adUnitCodes)&&(e.adUnitNames=e.adUnitCodes.map((function(e){return t[e]}))),["bids","bidderRequests","bidsReceived","noBids"].forEach((function(n){!function(e,t){(0,a.isArray)(e)&&e.forEach((function(e){I(e,t)}))}(e[n],t)}))}var h=Object.assign((0,o.ZP)({analyticsType:"endpoint"}),{getUrl:function(){return p},track:function(e){var t=e.eventType,n=e.args,i=void 0===n?{}:n;if(t===r.FP.BID_REQUESTED){var o="".concat(i.bidderCode,"_").concat(i.auctionId);u[o]=(0,a.deepClone)(i),u[o].bids=[],i.bids.forEach((function(e){v["".concat(e.bidId,"_").concat(e.auctionId)]=e}))}if(t===r.FP.BID_TIMEOUT&&(0,a.isArray)(i)){var d=h.eventsStorage,p={};i.forEach((function(e){var n="".concat(e.bidId,"_").concat(e.auctionId),a="".concat(e.bidder,"_").concat(e.auctionId);d[e.auctionId]||(d[e.auctionId]={events:[]}),(u[a]||p[e.bidder])&&v[n]&&(p[e.bidder]||(p[e.bidder]=u[a],d[e.auctionId].events.push({eventType:t,params:p[e.bidder]}),delete u[a]),p[e.bidder].bids.push(v[n]),delete v[n])}))}else if(l=i.auctionId||l){var S=h.eventsStorage;S[l]||(S[l]={events:[]});var y=i.refererInfo&&i.refererInfo.page;y&&g[l]!==y&&(g[l]=y);var E=Object.assign({},i);if(delete E.ad,E.bidsReceived&&(E.bidsReceived=E.bidsReceived.map((function(e){var t=Object.assign({},e);return delete t.ad,t}))),f[t]||S[l].events.push({eventType:t,params:E}),t===r.FP.AUCTION_END||t===r.FP.BID_WON){if(E.adServerTargeting=s.q0.getAllTargeting(c.K.getAdUnitCodes(),c.K.getBidsReceived()),h.eventsStorage[l]&&h.eventsStorage[l].events.length){h.eventsStorage[l].page={url:g[l]},h.eventsStorage[l].pubId=b,h.eventsStorage[l].wrapper_version="7.27.0";var A=function(){if(window.googletag&&window.googletag.pubads){var e=googletag.pubads();if(e&&e.getSlots){var t=e.getSlots();if(t&&t.length){var n={};return t.forEach((function(e){var t=e.getSlotElementId(),a=(e.getAdUnitPath()||"").split("/").pop();n[t]=a})),n}}}}();A&&h.eventsStorage[l].events.forEach((function(e){I(e.params,A)}))}h.sendStat(h.eventsStorage[l],l)}}},sendStat:function(e,t){e&&e.events&&e.events.length&&(delete h.eventsStorage[t],(0,i.h)(p,{success:function(){},error:function(){}},JSON.stringify(e),{method:"POST"}))}});h.eventsStorage={},h.originEnableAnalytics=h.enableAnalytics,h.enableAnalytics=function(e){var t=e&&e.options;t&&("string"==typeof t.url&&(p=t.url),t.pubId&&(b=t.pubId.toString())),h.originEnableAnalytics(e)},d.ZP.registerAnalyticsAdapter({adapter:h,code:"yieldone"});window.pbjs.installedModules.push("yieldoneAnalyticsAdapter")}},function(e){e.O(0,[4861],(function(){return t=83519,e(e.s=t);var t}));e.O()}]);
1
+ (self.pbjsChunk=self.pbjsChunk||[]).push([[4572],{83519:function(e,t,n){var a=n(64358),i=n(48928),o=n(64563),r=n(5644),d=n(51039),s=n(18621),c=n(78653),u={},v={},g={},f={};f[r.FP.BID_ADJUSTMENT]=!0,f[r.FP.BIDDER_DONE]=!0,f[r.FP.AUCTION_END]=!0;var l="",p="https://pool.tsukiji.iponweb.net/hba",b="";function I(e,t){e.adUnitCode&&t[e.adUnitCode]&&(e.adUnitName=t[e.adUnitCode]),(0,a.isArray)(e.adUnits)&&e.adUnits.forEach((function(e){e.code&&t[e.code]&&(e.name=t[e.code])})),(0,a.isArray)(e.adUnitCodes)&&(e.adUnitNames=e.adUnitCodes.map((function(e){return t[e]}))),["bids","bidderRequests","bidsReceived","noBids"].forEach((function(n){!function(e,t){(0,a.isArray)(e)&&e.forEach((function(e){I(e,t)}))}(e[n],t)}))}var h=Object.assign((0,o.ZP)({analyticsType:"endpoint"}),{getUrl:function(){return p},track:function(e){var t=e.eventType,n=e.args,i=void 0===n?{}:n;if(t===r.FP.BID_REQUESTED){var o="".concat(i.bidderCode,"_").concat(i.auctionId);u[o]=(0,a.deepClone)(i),u[o].bids=[],i.bids.forEach((function(e){v["".concat(e.bidId,"_").concat(e.auctionId)]=e}))}if(t===r.FP.BID_TIMEOUT&&(0,a.isArray)(i)){var d=h.eventsStorage,p={};i.forEach((function(e){var n="".concat(e.bidId,"_").concat(e.auctionId),a="".concat(e.bidder,"_").concat(e.auctionId);d[e.auctionId]||(d[e.auctionId]={events:[]}),(u[a]||p[e.bidder])&&v[n]&&(p[e.bidder]||(p[e.bidder]=u[a],d[e.auctionId].events.push({eventType:t,params:p[e.bidder]}),delete u[a]),p[e.bidder].bids.push(v[n]),delete v[n])}))}else if(l=i.auctionId||l){var S=h.eventsStorage;S[l]||(S[l]={events:[]});var y=i.refererInfo&&i.refererInfo.page;y&&g[l]!==y&&(g[l]=y);var E=Object.assign({},i);if(delete E.ad,E.bidsReceived&&(E.bidsReceived=E.bidsReceived.map((function(e){var t=Object.assign({},e);return delete t.ad,t}))),f[t]||S[l].events.push({eventType:t,params:E}),t===r.FP.AUCTION_END||t===r.FP.BID_WON){if(E.adServerTargeting=s.q0.getAllTargeting(c.K.getAdUnitCodes(),c.K.getBidsReceived()),h.eventsStorage[l]&&h.eventsStorage[l].events.length){h.eventsStorage[l].page={url:g[l]},h.eventsStorage[l].pubId=b,h.eventsStorage[l].wrapper_version="7.28.0";var A=function(){if(window.googletag&&window.googletag.pubads){var e=googletag.pubads();if(e&&e.getSlots){var t=e.getSlots();if(t&&t.length){var n={};return t.forEach((function(e){var t=e.getSlotElementId(),a=(e.getAdUnitPath()||"").split("/").pop();n[t]=a})),n}}}}();A&&h.eventsStorage[l].events.forEach((function(e){I(e.params,A)}))}h.sendStat(h.eventsStorage[l],l)}}},sendStat:function(e,t){e&&e.events&&e.events.length&&(delete h.eventsStorage[t],(0,i.h)(p,{success:function(){},error:function(){}},JSON.stringify(e),{method:"POST"}))}});h.eventsStorage={},h.originEnableAnalytics=h.enableAnalytics,h.enableAnalytics=function(e){var t=e&&e.options;t&&("string"==typeof t.url&&(p=t.url),t.pubId&&(b=t.pubId.toString())),h.originEnableAnalytics(e)},d.ZP.registerAnalyticsAdapter({adapter:h,code:"yieldone"});window.pbjs.installedModules.push("yieldoneAnalyticsAdapter")}},function(e){e.O(0,[4861],(function(){return t=83519,e(e.s=t);var t}));e.O()}]);
@@ -0,0 +1,54 @@
1
+ export const ERR_TYPE = 0; // field has wrong type (only objects, enums, and arrays of objects or enums are checked)
2
+ export const ERR_UNKNOWN_FIELD = 1; // field is not defined in ORTB 2.5 spec
3
+ export const ERR_ENUM = 2; // field is an enum and its value is not one of those listed in the ORTB 2.5 spec
4
+
5
+ // eslint-disable-next-line symbol-description
6
+ export const extend = Symbol();
7
+
8
+ export function Obj(primitiveFields, spec = {}) {
9
+ const scan = (path, parent, field, value, onError) => {
10
+ if (value == null || typeof value !== 'object') {
11
+ onError(ERR_TYPE, path, parent, field, value);
12
+ return;
13
+ }
14
+ Object.entries(value).forEach(([k, v]) => {
15
+ if (v == null) return;
16
+ const kpath = path == null ? k : `${path}.${k}`;
17
+ if (spec.hasOwnProperty(k)) {
18
+ spec[k](kpath, value, k, v, onError);
19
+ return;
20
+ }
21
+ if (k !== 'ext' && !primitiveFields.includes(k)) {
22
+ onError(ERR_UNKNOWN_FIELD, kpath, value, k, v);
23
+ }
24
+ });
25
+ };
26
+ scan[extend] = (extraPrimitives, specOverride = {}) =>
27
+ Obj(primitiveFields.concat(extraPrimitives), Object.assign({}, spec, specOverride));
28
+ return scan;
29
+ }
30
+
31
+ export const ID = Obj(['id']);
32
+ export const Named = ID[extend](['name']);
33
+
34
+ export function Arr(def) {
35
+ return (path, parent, field, value, onError) => {
36
+ if (!Array.isArray(value)) {
37
+ onError(ERR_TYPE, path, parent, field, value);
38
+ } else {
39
+ value.forEach((item, i) => def(`${path}.${i}`, value, i, item, onError));
40
+ }
41
+ };
42
+ }
43
+
44
+ export function IntEnum(min, max) {
45
+ return (path, parent, field, value, onError) => {
46
+ const errno = (() => {
47
+ if (typeof value !== 'number') return ERR_TYPE;
48
+ if (isNaN(value) || value > max || value < min) return ERR_ENUM;
49
+ })();
50
+ if (errno != null) {
51
+ onError(errno, path, parent, field, value);
52
+ }
53
+ };
54
+ }
@@ -0,0 +1,81 @@
1
+ import {Arr, extend, ID, IntEnum, Named, Obj} from './dsl.js';
2
+
3
+ const CatDomain = Named[extend](['cat', 'domain']);
4
+ const Segment = Named[extend](['value']);
5
+ const Data = Named[extend]([], {
6
+ segment: Arr(Segment)
7
+ });
8
+ const Content = ID[extend](['episode', 'title', 'series', 'season', 'artist', 'genre', 'album', 'isrc', 'url', 'cat', 'contentrating', 'userrating', 'keywords', 'livestream', 'sourcerelationship', 'len', 'language', 'embeddable'], {
9
+ producer: CatDomain,
10
+ data: Arr(Data),
11
+ prodq: IntEnum(0, 3),
12
+ videoquality: IntEnum(0, 3),
13
+ context: IntEnum(1, 7),
14
+ qagmediarating: IntEnum(1, 3),
15
+ });
16
+
17
+ const Client = CatDomain[extend](['sectioncat', 'pagecat', 'privacypolicy', 'keywords'], {
18
+ publisher: CatDomain, content: Content,
19
+ });
20
+ const Site = Client[extend](['page', 'ref', 'search', 'mobile']);
21
+ const App = Client[extend](['bundle', 'storeurl', 'ver', 'paid']);
22
+
23
+ const Geo = Obj(['lat', 'lon', 'accuracy', 'lastfix', 'country', 'region', 'regionfips104', 'metro', 'city', 'zip', 'utcoffset'], {
24
+ type: IntEnum(1, 3),
25
+ ipservice: IntEnum(1, 4)
26
+ });
27
+ const Device = Obj(['ua', 'dnt', 'lmt', 'ip', 'ipv6', 'make', 'model', 'os', 'osv', 'hwv', 'h', 'w', 'ppi', 'pxratio', 'js', 'geofetch', 'flashver', 'language', 'carrier', 'mccmnc', 'ifa', 'didsha1', 'didmd5', 'dpidsha1', 'dpidmd5', 'macsha1', 'macmd5'], {
28
+ geo: Geo, devicetype: IntEnum(1, 7), connectiontype: IntEnum(0, 6)
29
+ });
30
+ const User = ID[extend](['buyeruid', 'yob', 'gender', 'keywords', 'customdata'], {
31
+ geo: Geo, data: Arr(Data),
32
+ });
33
+
34
+ const Floorable = ID[extend](['bidfloor', 'bidfloorcur']);
35
+ const Deal = Floorable[extend](['at', 'wseat', 'wadomain']);
36
+ const Pmp = Obj(['private_auction'], {
37
+ deals: Arr(Deal),
38
+ });
39
+ const Format = Obj(['w', 'h', 'wratio', 'hratio', 'wmin']);
40
+ const MediaType = Obj(['mimes'], {
41
+ api: Arr(IntEnum(1, 6)), battr: Arr(IntEnum(1, 17))
42
+ });
43
+ const Banner = MediaType[extend](['id', 'w', 'h', 'wmax', 'hmax', 'hmin', 'wmin', 'topframe', 'vcm'], {
44
+ format: Arr(Format), btype: Arr(IntEnum(1, 4)), pos: IntEnum(0, 7), expdir: Arr(IntEnum(1, 5))
45
+ });
46
+ const Native = MediaType[extend](['request', 'ver']);
47
+ const RichMediaType = MediaType[extend](['minduration', 'maxduration', 'startdelay', 'sequence', 'maxextended', 'minbitrate', 'maxbitrate'], {
48
+ protocols: Arr(IntEnum(1, 10)),
49
+ delivery: Arr(IntEnum(1, 3)),
50
+ companionad: Arr(Banner),
51
+ companiontype: Arr(IntEnum(1, 3)),
52
+ });
53
+ /*
54
+ const Audio = RichMediaType[extend](['maxseq', 'stitched'], {
55
+ feed: IntEnum(1, 3), nvol: IntEnum(0, 4),
56
+ });
57
+ */
58
+ const Video = RichMediaType[extend](['w', 'h', 'skip', 'skipmin', 'skipafter', 'boxingallowed'], {
59
+ pos: IntEnum(0, 7),
60
+ protocol: IntEnum(1, 10),
61
+ placement: IntEnum(1, 5),
62
+ linearity: IntEnum(1, 2),
63
+ playbackmethod: Arr(IntEnum(1, 6)),
64
+ playbackend: IntEnum(1, 3),
65
+ });
66
+ const Metric = Obj(['type', 'value', 'vendor']);
67
+ const Imp = (() => {
68
+ const spec = {
69
+ metric: Arr(Metric), banner: Banner, video: Video, pmp: Pmp,
70
+ };
71
+ if (FEATURES.NATIVE) {
72
+ spec.native = Native;
73
+ }
74
+ return Floorable[extend](['displaymanager', 'displaymanagerver', 'instl', 'tagid', 'clickbrowser', 'secure', 'iframebuster', 'exp'], spec);
75
+ })();
76
+
77
+ const Regs = Obj(['coppa']);
78
+ const Source = Obj(['fd', 'tid', 'pchain']);
79
+ export const BidRequest = ID[extend](['test', 'at', 'tmax', 'wseat', 'bseat', 'allimps', 'cur', 'wlang', 'bcat', 'badv', 'bapp'], {
80
+ imp: Arr(Imp), site: Site, app: App, device: Device, user: User, source: Source, regs: Regs
81
+ });
@@ -0,0 +1,37 @@
1
+ import {BidRequest} from './spec.js';
2
+ import {logWarn} from '../../src/utils.js';
3
+ import {toOrtb25} from '../ortb2.5Translator/translator.js';
4
+
5
+ function deleteField(errno, path, obj, field, value) {
6
+ logWarn(`${path} is not valid ORTB 2.5, field will be removed from request:`, value);
7
+ Array.isArray(obj) ? obj.splice(field, 1) : delete obj[field];
8
+ }
9
+
10
+ /**
11
+ * Translates an ortb request to 2.5, and removes from the result any field that is:
12
+ * - not defined in the 2.5 spec, or
13
+ * - defined as an enum, but has a value that is not listed in the 2.5 spec.
14
+ *
15
+ * `ortb2` is modified in place and returned.
16
+ *
17
+ * Note that using this utility will cause your adapter to pull in an additional ~3KB after minification.
18
+ * If possible, consider making your endpoint tolerant to unrecognized or invalid fields instead.
19
+ *
20
+ *
21
+ * @param ortb2 ORTB request
22
+ * @param translator translation function. The default moves 2.x fields that have a known standard location in 2.5.
23
+ * See the `ortb2.5Translator` library.
24
+ * @param onError a function invoked once for each field that is not valid according to the 2.5 spec; it takes
25
+ * (errno, path, obj, field, value), where:
26
+ * - errno is an error code (defined in dsl.js)
27
+ * - path is the JSON path of the offending field, for example `regs.gdpr`
28
+ * - obj is the object containing the offending field, for example `ortb2.regs`
29
+ * - field is the field name, for example `'gdpr'`
30
+ * - value is `obj[field]`.
31
+ * The default logs a warning and deletes the offending field.
32
+ */
33
+ export function toOrtb25Strict(ortb2, translator = toOrtb25, onError = deleteField) {
34
+ ortb2 = translator(ortb2);
35
+ BidRequest(null, null, null, ortb2, onError);
36
+ return ortb2;
37
+ }
@@ -0,0 +1,82 @@
1
+ import {deepAccess, deepSetValue, logError} from '../../src/utils.js';
2
+
3
+ export const EXT_PROMOTIONS = [
4
+ 'source.schain',
5
+ 'regs.gdpr',
6
+ 'regs.us_privacy',
7
+ 'regs.gpp',
8
+ 'user.consent',
9
+ 'user.eids'
10
+ ];
11
+
12
+ export function splitPath(path) {
13
+ const parts = path.split('.');
14
+ const prefix = parts.slice(0, parts.length - 1).join('.');
15
+ const field = parts[parts.length - 1];
16
+ return [prefix, field];
17
+ }
18
+
19
+ /**
20
+ * @param sourcePath a JSON path such as `regs.us_privacy`
21
+ * @param dest {function(String, String): String} a function taking (prefix, field) and returning a destination path;
22
+ * for example, ('regs', 'us_privacy') => 'regs.ext.us_privacy'
23
+ * @return {(function({}): (function(): void|undefined))|*} a function that takes an object and, if it contains
24
+ * sourcePath, copies its contents to destinationPath, returning a function that deletes the original sourcePath.
25
+ */
26
+ export function moveRule(sourcePath, dest = (prefix, field) => `${prefix}.ext.${field}`) {
27
+ const [prefix, field] = splitPath(sourcePath);
28
+ dest = dest(prefix, field);
29
+ return (ortb2) => {
30
+ const obj = deepAccess(ortb2, prefix);
31
+ if (obj?.[field] != null) {
32
+ deepSetValue(ortb2, dest, obj[field]);
33
+ return () => delete obj[field];
34
+ }
35
+ };
36
+ }
37
+
38
+ function kwarrayRule(section) {
39
+ // move 2.6 `kwarray` into 2.5 comma-separated `keywords`.
40
+ return (ortb2) => {
41
+ const kwarray = ortb2[section]?.kwarray;
42
+ if (kwarray != null) {
43
+ let kw = (ortb2[section].keywords || '').split(',');
44
+ if (Array.isArray(kwarray)) kw.push(...kwarray);
45
+ ortb2[section].keywords = kw.join(',');
46
+ return () => delete ortb2[section].kwarray;
47
+ }
48
+ };
49
+ }
50
+
51
+ export const DEFAULT_RULES = Object.freeze([
52
+ ...EXT_PROMOTIONS.map((f) => moveRule(f)),
53
+ ...['app', 'content', 'site', 'user'].map(kwarrayRule)
54
+ ]);
55
+
56
+ /**
57
+ * Factory for ORTB 2.5 translation functions.
58
+ *
59
+ * @param deleteFields if true, the translation function will remove fields that have been translated (transferred somewhere else within the request)
60
+ * @param rules translation rules; an array of functions of the type returned by `moveRule`
61
+ * @return {function({}): {}} a translation function that takes an ORTB object, modifies it in place, and returns it.
62
+ */
63
+ export function ortb25Translator(deleteFields = true, rules = DEFAULT_RULES) {
64
+ return function (ortb2) {
65
+ rules.forEach(f => {
66
+ try {
67
+ const deleter = f(ortb2);
68
+ if (typeof deleter === 'function' && deleteFields) deleter();
69
+ } catch (e) {
70
+ logError('Error translating request to ORTB 2.5', e);
71
+ }
72
+ })
73
+ return ortb2;
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Translate an ortb request to version 2.5 by moving 2.6 (and later) fields that have a standardized 2.5 extension.
79
+ *
80
+ * The request is modified in place and returned.
81
+ */
82
+ export const toOrtb25 = ortb25Translator();
@@ -13,6 +13,7 @@ import { uspDataHandler } from '../src/adapterManager.js';
13
13
  const MODULE_NAME = '33acrossId';
14
14
  const API_URL = 'https://lexicon.33across.com/v1/envelope';
15
15
  const AJAX_TIMEOUT = 10000;
16
+ const CALLER_NAME = 'pbjs';
16
17
 
17
18
  function getEnvelope(response) {
18
19
  if (!response.succeeded) {
@@ -36,6 +37,8 @@ function calculateQueryStringParams(pid, gdprConsentData) {
36
37
  const params = {
37
38
  pid,
38
39
  gdpr: Number(gdprApplies),
40
+ src: CALLER_NAME,
41
+ ver: '$prebid.version$'
39
42
  };
40
43
 
41
44
  if (uspString) {
@@ -1,10 +1,12 @@
1
1
  import {registerBidder} from '../src/adapters/bidderFactory.js';
2
- import {includes} from '../src/polyfill.js';
3
- import {BANNER} from '../src/mediaTypes.js';
2
+ import { getStorageManager } from '../src/storageManager.js';
3
+ import { includes } from '../src/polyfill.js';
4
+ import { BANNER } from '../src/mediaTypes.js';
4
5
 
5
- const VERSION = '1.0';
6
+ const VERSION = '3.2';
6
7
  const BAD_WORD_STEP = 0.1;
7
8
  const BAD_WORD_MIN = 0.2;
9
+ const ADHASH_BIDDER_CODE = 'adhash';
8
10
 
9
11
  /**
10
12
  * Function that checks the page where the ads are being served for brand safety.
@@ -54,43 +56,92 @@ function brandSafety(badWords, maxScore) {
54
56
  return positive ? result : -result;
55
57
  };
56
58
 
59
+ /**
60
+ * Checks what rule will match in the given array with words
61
+ * @param {string} rule rule type (full, partial, starts, ends, regexp)
62
+ * @param {string} decodedWord decoded word
63
+ * @param {array} wordsToMatch array to find a match
64
+ * @returns {object|boolean} matched rule and occurances. If nothing is matched returns false
65
+ */
66
+ const wordsMatchedWithRule = function (rule, decodedWord, wordsToMatch) {
67
+ if (rule === 'full' && wordsToMatch && wordsToMatch.includes(decodedWord)) {
68
+ return { rule, occurances: wordsToMatch.filter(element => element === decodedWord).length };
69
+ } else if (rule === 'partial' && wordsToMatch && wordsToMatch.some(element => element.indexOf(decodedWord) > -1)) {
70
+ return { rule, occurances: wordsToMatch.filter(element => element.indexOf(decodedWord) > -1).length };
71
+ } else if (rule === 'starts' && wordsToMatch && wordsToMatch.some(word => word.startsWith(decodedWord))) {
72
+ return { rule, occurances: wordsToMatch.filter(element => element.startsWith(decodedWord)).length };
73
+ } else if (rule === 'ends' && wordsToMatch && wordsToMatch.some(word => word.endsWith(decodedWord))) {
74
+ return { rule, occurances: wordsToMatch.filter(element => element.endsWith(decodedWord)).length };
75
+ } else if (rule === 'regexp' && wordsToMatch && wordsToMatch.some(element => element.match(new RegExp(decodedWord, 'i')))) {
76
+ return { rule, occurances: wordsToMatch.filter(element => element.match(new RegExp(decodedWord, 'i'))).length };
77
+ }
78
+ return false;
79
+ };
80
+
57
81
  // Default parameters if the bidder is unable to send some of them
58
82
  badWords = badWords || [];
59
83
  maxScore = parseInt(maxScore) || 10;
60
84
 
61
85
  try {
62
86
  let score = 0;
87
+ const decodedUrl = decodeURI(window.top.location.href.substring(window.top.location.origin.length));
88
+ const wordsAndNumbersInUrl = decodedUrl
89
+ .replaceAll(/[-,\._/\?=&#%]/g, ' ')
90
+ .replaceAll(/\s\s+/g, ' ')
91
+ .toLowerCase()
92
+ .trim();
63
93
  const content = window.top.document.body.innerText.toLowerCase();
64
- const words = content.trim().split(/\s+/).length;
94
+ const contentWords = content.trim().split(/\s+/).length;
95
+ // \p{L} matches a single unicode code point in the category 'letter'. Matches any kind of letter from any language.
96
+ const regexp = new RegExp('[\\p{L}]+', 'gu');
97
+ const words = content.match(regexp);
98
+ const wordsInUrl = wordsAndNumbersInUrl.match(regexp);
99
+
65
100
  for (const [word, rule, points] of badWords) {
66
- if (rule === 'full' && new RegExp('\\b' + rot13(word) + '\\b', 'i').test(content)) {
67
- const occurances = content.match(new RegExp('\\b' + rot13(word) + '\\b', 'g')).length;
68
- score += scoreCalculator(points, occurances);
69
- } else if (rule === 'partial' && content.indexOf(rot13(word.toLowerCase())) > -1) {
70
- const occurances = content.match(new RegExp(rot13(word), 'g')).length;
71
- score += scoreCalculator(points, occurances);
101
+ const decodedWord = rot13(word.toLowerCase());
102
+
103
+ // Checks the words in the url of the page only for negative words. Don't serve any ad when at least one match is found
104
+ if (points > 0) {
105
+ const matchedRuleInUrl = wordsMatchedWithRule(rule, decodedWord, wordsInUrl);
106
+ if (matchedRuleInUrl.rule) {
107
+ return false;
108
+ }
109
+ }
110
+
111
+ // Check if site content's words match any of our brand safety rules
112
+ const matchedRule = wordsMatchedWithRule(rule, decodedWord, words);
113
+ if (matchedRule.rule === 'full') {
114
+ score += scoreCalculator(points, matchedRule.occurances);
115
+ } else if (matchedRule.rule === 'partial') {
116
+ score += scoreCalculator(points, matchedRule.occurances);
117
+ } else if (matchedRule.rule === 'starts') {
118
+ score += scoreCalculator(points, matchedRule.occurances);
119
+ } else if (matchedRule.rule === 'ends') {
120
+ score += scoreCalculator(points, matchedRule.occurances);
121
+ } else if (matchedRule.rule === 'regexp') {
122
+ score += scoreCalculator(points, matchedRule.occurances);
72
123
  }
73
124
  }
74
- return score < maxScore * words / 500;
125
+ return score < (maxScore * contentWords) / 1000;
75
126
  } catch (e) {
76
127
  return true;
77
128
  }
78
129
  }
79
130
 
80
131
  export const spec = {
81
- code: 'adhash',
82
- url: 'https://bidder.adhash.com/rtb?version=' + VERSION + '&prebid=true',
132
+ code: ADHASH_BIDDER_CODE,
83
133
  supportedMediaTypes: [ BANNER ],
84
134
 
85
135
  isBidRequestValid: (bid) => {
86
136
  try {
87
- const { publisherId, platformURL } = bid.params;
137
+ const { publisherId, platformURL, bidderURL } = bid.params;
88
138
  return (
89
139
  includes(Object.keys(bid.mediaTypes), BANNER) &&
90
140
  typeof publisherId === 'string' &&
91
141
  publisherId.length === 42 &&
92
142
  typeof platformURL === 'string' &&
93
- platformURL.length >= 13
143
+ platformURL.length >= 13 &&
144
+ (!bidderURL || bidderURL.indexOf('https://') === 0)
94
145
  );
95
146
  } catch (error) {
96
147
  return false;
@@ -98,24 +149,51 @@ export const spec = {
98
149
  },
99
150
 
100
151
  buildRequests: (validBidRequests, bidderRequest) => {
152
+ const storage = getStorageManager({ bidderCode: ADHASH_BIDDER_CODE });
101
153
  const { gdprConsent } = bidderRequest;
102
- const { url } = spec;
103
154
  const bidRequests = [];
104
- let referrer = '';
105
- if (bidderRequest && bidderRequest.refererInfo) {
106
- // TODO: is 'page' the right value here?
107
- referrer = bidderRequest.refererInfo.page;
108
- }
109
- for (var i = 0; i < validBidRequests.length; i++) {
110
- var index = Math.floor(Math.random() * validBidRequests[i].sizes.length);
111
- var size = validBidRequests[i].sizes[index].join('x');
155
+ const body = document.body;
156
+ const html = document.documentElement;
157
+ const pageHeight = Math.max(
158
+ body.scrollHeight,
159
+ body.offsetHeight,
160
+ html.clientHeight,
161
+ html.scrollHeight,
162
+ html.offsetHeight
163
+ );
164
+ const pageWidth = Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth);
165
+
166
+ for (let i = 0; i < validBidRequests.length; i++) {
167
+ const bidderURL = validBidRequests[i].params.bidderURL || 'https://bidder.adhash.com';
168
+ const url = `${bidderURL}/rtb?version=${VERSION}&prebid=true`;
169
+ const index = Math.floor(Math.random() * validBidRequests[i].sizes.length);
170
+ const size = validBidRequests[i].sizes[index].join('x');
171
+
172
+ let recentAds = [];
173
+ if (storage.localStorageIsEnabled()) {
174
+ const prefix = validBidRequests[i].params.prefix || 'adHash';
175
+ recentAds = JSON.parse(storage.getDataFromLocalStorage(prefix + 'recentAds') || '[]');
176
+ }
177
+
178
+ // Needed for the ad density calculation
179
+ var adHeight = validBidRequests[i].sizes[index][1];
180
+ var adWidth = validBidRequests[i].sizes[index][0];
181
+ if (!window.adsCount) {
182
+ window.adsCount = 0;
183
+ }
184
+ if (!window.adsTotalSurface) {
185
+ window.adsTotalSurface = 0;
186
+ }
187
+ window.adsTotalSurface += adHeight * adWidth;
188
+ window.adsCount++;
189
+
112
190
  bidRequests.push({
113
191
  method: 'POST',
114
192
  url: url + '&publisher=' + validBidRequests[i].params.publisherId,
115
193
  bidRequest: validBidRequests[i],
116
194
  data: {
117
195
  timezone: new Date().getTimezoneOffset() / 60,
118
- location: referrer,
196
+ location: bidderRequest.refererInfo ? bidderRequest.refererInfo.topmostLocation : '',
119
197
  publisherId: validBidRequests[i].params.publisherId,
120
198
  size: {
121
199
  screenWidth: window.screen.width,
@@ -131,10 +209,14 @@ export const spec = {
131
209
  position: validBidRequests[i].adUnitCode
132
210
  }],
133
211
  blockedCreatives: [],
134
- currentTimestamp: new Date().getTime(),
135
- recentAds: [],
212
+ currentTimestamp: (new Date().getTime() / 1000) | 0,
213
+ recentAds: recentAds,
136
214
  GDPRApplies: gdprConsent ? gdprConsent.gdprApplies : null,
137
- GDPR: gdprConsent ? gdprConsent.consentString : null
215
+ GDPR: gdprConsent ? gdprConsent.consentString : null,
216
+ servedAdsCount: window.adsCount,
217
+ adsTotalSurface: window.adsTotalSurface,
218
+ pageHeight: pageHeight,
219
+ pageWidth: pageWidth
138
220
  },
139
221
  options: {
140
222
  withCredentials: false,
@@ -157,7 +239,11 @@ export const spec = {
157
239
  }
158
240
 
159
241
  const publisherURL = JSON.stringify(request.bidRequest.params.platformURL);
242
+ const bidderURL = request.bidRequest.params.bidderURL || 'https://bidder.adhash.com';
160
243
  const oneTimeId = request.bidRequest.adUnitCode + Math.random().toFixed(16).replace('0.', '.');
244
+ const globalScript = !request.bidRequest.params.globalScript
245
+ ? `<script src="${bidderURL}/static/scripts/creative.min.js"></script>`
246
+ : '';
161
247
  const bidderResponse = JSON.stringify({ responseText: JSON.stringify(responseBody) });
162
248
  const requestData = JSON.stringify(request.data);
163
249
 
@@ -165,8 +251,7 @@ export const spec = {
165
251
  requestId: request.bidRequest.bidId,
166
252
  cpm: responseBody.creatives[0].costEUR,
167
253
  ad:
168
- `<div id="${oneTimeId}"></div>
169
- <script src="https://bidder.adhash.com/static/scripts/creative.min.js"></script>
254
+ `<div id="${oneTimeId}"></div>${globalScript}
170
255
  <script>callAdvertiser(${bidderResponse},['${oneTimeId}'],${requestData},${publisherURL})</script>`,
171
256
  width: request.bidRequest.sizes[0][0],
172
257
  height: request.bidRequest.sizes[0][1],
@@ -3,7 +3,7 @@
3
3
  ```
4
4
  Module Name: AdHash Bidder Adapter
5
5
  Module Type: Bidder Adapter
6
- Maintainer: damyan@adhash.org
6
+ Maintainer: damyan@adhash.com
7
7
  ```
8
8
 
9
9
  # Description
@@ -14,8 +14,6 @@ Here is what you need for Prebid integration with AdHash:
14
14
  3. Use the Publisher ID and Platform URL as parameters in params.
15
15
 
16
16
  Please note that a number of AdHash functionalities are not supported in the Prebid.js integration:
17
- * Cookie-less frequency and recency capping;
18
- * Audience segments;
19
17
  * Price floors and passback tags, as they are not needed in the Prebid.js setup;
20
18
  * Reservation for direct deals only, as bids are evaluated based on their price.
21
19
 
@@ -277,6 +277,8 @@ function getItems(validBidRequests, bidderRequest) {
277
277
  function getParam(validBidRequests, bidderRequest) {
278
278
  const pubcid = utils.deepAccess(validBidRequests[0], 'crumbs.pubcid');
279
279
  let isMobile = getDevice() ? 1 : 0;
280
+ // input test status by Publisher. more frequently for test true req
281
+ let isTest = validBidRequests[0].params.test || 0;
280
282
  let auctionId = getKv(bidderRequest, 'auctionId');
281
283
  let items = getItems(validBidRequests, bidderRequest);
282
284
 
@@ -290,6 +292,7 @@ function getParam(validBidRequests, bidderRequest) {
290
292
  if (items && items.length) {
291
293
  let c = {
292
294
  id: 'pp_hbjs_' + auctionId,
295
+ test: +isTest,
293
296
  at: 1,
294
297
  bcat: globals['bcat'],
295
298
  badv: globals['adv'],
@@ -231,7 +231,7 @@ export const spec = {
231
231
  _getAllMetadata(bidderRequest, tdid) {
232
232
  return {
233
233
  userIDs: spec._getUserIds(tdid, bidderRequest.uspConsent, bidderRequest.gdprConsent),
234
- pageURL: bidderRequest?.refererInfo?.topmostLocation || bidderRequest?.refererInfo?.page,
234
+ pageURL: bidderRequest?.refererInfo?.page,
235
235
  rawCRB: storage.getCookie('krg_crb'),
236
236
  rawCRBLocalStorage: spec._getLocalStorageSafely('krg_crb')
237
237
  };
@@ -54,7 +54,7 @@ export const spec = {
54
54
 
55
55
  return {
56
56
  method: 'GET',
57
- url: getBidRequestUrl(aimXR),
57
+ url: getBidRequestUrl(aimXR, bidRequest.params),
58
58
  data: payload,
59
59
  options: {
60
60
  withCredentials: true
@@ -113,11 +113,15 @@ export const spec = {
113
113
  supportedMediaTypes: [BANNER]
114
114
  }
115
115
 
116
- function getBidRequestUrl(aimXR) {
116
+ function getBidRequestUrl(aimXR, params) {
117
+ let path = '/request';
118
+ if (params && params.dtc) {
119
+ path = '/dtc-request';
120
+ }
117
121
  if (!aimXR) {
118
- return GET_IUD_URL + ENDPOINT_URL + '/request';
122
+ return GET_IUD_URL + ENDPOINT_URL + path;
119
123
  }
120
- return ENDPOINT_URL + '/request'
124
+ return ENDPOINT_URL + path;
121
125
  }
122
126
 
123
127
  function getDeviceData() {
@@ -10,7 +10,6 @@ import { submodule } from '../src/hook.js';
10
10
  import { LiveConnect } from 'live-connect-js/esm/initializer.js';
11
11
  import { gdprDataHandler, uspDataHandler } from '../src/adapterManager.js';
12
12
  import { getStorageManager } from '../src/storageManager.js';
13
- import { MinimalLiveConnect } from 'live-connect-js/esm/minimal-live-connect.js';
14
13
 
15
14
  const MODULE_NAME = 'liveIntentId';
16
15
  export const storage = getStorageManager({gvlid: null, moduleName: MODULE_NAME});
@@ -140,7 +139,7 @@ export const liveIntentIdSubmodule = {
140
139
  this.moduleMode = mode
141
140
  },
142
141
  getInitializer() {
143
- return this.moduleMode === 'minimal' ? MinimalLiveConnect : LiveConnect
142
+ return (liveConnectConfig, storage, calls) => LiveConnect(liveConnectConfig, storage, calls, this.moduleMode)
144
143
  },
145
144
 
146
145
  /**
@@ -297,7 +297,8 @@ function getParam(validBidRequests, bidderRequest) {
297
297
  utils.deepAccess(validBidRequests[0], 'userId.sharedid.id') ||
298
298
  utils.deepAccess(validBidRequests[0], 'userId.pubcid');
299
299
  let isMobile = isMobileAndTablet() ? 1 : 0;
300
- let isTest = 0;
300
+ // input test status by Publisher. more frequently for test true req
301
+ let isTest = validBidRequests[0].params.test || 0;
301
302
  let auctionId = getProperty(bidderRequest, 'auctionId');
302
303
  let items = getItems(validBidRequests, bidderRequest);
303
304