prebid.js 9.40.0 → 9.41.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.
- package/dist/33acrossAnalyticsAdapter.js +1 -1
- package/dist/33acrossBidAdapter.js +1 -1
- package/dist/33acrossIdSystem.js +1 -1
- package/dist/BTBidAdapter.js +1 -1
- package/dist/adagioAnalyticsAdapter.js +1 -1
- package/dist/adagioBidAdapter.js +1 -1
- package/dist/adagioUtils.js +1 -1
- package/dist/addefendBidAdapter.js +1 -1
- package/dist/adgenerationBidAdapter.js +1 -1
- package/dist/adlooxRtdProvider.js +1 -1
- package/dist/adqueryBidAdapter.js +1 -1
- package/dist/adrelevantisBidAdapter.js +1 -1
- package/dist/adstirBidAdapter.js +1 -1
- package/dist/adtrgtmeBidAdapter.js +1 -1
- package/dist/adxcgAnalyticsAdapter.js +1 -1
- package/dist/adxcgBidAdapter.js +1 -1
- package/dist/adyoulikeBidAdapter.js +1 -1
- package/dist/agmaAnalyticsAdapter.js +1 -1
- package/dist/ajaBidAdapter.js +1 -1
- package/dist/amxBidAdapter.js +1 -1
- package/dist/amxIdSystem.js +1 -1
- package/dist/aniviewBidAdapter.js +1 -1
- package/dist/appierAnalyticsAdapter.js +1 -1
- package/dist/appnexusBidAdapter.js +1 -1
- package/dist/asoBidAdapter.js +1 -1
- package/dist/axonixBidAdapter.js +1 -1
- package/dist/beopBidAdapter.js +1 -1
- package/dist/bidglassBidAdapter.js +1 -1
- package/dist/big-richmediaBidAdapter.js +1 -1
- package/dist/bitmediaBidAdapter.js +1 -1
- package/dist/braveUtils.js +1 -1
- package/dist/bridBidAdapter.js +1 -1
- package/dist/bridgeuppBidAdapter.js +1 -1
- package/dist/bridgewellBidAdapter.js +1 -1
- package/dist/brightMountainMediaBidAdapter.js +1 -1
- package/dist/carodaBidAdapter.js +1 -1
- package/dist/chtnwBidAdapter.js +1 -1
- package/dist/chunk-core.js +1 -1
- package/dist/concertBidAdapter.js +1 -1
- package/dist/connectadBidAdapter.js +1 -1
- package/dist/consumableBidAdapter.js +1 -1
- package/dist/contxtfulBidAdapter.js +1 -1
- package/dist/conversantAnalyticsAdapter.js +1 -1
- package/dist/conversantBidAdapter.js +1 -1
- package/dist/craftBidAdapter.js +1 -1
- package/dist/criteoBidAdapter.js +1 -1
- package/dist/cwireBidAdapter.js +1 -1
- package/dist/dailymotionBidAdapter.js +1 -1
- package/dist/dependencies.json +4 -0
- package/dist/dspxBidAdapter.js +1 -1
- package/dist/dxkultureBidAdapter.js +1 -1
- package/dist/eplanningBidAdapter.js +1 -1
- package/dist/epomDspBidAdapter.js +1 -0
- package/dist/equativBidAdapter.js +1 -1
- package/dist/eskimiBidAdapter.js +1 -1
- package/dist/euidIdSystem.js +1 -1
- package/dist/exadsBidAdapter.js +1 -1
- package/dist/excoBidAdapter.js +1 -1
- package/dist/feedadBidAdapter.js +1 -1
- package/dist/finativeBidAdapter.js +1 -1
- package/dist/freewheel-sspBidAdapter.js +1 -1
- package/dist/gmosspBidAdapter.js +1 -1
- package/dist/goldbachBidAdapter.js +1 -1
- package/dist/greenbidsAnalyticsAdapter.js +1 -1
- package/dist/greenbidsBidAdapter.js +1 -1
- package/dist/greenbidsRtdProvider.js +1 -1
- package/dist/gridBidAdapter.js +1 -1
- package/dist/gumgumBidAdapter.js +1 -1
- package/dist/h12mediaBidAdapter.js +1 -1
- package/dist/hypelabBidAdapter.js +1 -1
- package/dist/id5AnalyticsAdapter.js +1 -1
- package/dist/id5IdSystem.js +1 -1
- package/dist/imdsBidAdapter.js +1 -1
- package/dist/improvedigitalBidAdapter.js +1 -1
- package/dist/inmobiBidAdapter.js +1 -1
- package/dist/insticatorBidAdapter.js +1 -1
- package/dist/intentIqAnalyticsAdapter.js +1 -1
- package/dist/ixBidAdapter.js +1 -1
- package/dist/jixieBidAdapter.js +1 -1
- package/dist/justpremiumBidAdapter.js +1 -1
- package/dist/kargoBidAdapter.js +1 -1
- package/dist/kimberliteBidAdapter.js +1 -1
- package/dist/konduitAnalyticsAdapter.js +1 -1
- package/dist/kueezBidAdapter.js +1 -1
- package/dist/lassoBidAdapter.js +1 -1
- package/dist/lifestreetBidAdapter.js +1 -1
- package/dist/liveIntentId.js +1 -1
- package/dist/logicadBidAdapter.js +1 -1
- package/dist/loglyliftBidAdapter.js +1 -1
- package/dist/luceadBidAdapter.js +1 -1
- package/dist/mabidderBidAdapter.js +1 -1
- package/dist/madsenseBidAdapter.js +1 -1
- package/dist/magniteAnalyticsAdapter.js +1 -1
- package/dist/malltvAnalyticsAdapter.js +1 -1
- package/dist/marsmediaBidAdapter.js +1 -1
- package/dist/mediafuseBidAdapter.js +1 -1
- package/dist/medianetBidAdapter.js +1 -1
- package/dist/medianetUtils.js +1 -1
- package/dist/mediasquareBidAdapter.js +1 -1
- package/dist/mgidBidAdapter.js +1 -1
- package/dist/missenaBidAdapter.js +1 -1
- package/dist/nextMillenniumBidAdapter.js +1 -1
- package/dist/nexx360BidAdapter.js +1 -1
- package/dist/nobidAnalyticsAdapter.js +1 -1
- package/dist/nobidBidAdapter.js +1 -1
- package/dist/nodalsAiRtdProvider.js +1 -1
- package/dist/not-for-prod/prebid.js +170 -168
- package/dist/oguryBidAdapter.js +1 -1
- package/dist/onetagBidAdapter.js +1 -1
- package/dist/ooloAnalyticsAdapter.js +1 -1
- package/dist/openxBidAdapter.js +1 -1
- package/dist/optidigitalBidAdapter.js +1 -1
- package/dist/orbidderBidAdapter.js +1 -1
- package/dist/outbrainBidAdapter.js +1 -1
- package/dist/pixfutureBidAdapter.js +1 -1
- package/dist/publinkIdSystem.js +1 -1
- package/dist/pubmaticAnalyticsAdapter.js +1 -1
- package/dist/pubmaticBidAdapter.js +1 -1
- package/dist/pubwiseAnalyticsAdapter.js +1 -1
- package/dist/pubxaiAnalyticsAdapter.js +1 -1
- package/dist/pxyzBidAdapter.js +1 -1
- package/dist/quantcastBidAdapter.js +1 -1
- package/dist/raveltechRtdProvider.js +1 -0
- package/dist/readpeakBidAdapter.js +1 -1
- package/dist/relaidoBidAdapter.js +1 -1
- package/dist/retailspotBidAdapter.js +1 -1
- package/dist/rhythmoneBidAdapter.js +1 -1
- package/dist/riseUtils.js +1 -1
- package/dist/rubiconBidAdapter.js +1 -1
- package/dist/seedingAllianceBidAdapter.js +1 -1
- package/dist/seedtagBidAdapter.js +1 -1
- package/dist/sharethroughAnalyticsAdapter.js +1 -1
- package/dist/sharethroughBidAdapter.js +1 -1
- package/dist/showheroes-bsBidAdapter.js +1 -1
- package/dist/smaatoBidAdapter.js +1 -1
- package/dist/smartadserverBidAdapter.js +1 -1
- package/dist/smartxBidAdapter.js +1 -1
- package/dist/smilewantedBidAdapter.js +1 -1
- package/dist/snigelBidAdapter.js +1 -1
- package/dist/sonobiBidAdapter.js +1 -1
- package/dist/sovrnBidAdapter.js +1 -1
- package/dist/sparteoBidAdapter.js +1 -1
- package/dist/sspBCBidAdapter.js +1 -1
- package/dist/stvBidAdapter.js +1 -1
- package/dist/sublimeBidAdapter.js +1 -1
- package/dist/taboolaBidAdapter.js +1 -1
- package/dist/tappxBidAdapter.js +1 -1
- package/dist/targetVideoBidAdapter.js +1 -1
- package/dist/teadsBidAdapter.js +1 -1
- package/dist/terceptAnalyticsAdapter.js +1 -1
- package/dist/themoneytizerBidAdapter.js +1 -1
- package/dist/trionBidAdapter.js +1 -1
- package/dist/tripleliftBidAdapter.js +1 -1
- package/dist/ttdBidAdapter.js +1 -1
- package/dist/ucfunnelAnalyticsAdapter.js +1 -1
- package/dist/uid2IdSystem.js +1 -1
- package/dist/underdogmediaBidAdapter.js +1 -1
- package/dist/undertoneBidAdapter.js +1 -1
- package/dist/unrulyBidAdapter.js +1 -1
- package/dist/vidazooUtils.js +1 -1
- package/dist/videobyteBidAdapter.js +1 -1
- package/dist/visxBidAdapter.js +1 -1
- package/dist/vuukleBidAdapter.js +1 -1
- package/dist/widespaceBidAdapter.js +1 -1
- package/dist/winrBidAdapter.js +1 -1
- package/dist/yahooAdsBidAdapter.js +1 -1
- package/dist/yandexBidAdapter.js +1 -1
- package/dist/yieldmoBidAdapter.js +1 -1
- package/dist/yieldoneAnalyticsAdapter.js +1 -1
- package/dist/yieldoneBidAdapter.js +1 -1
- package/gulpfile.js +2 -1
- package/integrationExamples/gpt/raveltechRtdProvider_example.html +379 -0
- package/libraries/braveUtils/index.js +1 -1
- package/libraries/riseUtils/index.js +1 -1
- package/modules/epomDspBidAdapter.js +177 -0
- package/modules/epomDspBidAdapter.md +170 -0
- package/modules/equativBidAdapter.js +14 -3
- package/modules/goldbachBidAdapter.js +160 -300
- package/modules/raveltechRtdProvider.js +131 -0
- package/modules/raveltechRtdProvider.md +61 -0
- package/modules/seedtagBidAdapter.js +1 -0
- package/modules/yandexBidAdapter.js +4 -0
- package/modules/yieldoneBidAdapter.js +6 -0
- package/package.json +2 -2
- package/test/spec/modules/epomDspBidAdapter_spec.js +73 -0
- package/test/spec/modules/equativBidAdapter_spec.js +19 -1
- package/test/spec/modules/goldbachBidAdapter_spec.js +227 -499
- package/test/spec/modules/raveltechRtdProvider_spec.js +108 -0
- package/test/spec/modules/seedtagBidAdapter_spec.js +56 -2
- package/test/spec/modules/yandexBidAdapter_spec.js +6 -0
- package/test/spec/modules/yieldoneBidAdapter_spec.js +40 -0
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
import { ajax } from '../src/ajax.js';
|
|
2
2
|
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes.js';
|
|
3
|
+
import { deepAccess, generateUUID } from '../src/utils.js';
|
|
3
4
|
import { registerBidder } from '../src/adapters/bidderFactory.js';
|
|
5
|
+
import { ortbConverter } from '../libraries/ortbConverter/converter.js';
|
|
4
6
|
import { Renderer } from '../src/Renderer.js';
|
|
5
|
-
import {
|
|
7
|
+
import { hasPurpose1Consent } from '../src/utils/gdpr.js';
|
|
8
|
+
import { getStorageManager } from '../src/storageManager.js';
|
|
6
9
|
|
|
7
10
|
/* General config */
|
|
8
11
|
const IS_LOCAL_MODE = false;
|
|
9
12
|
const BIDDER_CODE = 'goldbach';
|
|
13
|
+
const BIDDER_UID_KEY = 'goldbach_uid';
|
|
10
14
|
const GVLID = 580;
|
|
11
|
-
const URL = 'https://goldlayer-api.prod.gbads.net/
|
|
12
|
-
const URL_LOCAL = 'http://localhost:3000/
|
|
13
|
-
const
|
|
15
|
+
const URL = 'https://goldlayer-api.prod.gbads.net/openrtb/2.5/auction';
|
|
16
|
+
const URL_LOCAL = 'http://localhost:3000/openrtb/2.5/auction';
|
|
17
|
+
const URL_LOGGING = 'https://l.da-services.ch/pb';
|
|
18
|
+
const URL_COOKIESYNC = 'https://goldlayer-api.prod.gbads.net/cookiesync';
|
|
19
|
+
const METHOD = 'POST';
|
|
20
|
+
const DEFAULT_CURRENCY = 'USD';
|
|
21
|
+
const LOGGING_PERCENTAGE_REGULAR = 0.001;
|
|
14
22
|
const LOGGING_PERCENTAGE_ERROR = 0.001;
|
|
15
|
-
const
|
|
23
|
+
const COOKIE_EXP = 1000 * 60 * 60 * 24 * 365;
|
|
16
24
|
|
|
17
25
|
/* Renderer settings */
|
|
18
26
|
const RENDERER_OPTIONS = {
|
|
19
27
|
OUTSTREAM_GP: {
|
|
20
|
-
MIN_HEIGHT: 300,
|
|
21
|
-
MIN_WIDTH: 300,
|
|
22
28
|
URL: 'https://goldplayer.prod.gbads.net/scripts/goldplayer.js'
|
|
23
29
|
}
|
|
24
30
|
};
|
|
@@ -32,220 +38,69 @@ const EVENTS = {
|
|
|
32
38
|
ERROR: 'error'
|
|
33
39
|
};
|
|
34
40
|
|
|
35
|
-
/*
|
|
36
|
-
const
|
|
37
|
-
// request level
|
|
38
|
-
GEO_LAT: 'lat',
|
|
39
|
-
GEO_LON: 'long',
|
|
40
|
-
GEO_ZIP: 'zip',
|
|
41
|
-
CONNECTION_TYPE: 'connection',
|
|
42
|
-
// slot level
|
|
43
|
-
VIDEO_DURATION: 'duration',
|
|
44
|
-
};
|
|
41
|
+
/* Goldbach storage */
|
|
42
|
+
export const storage = getStorageManager({ bidderCode: BIDDER_CODE });
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
},
|
|
53
|
-
ASSET_ID: {
|
|
54
|
-
TITLE: 1,
|
|
55
|
-
IMAGE: 2,
|
|
56
|
-
ICON: 3,
|
|
57
|
-
BODY: 4,
|
|
58
|
-
CTA: 5,
|
|
59
|
-
SPONSORED: 6,
|
|
60
|
-
}
|
|
44
|
+
const setUid = (uid) => {
|
|
45
|
+
if (storage.localStorageIsEnabled()) {
|
|
46
|
+
storage.setDataInLocalStorage(BIDDER_UID_KEY, uid);
|
|
47
|
+
} else if (storage.cookiesAreEnabled()) {
|
|
48
|
+
const cookieExpiration = new Date(Date.now() + COOKIE_EXP).toISOString();
|
|
49
|
+
storage.setCookie(BIDDER_UID_KEY, uid, cookieExpiration, 'None');
|
|
61
50
|
}
|
|
62
51
|
};
|
|
63
52
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (bidderRequest?.ortb2?.device?.geo) {
|
|
70
|
-
if (bidderRequest?.ortb2?.device?.geo?.lon) {
|
|
71
|
-
customTargeting[TARGETING_KEYS.GEO_LON] = bidderRequest.ortb2.device.geo.lon;
|
|
72
|
-
}
|
|
73
|
-
if (bidderRequest?.ortb2?.device?.geo?.lat) {
|
|
74
|
-
customTargeting[TARGETING_KEYS.GEO_LAT] = bidderRequest.ortb2.device.geo.lat;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// connection
|
|
79
|
-
if (bidderRequest?.ortb2?.device?.connectiontype) {
|
|
80
|
-
switch (bidderRequest.ortb2.device.connectiontype) {
|
|
81
|
-
case 1:
|
|
82
|
-
customTargeting[TARGETING_KEYS.CONNECTION_TYPE] = 'ethernet';
|
|
83
|
-
break;
|
|
84
|
-
case 2:
|
|
85
|
-
customTargeting[TARGETING_KEYS.CONNECTION_TYPE] = 'wifi';
|
|
86
|
-
break;
|
|
87
|
-
case 4:
|
|
88
|
-
customTargeting[TARGETING_KEYS.CONNECTION_TYPE] = '2G';
|
|
89
|
-
break;
|
|
90
|
-
case 5:
|
|
91
|
-
customTargeting[TARGETING_KEYS.CONNECTION_TYPE] = '3G';
|
|
92
|
-
break;
|
|
93
|
-
case 6:
|
|
94
|
-
customTargeting[TARGETING_KEYS.CONNECTION_TYPE] = '4G';
|
|
95
|
-
break;
|
|
96
|
-
case 0:
|
|
97
|
-
case 3:
|
|
98
|
-
default:
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// zip
|
|
104
|
-
if (bidderRequest?.ortb2?.device?.geo?.zip) {
|
|
105
|
-
customTargeting[TARGETING_KEYS.GEO_ZIP] = bidderRequest.ortb2.device.geo.zip;
|
|
53
|
+
const getUid = () => {
|
|
54
|
+
if (storage.localStorageIsEnabled()) {
|
|
55
|
+
return storage.getDataFromLocalStorage(BIDDER_UID_KEY);
|
|
56
|
+
} else if (storage.cookiesAreEnabled()) {
|
|
57
|
+
return storage.getCookie(BIDDER_UID_KEY);
|
|
106
58
|
}
|
|
59
|
+
return null;
|
|
60
|
+
};
|
|
107
61
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (duration > 15 && duration <= 30) customTargeting[TARGETING_KEYS.VIDEO_DURATION] = 'XL';
|
|
120
|
-
if (duration > 30) customTargeting[TARGETING_KEYS.VIDEO_DURATION] = 'XXL';
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return customTargeting
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const convertToProprietaryData = (validBidRequests, bidderRequest) => {
|
|
128
|
-
const requestData = {
|
|
129
|
-
mock: false,
|
|
130
|
-
debug: false,
|
|
131
|
-
timestampStart: undefined,
|
|
132
|
-
timestampEnd: undefined,
|
|
133
|
-
config: {
|
|
134
|
-
publisher: {
|
|
135
|
-
id: undefined,
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
gdpr: {
|
|
139
|
-
consent: undefined,
|
|
140
|
-
consentString: undefined,
|
|
141
|
-
},
|
|
142
|
-
contextInfo: {
|
|
143
|
-
contentUrl: undefined,
|
|
144
|
-
bidderResources: undefined,
|
|
145
|
-
},
|
|
146
|
-
appInfo: {
|
|
147
|
-
id: undefined,
|
|
148
|
-
},
|
|
149
|
-
userInfo: {
|
|
150
|
-
ip: undefined,
|
|
151
|
-
ua: undefined,
|
|
152
|
-
ifa: undefined,
|
|
153
|
-
ppid: [],
|
|
154
|
-
},
|
|
155
|
-
slots: [],
|
|
156
|
-
targetings: {},
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
// Set timestamps
|
|
160
|
-
requestData.timestampStart = Date.now();
|
|
161
|
-
requestData.timestampEnd = Date.now() + (!isNaN(bidderRequest.timeout) ? Number(bidderRequest.timeout) : 0);
|
|
162
|
-
|
|
163
|
-
// Set config
|
|
164
|
-
if (validBidRequests[0]?.params?.publisherId) {
|
|
165
|
-
requestData.config.publisher.id = validBidRequests[0].params.publisherId;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Set GDPR
|
|
169
|
-
if (bidderRequest?.gdprConsent) {
|
|
170
|
-
requestData.gdpr.consent = bidderRequest.gdprConsent.gdprApplies;
|
|
171
|
-
requestData.gdpr.consentString = bidderRequest.gdprConsent.consentString;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Set contextInfo
|
|
175
|
-
requestData.contextInfo.contentUrl = bidderRequest.refererInfo?.canonicalUrl || bidderRequest.refererInfo?.topmostLocation || bidderRequest?.ortb2?.site?.page;
|
|
176
|
-
|
|
177
|
-
// Set appInfo
|
|
178
|
-
requestData.appInfo.id = bidderRequest?.ortb2?.site?.domain || bidderRequest.refererInfo?.page;
|
|
179
|
-
|
|
180
|
-
// Set userInfo
|
|
181
|
-
requestData.userInfo.ip = bidderRequest?.ortb2?.device?.ip || navigator.ip;
|
|
182
|
-
requestData.userInfo.ua = bidderRequest?.ortb2?.device?.ua || navigator.userAgent;
|
|
183
|
-
|
|
184
|
-
// Set userInfo.ppid
|
|
185
|
-
requestData.userInfo.ppid = (validBidRequests || []).reduce((ppids, validBidRequest) => {
|
|
186
|
-
const extractedPpids = [];
|
|
187
|
-
(validBidRequest.userIdAsEids || []).forEach((eid) => {
|
|
188
|
-
(eid?.uids || []).forEach(uid => {
|
|
189
|
-
if (uid?.ext?.stype === 'ppuid') {
|
|
190
|
-
const isExistingInExtracted = !!extractedPpids.find(id => id.source === eid.source);
|
|
191
|
-
const isExistingInPpids = !!ppids.find(id => id.source === eid.source);
|
|
192
|
-
if (!isExistingInExtracted && !isExistingInPpids) extractedPpids.push({source: eid.source, id: uid.id});
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
})
|
|
196
|
-
return [...ppids, ...extractedPpids];
|
|
197
|
-
}, []);
|
|
198
|
-
|
|
199
|
-
// Set userInfo.ifa
|
|
200
|
-
if (bidderRequest.ortb2?.device?.ifa) {
|
|
201
|
-
requestData.userInfo.ifa = bidderRequest.ortb2.device.ifa;
|
|
202
|
-
} else {
|
|
203
|
-
requestData.userInfo.ifa = validBidRequests.find(validBidRequest => {
|
|
204
|
-
return !!validBidRequest.ortb2?.device?.ifa;
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Set slots
|
|
209
|
-
requestData.slots = validBidRequests.map((validBidRequest) => {
|
|
210
|
-
const slot = {
|
|
211
|
-
id: validBidRequest.params?.slotId,
|
|
212
|
-
sizes: [
|
|
213
|
-
...(validBidRequest.sizes || []),
|
|
214
|
-
...(validBidRequest.mediaTypes?.[VIDEO]?.sizes ? validBidRequest.mediaTypes[VIDEO].sizes : [])
|
|
215
|
-
],
|
|
216
|
-
targetings: {
|
|
217
|
-
...validBidRequest?.params?.customTargeting,
|
|
218
|
-
...convertToCustomSlotTargeting(validBidRequest)
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
return slot;
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
// Set targetings
|
|
225
|
-
requestData.targetings = convertToCustomTargeting(bidderRequest);
|
|
226
|
-
|
|
227
|
-
return requestData;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const getRendererForBid = (bidRequest, creative) => {
|
|
231
|
-
if (!bidRequest.renderer && creative.contextType === 'video_outstream') {
|
|
232
|
-
if (!creative.vastUrl && !creative.vastXml) return undefined;
|
|
62
|
+
const ensureUid = (gdprConsent) => {
|
|
63
|
+
// Check if the user has given consent for purpose 1
|
|
64
|
+
if (!gdprConsent || !hasPurpose1Consent(gdprConsent)) return null;
|
|
65
|
+
// Check if the UID already exists
|
|
66
|
+
const existingUid = getUid();
|
|
67
|
+
if (existingUid) return existingUid;
|
|
68
|
+
// Generate a new UID if it doesn't exist
|
|
69
|
+
const uid = generateUUID();
|
|
70
|
+
setUid(uid);
|
|
71
|
+
return uid;
|
|
72
|
+
};
|
|
233
73
|
|
|
74
|
+
/* Custom extensions */
|
|
75
|
+
const getRendererForBid = (bidRequest, bidResponse) => {
|
|
76
|
+
if (!bidRequest.renderer) {
|
|
234
77
|
const config = { documentResolver: (_, sourceDocument, renderDocument) => renderDocument ?? sourceDocument };
|
|
235
|
-
const renderer = Renderer.install({
|
|
78
|
+
const renderer = Renderer.install({
|
|
79
|
+
id: bidRequest.bidId,
|
|
80
|
+
url: RENDERER_OPTIONS.OUTSTREAM_GP.URL,
|
|
81
|
+
adUnitCode: bidRequest.adUnitCode,
|
|
82
|
+
config
|
|
83
|
+
});
|
|
236
84
|
|
|
237
85
|
renderer.setRender((bid, doc) => {
|
|
86
|
+
const videoParams = bidRequest?.mediaTypes?.video || {};
|
|
87
|
+
const playerSize = videoParams.playerSize;
|
|
88
|
+
const playbackmethod = videoParams.playbackmethod;
|
|
89
|
+
const isMuted = typeof playbackmethod === 'number' ? [2, 6].includes(playbackmethod) : false;
|
|
90
|
+
const isAutoplay = typeof playbackmethod === 'number' ? [1, 2].includes(playbackmethod) : false;
|
|
91
|
+
|
|
238
92
|
bid.renderer.push(() => {
|
|
239
93
|
if (doc.defaultView?.GoldPlayer) {
|
|
240
94
|
const options = {
|
|
241
|
-
vastUrl:
|
|
242
|
-
vastXML:
|
|
243
|
-
autoplay:
|
|
244
|
-
muted:
|
|
95
|
+
vastUrl: bid.vastUrl,
|
|
96
|
+
vastXML: bid.vastXml,
|
|
97
|
+
autoplay: isAutoplay,
|
|
98
|
+
muted: isMuted,
|
|
245
99
|
controls: true,
|
|
100
|
+
resizeMode: 'auto',
|
|
246
101
|
styling: { progressbarColor: '#000' },
|
|
247
|
-
|
|
248
|
-
|
|
102
|
+
publisherProvidedWidth: playerSize?.[0],
|
|
103
|
+
publisherProvidedHeight: playerSize?.[1],
|
|
249
104
|
};
|
|
250
105
|
const GP = doc.defaultView.GoldPlayer;
|
|
251
106
|
const player = new GP(options);
|
|
@@ -253,98 +108,86 @@ const getRendererForBid = (bidRequest, creative) => {
|
|
|
253
108
|
}
|
|
254
109
|
});
|
|
255
110
|
});
|
|
256
|
-
|
|
257
111
|
return renderer;
|
|
258
112
|
}
|
|
259
113
|
return undefined;
|
|
260
|
-
}
|
|
114
|
+
};
|
|
261
115
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
impressionTrackers: nativeAssets?.imptrackers,
|
|
268
|
-
clickTrackers: nativeAssets?.clicktrackers,
|
|
269
|
-
javascriptTrackers: nativeAssets?.jstracker && [nativeAssets.jstracker],
|
|
270
|
-
};
|
|
271
|
-
(nativeAssets?.assets || []).forEach(asset => {
|
|
272
|
-
switch (asset.id) {
|
|
273
|
-
case OPENRTB.NATIVE.ASSET_ID.TITLE:
|
|
274
|
-
result.title = asset.title?.text;
|
|
275
|
-
break;
|
|
276
|
-
case OPENRTB.NATIVE.ASSET_ID.IMAGE:
|
|
277
|
-
result.image = {
|
|
278
|
-
url: encodeURI(asset.img?.url),
|
|
279
|
-
width: asset.img?.w,
|
|
280
|
-
height: asset.img?.h
|
|
281
|
-
};
|
|
282
|
-
break;
|
|
283
|
-
case OPENRTB.NATIVE.ASSET_ID.ICON:
|
|
284
|
-
result.icon = {
|
|
285
|
-
url: encodeURI(asset.img.url),
|
|
286
|
-
width: asset.img?.w,
|
|
287
|
-
height: asset.img?.h
|
|
288
|
-
};
|
|
289
|
-
break;
|
|
290
|
-
case OPENRTB.NATIVE.ASSET_ID.BODY:
|
|
291
|
-
result.body = asset.data?.value;
|
|
292
|
-
break;
|
|
293
|
-
case OPENRTB.NATIVE.ASSET_ID.SPONSORED:
|
|
294
|
-
result.sponsoredBy = asset.data?.value;
|
|
295
|
-
break;
|
|
296
|
-
case OPENRTB.NATIVE.ASSET_ID.CTA:
|
|
297
|
-
result.cta = asset.data?.value;
|
|
298
|
-
break;
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
return result;
|
|
302
|
-
}
|
|
303
|
-
return undefined;
|
|
304
|
-
}
|
|
116
|
+
/* Converter config, applying custom extensions */
|
|
117
|
+
const converter = ortbConverter({
|
|
118
|
+
context: { netRevenue: true, ttl: 3600 },
|
|
119
|
+
imp(buildImp, bidRequest, context) {
|
|
120
|
+
const imp = buildImp(bidRequest, context);
|
|
305
121
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
122
|
+
// Apply custom extensions to the imp
|
|
123
|
+
imp.ext = imp.ext || {};
|
|
124
|
+
imp.ext[BIDDER_CODE] = imp.ext[BIDDER_CODE] || {};
|
|
125
|
+
imp.ext[BIDDER_CODE].targetings = bidRequest?.params?.customTargeting || {};
|
|
126
|
+
imp.ext[BIDDER_CODE].slotId = bidRequest?.params?.slotId || bidRequest?.adUnitCode;
|
|
309
127
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
128
|
+
return imp;
|
|
129
|
+
},
|
|
130
|
+
request(buildRequest, imps, bidderRequest, context) {
|
|
131
|
+
const ortbRequest = buildRequest(imps, bidderRequest, context);
|
|
132
|
+
const { bidRequests = [] } = context;
|
|
133
|
+
const firstBidRequest = bidRequests?.[0];
|
|
134
|
+
|
|
135
|
+
// Read gdpr consent data
|
|
136
|
+
const gdprConsent = bidderRequest?.gdprConsent;
|
|
137
|
+
|
|
138
|
+
// Apply custom extensions to the request
|
|
139
|
+
if (bidRequests.length > 0) {
|
|
140
|
+
ortbRequest.ext = ortbRequest.ext || {};
|
|
141
|
+
ortbRequest.ext[BIDDER_CODE] = ortbRequest.ext[BIDDER_CODE] || {};
|
|
142
|
+
ortbRequest.ext[BIDDER_CODE].uid = ensureUid(gdprConsent);
|
|
143
|
+
ortbRequest.ext[BIDDER_CODE].publisherId = firstBidRequest?.params?.publisherId;
|
|
144
|
+
ortbRequest.ext[BIDDER_CODE].mockResponse = firstBidRequest?.params?.mockResponse || false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Apply gdpr consent data
|
|
148
|
+
if (bidderRequest?.gdprConsent) {
|
|
149
|
+
ortbRequest.regs = ortbRequest.regs || {};
|
|
150
|
+
ortbRequest.regs.ext = ortbRequest.regs.ext || {};
|
|
151
|
+
ortbRequest.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0;
|
|
152
|
+
ortbRequest.user = ortbRequest.user || {};
|
|
153
|
+
ortbRequest.user.ext = ortbRequest.user.ext || {};
|
|
154
|
+
ortbRequest.user.ext.consent = bidderRequest.gdprConsent.consentString;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return ortbRequest;
|
|
158
|
+
},
|
|
159
|
+
bidResponse(buildBidResponse, bid, context) {
|
|
160
|
+
// Setting context: media type
|
|
161
|
+
context.mediaType = deepAccess(bid, 'ext.prebid.type');
|
|
162
|
+
const bidResponse = buildBidResponse(bid, context);
|
|
163
|
+
const { bidRequest } = context;
|
|
164
|
+
|
|
165
|
+
// Setting required properties: cpm, currency
|
|
166
|
+
bidResponse.currency = bidResponse.currency || deepAccess(bid, 'ext.origbidcur') || DEFAULT_CURRENCY;
|
|
167
|
+
bidResponse.cpm = bidResponse.cpm || deepAccess(bid, 'price');
|
|
168
|
+
|
|
169
|
+
// Setting required properties: meta
|
|
170
|
+
bidResponse.meta = bidResponse.meta || {};
|
|
171
|
+
bidResponse.meta.advertiserDomains = deepAccess(bid, 'adomain');
|
|
172
|
+
bidResponse.meta.mediaType = deepAccess(bid, 'ext.prebid.type');
|
|
173
|
+
bidResponse.meta.primaryCatId = deepAccess(bid, 'ext.prebid.video.primary_category');
|
|
174
|
+
bidResponse.meta.secondaryCatIds = deepAccess(bid, 'ext.prebid.video.secondary_categories');
|
|
175
|
+
|
|
176
|
+
// Setting extensions: outstream video renderer
|
|
177
|
+
if (bidResponse.mediaType === VIDEO && bidRequest.mediaTypes.video.context === 'outstream' && (bidResponse.vastUrl || bidResponse.vastXml)) {
|
|
178
|
+
bidResponse.renderer = getRendererForBid(bidRequest, bidResponse);
|
|
179
|
+
}
|
|
180
|
+
return bidResponse;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
340
183
|
|
|
341
184
|
/* Logging */
|
|
342
185
|
const sendLog = (data, percentage = 0.0001) => {
|
|
343
186
|
if (Math.random() > percentage) return;
|
|
344
187
|
const encodedData = `data=${window.btoa(JSON.stringify({...data, source: 'goldbach_pbjs', projectedAmount: (1 / percentage)}))}`;
|
|
345
|
-
ajax(
|
|
188
|
+
ajax(URL_LOGGING, null, encodedData, {
|
|
346
189
|
withCredentials: false,
|
|
347
|
-
method:
|
|
190
|
+
method: METHOD,
|
|
348
191
|
crossOrigin: true,
|
|
349
192
|
contentType: 'application/x-www-form-urlencoded',
|
|
350
193
|
});
|
|
@@ -355,24 +198,38 @@ export const spec = {
|
|
|
355
198
|
gvlid: GVLID,
|
|
356
199
|
supportedMediaTypes: [BANNER, VIDEO, NATIVE],
|
|
357
200
|
isBidRequestValid: function (bid) {
|
|
358
|
-
return typeof bid.params
|
|
201
|
+
return typeof bid.params?.publisherId === 'string' && bid.params?.publisherId.length > 0;
|
|
359
202
|
},
|
|
360
|
-
buildRequests: function (
|
|
203
|
+
buildRequests: function (bidRequests, bidderRequest) {
|
|
361
204
|
const url = IS_LOCAL_MODE ? URL_LOCAL : URL;
|
|
362
|
-
const data =
|
|
363
|
-
return
|
|
364
|
-
method:
|
|
205
|
+
const data = converter.toORTB({ bidRequests, bidderRequest });
|
|
206
|
+
return {
|
|
207
|
+
method: METHOD,
|
|
365
208
|
url: url,
|
|
366
209
|
data: data,
|
|
367
|
-
bidderRequest: bidderRequest,
|
|
368
210
|
options: {
|
|
369
211
|
withCredentials: false,
|
|
370
212
|
contentType: 'application/json',
|
|
371
213
|
}
|
|
372
|
-
}
|
|
214
|
+
};
|
|
373
215
|
},
|
|
374
|
-
interpretResponse: function (
|
|
375
|
-
|
|
216
|
+
interpretResponse: function (ortbResponse, request) {
|
|
217
|
+
const bids = converter.fromORTB({response: ortbResponse.body, request: request.data}).bids;
|
|
218
|
+
return bids
|
|
219
|
+
},
|
|
220
|
+
getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) {
|
|
221
|
+
const syncs = []
|
|
222
|
+
const uid = ensureUid(gdprConsent);
|
|
223
|
+
if (hasPurpose1Consent(gdprConsent)) {
|
|
224
|
+
let type = (syncOptions.pixelEnabled) ? 'image' : null ?? (syncOptions.iframeEnabled) ? 'iframe' : null
|
|
225
|
+
if (type) {
|
|
226
|
+
syncs.push({
|
|
227
|
+
type: type,
|
|
228
|
+
url: `https://ib.adnxs.com/getuid?${URL_COOKIESYNC}?uid=${uid}&xandrId=$UID`
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return syncs
|
|
376
233
|
},
|
|
377
234
|
onTimeout: function(timeoutData) {
|
|
378
235
|
const payload = {
|
|
@@ -384,8 +241,9 @@ export const spec = {
|
|
|
384
241
|
onBidWon: function(bid) {
|
|
385
242
|
const payload = {
|
|
386
243
|
event: EVENTS.BID_WON,
|
|
244
|
+
publisherId: bid.params?.[0]?.publisherId,
|
|
245
|
+
creativeId: bid.creativeId,
|
|
387
246
|
adUnitCode: bid.adUnitCode,
|
|
388
|
-
adId: bid.adId,
|
|
389
247
|
mediaType: bid.mediaType,
|
|
390
248
|
size: bid.size,
|
|
391
249
|
};
|
|
@@ -393,9 +251,10 @@ export const spec = {
|
|
|
393
251
|
},
|
|
394
252
|
onSetTargeting: function(bid) {
|
|
395
253
|
const payload = {
|
|
396
|
-
event: EVENTS.
|
|
254
|
+
event: EVENTS.BID_WON,
|
|
255
|
+
publisherId: bid.params?.[0]?.publisherId,
|
|
256
|
+
creativeId: bid.creativeId,
|
|
397
257
|
adUnitCode: bid.adUnitCode,
|
|
398
|
-
adId: bid.adId,
|
|
399
258
|
mediaType: bid.mediaType,
|
|
400
259
|
size: bid.size,
|
|
401
260
|
};
|
|
@@ -410,9 +269,10 @@ export const spec = {
|
|
|
410
269
|
},
|
|
411
270
|
onAdRenderSucceeded: function(bid) {
|
|
412
271
|
const payload = {
|
|
413
|
-
event: EVENTS.
|
|
272
|
+
event: EVENTS.BID_WON,
|
|
273
|
+
publisherId: bid.params?.[0]?.publisherId,
|
|
274
|
+
creativeId: bid.creativeId,
|
|
414
275
|
adUnitCode: bid.adUnitCode,
|
|
415
|
-
adId: bid.adId,
|
|
416
276
|
mediaType: bid.mediaType,
|
|
417
277
|
size: bid.size,
|
|
418
278
|
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import {submodule, getHook} from '../src/hook.js';
|
|
2
|
+
import adapterManager from '../src/adapterManager.js';
|
|
3
|
+
import {logInfo, deepClone, isArray, isStr, isPlainObject, logError} from '../src/utils.js';
|
|
4
|
+
|
|
5
|
+
// Constants
|
|
6
|
+
const MODULE_NAME = 'raveltech';
|
|
7
|
+
const RAVEL_ENDPOINT = 'https://pb1.rvlproxy.net/bid/bid';
|
|
8
|
+
|
|
9
|
+
const getAdapterNameForAlias = (aliasName) => adapterManager.aliasRegistry[aliasName] || aliasName;
|
|
10
|
+
|
|
11
|
+
const getAnonymizedEids = (eids) => {
|
|
12
|
+
const ZKAD = window.ZKAD || { anonymizeID(v, p) { return undefined; } };
|
|
13
|
+
logInfo('ZKAD.ready=', ZKAD.ready);
|
|
14
|
+
if (!eids) { return eids; }
|
|
15
|
+
|
|
16
|
+
eids.forEach(eid => {
|
|
17
|
+
if (!eid || !eid.uids || eid.uids.length === 0) { return eid }
|
|
18
|
+
logInfo('eid.source=', eid.source);
|
|
19
|
+
eid.uids = eid.uids.flatMap(uid => {
|
|
20
|
+
if (!uid || !uid.id) { return []; }
|
|
21
|
+
const id = ZKAD.anonymizeID(uid.id, eid.source);
|
|
22
|
+
if (!id) {
|
|
23
|
+
logError('Error while anonymizing uid :', eid, uid);
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
logInfo('Anonymized as byte array of length=', id.length);
|
|
27
|
+
return [ {
|
|
28
|
+
...uid,
|
|
29
|
+
id
|
|
30
|
+
} ];
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
return eids;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const addRavelDataToRequest = (request, adapterName) => {
|
|
38
|
+
if (isStr(request.data)) {
|
|
39
|
+
try {
|
|
40
|
+
const data = JSON.parse(request.data);
|
|
41
|
+
data.ravel = { pbjsAdapter: adapterName };
|
|
42
|
+
request.data = JSON.stringify(data);
|
|
43
|
+
} catch (_e) {}
|
|
44
|
+
} else if (!request.data) {
|
|
45
|
+
request.data = { ravel: { pbjsAdapter: adapterName } };
|
|
46
|
+
} else if (isPlainObject(request.data)) {
|
|
47
|
+
request.data.ravel = { pbjsAdapter: adapterName };
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const wrapBuildRequests = (aliasName, preserveOriginalBid, buildRequests) => {
|
|
52
|
+
const adapterName = getAdapterNameForAlias(aliasName)
|
|
53
|
+
|
|
54
|
+
return (validBidRequests, ...rest) => {
|
|
55
|
+
if (!window.ZKAD || !window.ZKAD.ready) {
|
|
56
|
+
return buildRequests(validBidRequests, ...rest);
|
|
57
|
+
}
|
|
58
|
+
let requests = preserveOriginalBid ? buildRequests(validBidRequests, ...rest) : [];
|
|
59
|
+
if (!isArray(requests)) {
|
|
60
|
+
requests = [ requests ];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const ravelBidRequests = deepClone(validBidRequests);
|
|
65
|
+
|
|
66
|
+
// Anonymize eids for ravel proxified requests
|
|
67
|
+
const anonymizedEids = getAnonymizedEids(ravelBidRequests[0]?.userIdAsEids);
|
|
68
|
+
|
|
69
|
+
ravelBidRequests.forEach(bidRequest => {
|
|
70
|
+
// Replace original eids with anonymized eids
|
|
71
|
+
bidRequest.userIdAsEids = anonymizedEids;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
let ravelRequests = buildRequests(ravelBidRequests, ...rest);
|
|
75
|
+
if (!isArray(ravelRequests) && ravelRequests) {
|
|
76
|
+
ravelRequests = [ ravelRequests ];
|
|
77
|
+
}
|
|
78
|
+
if (ravelRequests) {
|
|
79
|
+
ravelRequests.forEach(request => {
|
|
80
|
+
// Proxyfy request
|
|
81
|
+
request.url = RAVEL_ENDPOINT;
|
|
82
|
+
request.method = 'POST';
|
|
83
|
+
addRavelDataToRequest(request, adapterName);
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return [ ...requests ?? [], ...ravelRequests ?? [] ];
|
|
88
|
+
} catch (e) {
|
|
89
|
+
logError('Error while generating ravel requests :', e);
|
|
90
|
+
return requests;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const getBidderRequestsHook = (config) => {
|
|
96
|
+
const allowedBidders = config.params.bidders || [];
|
|
97
|
+
const preserveOriginalBid = config.params.preserveOriginalBid ?? false;
|
|
98
|
+
const wrappedBidders = [];
|
|
99
|
+
|
|
100
|
+
return (next, spec, ...rest) => {
|
|
101
|
+
if (allowedBidders.includes(spec.code) && !wrappedBidders.includes(spec.code)) {
|
|
102
|
+
spec.buildRequests = wrapBuildRequests(spec.code, preserveOriginalBid, spec.buildRequests);
|
|
103
|
+
|
|
104
|
+
wrappedBidders.push(spec.code);
|
|
105
|
+
}
|
|
106
|
+
next(spec, ...rest);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Init
|
|
112
|
+
* @param {Object} config Module configuration
|
|
113
|
+
* @param {boolean} _userConsent
|
|
114
|
+
* @returns true
|
|
115
|
+
*/
|
|
116
|
+
const init = (config, _userConsent) => {
|
|
117
|
+
const allowedBidders = config.params.bidders || [];
|
|
118
|
+
const preserveOriginalBid = config.params.preserveOriginalBid ?? false;
|
|
119
|
+
|
|
120
|
+
getHook('processBidderRequests').before(getBidderRequestsHook(config));
|
|
121
|
+
logInfo(`Raveltech RTD ready - ${preserveOriginalBid ? 'will' : `won't`} duplicate bid requests - Allowed bidders : `, allowedBidders);
|
|
122
|
+
return true;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const raveltechSubmodule = {
|
|
126
|
+
name: MODULE_NAME,
|
|
127
|
+
init
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Register raveltechSubmodule as submodule of realTimeData
|
|
131
|
+
submodule('realTimeData', raveltechSubmodule);
|