forked from prebid/Prebid.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexpress.js
210 lines (186 loc) · 7.46 KB
/
express.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
import { logMessage, logWarn, logError, logInfo } from '../src/utils.js';
import {getGlobal} from '../src/prebidGlobal.js';
const MODULE_NAME = 'express';
const pbjsInstance = getGlobal();
/**
* Express Module
*
* The express module allows the initiation of Prebid.js auctions automatically based on calls such as gpt.defineSlot.
* It works by monkey-patching the gpt methods and overloading their functionality. In order for this module to be
* used gpt must be included in the page, this module must be included in the Prebid.js bundle, and a call to
* pbjs.express() must be made.
*
* @param {Object[]} [adUnits = pbjs.adUnits] - an array of adUnits for express to operate on.
*/
pbjsInstance.express = function(adUnits = pbjsInstance.adUnits) {
logMessage('loading ' + MODULE_NAME);
if (adUnits.length === 0) {
logWarn('no valid adUnits found, not loading ' + MODULE_NAME);
}
// store gpt slots in a more performant hash lookup by elementId (adUnit code)
var gptSlotCache = {};
// put adUnits in a more performant hash lookup by code.
var adUnitsCache = adUnits.reduce(function (cache, adUnit) {
if (adUnit.code && adUnit.bids) {
cache[adUnit.code] = adUnit;
} else {
logError('misconfigured adUnit', null, adUnit);
}
return cache;
}, {});
window.googletag = window.googletag || {};
window.googletag.cmd = window.googletag.cmd || [];
window.googletag.cmd.push(function () {
// verify all necessary gpt functions exist
var gpt = window.googletag;
var pads = gpt.pubads;
if (!gpt.display || !gpt.enableServices || typeof pads !== 'function' || !pads().refresh || !pads().disableInitialLoad || !pads().getSlots || !pads().enableSingleRequest) {
logError('could not bind to gpt googletag api');
return;
}
logMessage('running');
// function to convert google tag slot sizes to [[w,h],...]
function mapGptSlotSizes(aGPTSlotSizes) {
var aSlotSizes = [];
for (var i = 0; i < aGPTSlotSizes.length; i++) {
try {
aSlotSizes.push([aGPTSlotSizes[i].getWidth(), aGPTSlotSizes[i].getHeight()]);
} catch (e) {
logWarn('slot size ' + aGPTSlotSizes[i].toString() + ' not supported by' + MODULE_NAME);
}
}
return aSlotSizes;
}
// a helper function to verify slots or get slots if not present
function defaultSlots(slots) {
return Array.isArray(slots)
? slots.slice()
// eslint-disable-next-line no-undef
: googletag.pubads().getSlots().slice();
}
// maps gpt slots to adUnits, matches are copied to new array and removed from passed array.
function pickAdUnits(gptSlots) {
var adUnits = [];
// traverse backwards (since gptSlots is mutated) to find adUnits in cache and remove non-mapped slots
for (var i = gptSlots.length - 1; i > -1; i--) {
const gptSlot = gptSlots[i];
const elemId = gptSlot.getSlotElementId();
const adUnit = adUnitsCache[elemId];
if (adUnit) {
gptSlotCache[elemId] = gptSlot; // store by elementId
adUnit.sizes = adUnit.sizes || mapGptSlotSizes(gptSlot.getSizes());
adUnits.push(adUnit);
gptSlots.splice(i, 1);
}
}
return adUnits;
}
// store original gpt functions that will be overridden
var fGptDisplay = gpt.display;
var fGptEnableServices = gpt.enableServices;
var fGptRefresh = pads().refresh;
var fGptDisableInitialLoad = pads().disableInitialLoad;
var fGptEnableSingleRequest = pads().enableSingleRequest;
// override googletag.enableServices()
// - make sure fGptDisableInitialLoad() has been called so we can
// better control when slots are displayed, then call original
// fGptEnableServices()
gpt.enableServices = function () {
if (!bInitialLoadDisabled) {
fGptDisableInitialLoad.apply(pads());
}
return fGptEnableServices.apply(gpt, arguments);
};
// override googletag.display()
// - call the real fGptDisplay(). this won't initiate auctions because we've disabled initial load
// - define all corresponding rubicon slots
// - if disableInitialLoad() has been called by the pub, done
// - else run an auction and call the real fGptRefresh() to
// initiate the DFP request
gpt.display = function (sElementId) {
logInfo('display:', sElementId);
// call original gpt display() function
fGptDisplay.apply(gpt, arguments);
// if not SRA mode, get only the gpt slot corresponding to sEementId
var aGptSlots;
if (!bEnabledSRA) {
// eslint-disable-next-line no-undef
aGptSlots = googletag.pubads().getSlots().filter(function (oGptSlot) {
return oGptSlot.getSlotElementId() === sElementId;
});
}
aGptSlots = defaultSlots(aGptSlots).filter(function (gptSlot) {
return !gptSlot._displayed;
});
aGptSlots.forEach(function (gptSlot) {
gptSlot._displayed = true;
});
var adUnits = pickAdUnits(/* mutated: */ aGptSlots);
if (!bInitialLoadDisabled) {
if (aGptSlots.length) {
fGptRefresh.apply(pads(), [aGptSlots]);
}
if (adUnits.length) {
pbjsInstance.requestBids({
adUnits: adUnits,
bidsBackHandler: function () {
pbjsInstance.setTargetingForGPTAsync();
fGptRefresh.apply(pads(), [
adUnits.map(function (adUnit) {
return gptSlotCache[adUnit.code];
})
]);
}
});
}
}
};
// override gpt refresh() function
// - run auctions for provided gpt slots, then initiate ad-server call
pads().refresh = function (aGptSlots, options) {
logInfo('refresh:', aGptSlots);
// get already displayed adUnits from aGptSlots if provided, else all defined gptSlots
aGptSlots = defaultSlots(aGptSlots);
var adUnits = pickAdUnits(/* mutated: */ aGptSlots).filter(function (adUnit) {
return gptSlotCache[adUnit.code]._displayed;
});
if (aGptSlots.length) {
fGptRefresh.apply(pads(), [aGptSlots, options]);
}
if (adUnits.length) {
pbjsInstance.requestBids({
adUnits: adUnits,
bidsBackHandler: function () {
pbjsInstance.setTargetingForGPTAsync();
fGptRefresh.apply(pads(), [
adUnits.map(function (adUnit) {
return gptSlotCache[adUnit.code];
}),
options
]);
}
});
}
};
// override gpt disableInitialLoad function
// Register that initial load was called, meaning calls to display()
// should not initiate an ad-server request. Instead a call to
// refresh() will be needed to iniate the request.
// We will assume the pub is using this the correct way, calling it
// before enableServices()
var bInitialLoadDisabled = false;
pads().disableInitialLoad = function () {
bInitialLoadDisabled = true;
return fGptDisableInitialLoad.apply(window.googletag.pubads(), arguments);
};
// override gpt useSingleRequest function
// Register that SRA has been turned on
// We will assume the pub is using this the correct way, calling it
// before enableServices()
var bEnabledSRA = false;
pads().enableSingleRequest = function () {
bEnabledSRA = true;
return fGptEnableSingleRequest.apply(window.googletag.pubads(), arguments);
};
});
};