prebid-universal-creative 1.14.2 → 1.16.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/.babelrc +1 -2
- package/.github/workflows/issue_tracker.yml +31 -16
- package/README.md +21 -3
- package/dist/amp.js +3 -0
- package/dist/banner.js +3 -0
- package/dist/creative.js +4 -3
- package/dist/creative.max.js +546 -593
- package/dist/load-cookie-with-consent.html +1 -1
- package/dist/load-cookie.html +1 -1
- package/dist/mobile.js +3 -0
- package/dist/native-render.js +3 -3
- package/dist/native-trk.js +3 -3
- package/dist/native.js +3 -0
- package/dist/uid.js +3 -3
- package/dist/video.js +3 -0
- package/gulpfile.js +84 -45
- package/karma.conf.maker.js +4 -6
- package/package.json +80 -82
- package/src/ampOrMobile.js +14 -0
- package/src/creative.js +2 -9
- package/src/environment.js +62 -75
- package/src/legacy.js +29 -0
- package/src/legacyNativeRender.js +6 -0
- package/src/mobileAndAmpRender.js +239 -0
- package/src/nativeAssetManager.js +91 -57
- package/src/nativeORTBTrackerManager.js +2 -2
- package/src/nativeRender.js +2 -2
- package/src/nativeRenderManager.js +46 -69
- package/src/postscribeRender.js +10 -0
- package/src/renderingManager.js +106 -358
- package/src/utils.js +1 -11
- package/template/amp/dfp-creative.html +1 -1
- package/test/spec/environment_spec.js +4 -11
- package/test/spec/legacyNativeRender_spec.js +25 -0
- package/test/spec/mobileAndAmpRender_spec.js +316 -0
- package/test/spec/nativeAssetManager_spec.js +227 -79
- package/test/spec/nativeORTBTrackerManager_spec.js +3 -19
- package/test/spec/nativeRenderManager_spec.js +77 -55
- package/test/spec/nativeRender_spec.js +23 -0
- package/test/spec/renderingManager_spec.js +16 -265
- package/webpack.conf.js +3 -1
@@ -0,0 +1,239 @@
|
|
1
|
+
import { getCreativeCommentMarkup, triggerPixel, createTrackPixelHtml, loadScript, getCreativeComment, writeAdUrl, transformAuctionTargetingData, sendRequest, getUUID } from './utils';
|
2
|
+
import { isSafeFrame, isMobileApp } from './environment';
|
3
|
+
import { insertElement } from './domHelper';
|
4
|
+
import { writeAdHtml } from './postscribeRender';
|
5
|
+
|
6
|
+
const DEFAULT_CACHE_HOST = 'prebid.adnxs.com';
|
7
|
+
const DEFAULT_CACHE_PATH = '/pbc/v1/cache';
|
8
|
+
|
9
|
+
/**
|
10
|
+
* Render mobile or amp ad
|
11
|
+
* @param {string} cacheHost Cache host
|
12
|
+
* @param {string} cachePath Cache path
|
13
|
+
* @param {string} uuid id to render response from cache endpoint
|
14
|
+
* @param {string} size size of the creative
|
15
|
+
* @param {string} hbPb final price of the winning bid
|
16
|
+
* @param {Bool} isMobileApp flag to detect mobile app
|
17
|
+
*/
|
18
|
+
export function renderAmpOrMobileAd(dataObject) {
|
19
|
+
const targetingData = transformAuctionTargetingData(dataObject);
|
20
|
+
let { cacheHost, cachePath, uuid, size, hbPb } = targetingData;
|
21
|
+
uuid = uuid || '';
|
22
|
+
// For MoPub, creative is stored in localStorage via SDK.
|
23
|
+
let search = 'Prebid_';
|
24
|
+
if (uuid.substr(0, search.length) === search) {
|
25
|
+
loadFromLocalCache(uuid);
|
26
|
+
//register creative right away to not miss initial geom-update
|
27
|
+
updateIframe(size);
|
28
|
+
} else {
|
29
|
+
let adUrl = `${getCacheEndpoint(cacheHost, cachePath)}?uuid=${uuid}`;
|
30
|
+
//register creative right away to not miss initial geom-update
|
31
|
+
updateIframe(size);
|
32
|
+
sendRequest(adUrl, responseCallback(isMobileApp(targetingData.env), hbPb));
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Load response from localStorage. In case of MoPub, sdk caches response
|
38
|
+
* @param {string} cacheId
|
39
|
+
*/
|
40
|
+
function loadFromLocalCache(cacheId) {
|
41
|
+
let bid = window.localStorage.getItem(cacheId);
|
42
|
+
let displayFn = responseCallback(true);
|
43
|
+
displayFn(bid);
|
44
|
+
}
|
45
|
+
|
46
|
+
/**
|
47
|
+
* update iframe by using size string to resize
|
48
|
+
* @param {string} size
|
49
|
+
*/
|
50
|
+
function updateIframe(size) {
|
51
|
+
if (size) {
|
52
|
+
const sizeArr = size.split('x').map(Number);
|
53
|
+
resizeIframe(sizeArr[0], sizeArr[1]);
|
54
|
+
} else {
|
55
|
+
console.log('Targeting key hb_size not found to resize creative');
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
/**
|
60
|
+
* Resize container iframe
|
61
|
+
* @param {Number} width width of creative
|
62
|
+
* @param {Number} height height of creative
|
63
|
+
*/
|
64
|
+
function resizeIframe(width, height) {
|
65
|
+
if (isSafeFrame(window)) {
|
66
|
+
const iframeWidth = window.innerWidth;
|
67
|
+
const iframeHeight = window.innerHeight;
|
68
|
+
|
69
|
+
function resize(status) {
|
70
|
+
let newWidth = width - iframeWidth;
|
71
|
+
let newHeight = height - iframeHeight;
|
72
|
+
window.$sf.ext.expand({ r: newWidth, b: newHeight, push: true });
|
73
|
+
}
|
74
|
+
|
75
|
+
if (iframeWidth !== width || iframeHeight !== height) {
|
76
|
+
window.$sf.ext.register(width, height, resize);
|
77
|
+
// we need to resize the DFP container as well
|
78
|
+
window.parent.postMessage({
|
79
|
+
sentinel: 'amp',
|
80
|
+
type: 'embed-size',
|
81
|
+
width: width,
|
82
|
+
height: height
|
83
|
+
}, '*');
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Returns cache endpoint concatenated with cache path
|
90
|
+
* @param {string} cacheHost Cache Endpoint host
|
91
|
+
* @param {string} cachePath Cache Endpoint path
|
92
|
+
*/
|
93
|
+
function getCacheEndpoint(cacheHost, cachePath) {
|
94
|
+
let host = (typeof cacheHost === 'undefined' || cacheHost === "") ? DEFAULT_CACHE_HOST : cacheHost;
|
95
|
+
let path = (typeof cachePath === 'undefined' || cachePath === "") ? DEFAULT_CACHE_PATH : cachePath;
|
96
|
+
|
97
|
+
return `https://${host}${path}`;
|
98
|
+
}
|
99
|
+
|
100
|
+
|
101
|
+
/**
|
102
|
+
* Cache request Callback to display creative
|
103
|
+
* @param {Bool} isMobileApp
|
104
|
+
* @param {string} hbPb final price of the winning bid
|
105
|
+
* @returns {function} a callback function that parses response
|
106
|
+
*/
|
107
|
+
function responseCallback(isMobileApp, hbPb) {
|
108
|
+
return function (response) {
|
109
|
+
let bidObject = parseResponse(response);
|
110
|
+
let auctionPrice = bidObject.price || hbPb;
|
111
|
+
let ad = getCreativeCommentMarkup(bidObject);
|
112
|
+
let width = (bidObject.width) ? bidObject.width : bidObject.w;
|
113
|
+
let height = (bidObject.height) ? bidObject.height : bidObject.h;
|
114
|
+
|
115
|
+
// When Prebid Universal Creative reads from Prebid Cache, we need to have it check for the existence of the wurl parameter. If it exists, hit it.
|
116
|
+
if (bidObject.wurl) {
|
117
|
+
triggerPixel(decodeURIComponent(bidObject.wurl));
|
118
|
+
}
|
119
|
+
|
120
|
+
if (bidObject.adm) {
|
121
|
+
if (auctionPrice) { // replace ${AUCTION_PRICE} macro with the bidObject.price or hb_pb.
|
122
|
+
bidObject.adm = bidObject.adm.replace('${AUCTION_PRICE}', auctionPrice);
|
123
|
+
} else {
|
124
|
+
/*
|
125
|
+
From OpenRTB spec 2.5: If the source value is an optional parameter that was not specified, the macro will simply be removed (i.e., replaced with a zero-length string).
|
126
|
+
*/
|
127
|
+
bidObject.adm = bidObject.adm.replace('${AUCTION_PRICE}', '');
|
128
|
+
}
|
129
|
+
ad += (isMobileApp) ? constructMarkup(bidObject.adm, width, height) : bidObject.adm;
|
130
|
+
if (bidObject.nurl) {
|
131
|
+
ad += createTrackPixelHtml(decodeURIComponent(bidObject.nurl));
|
132
|
+
}
|
133
|
+
if (bidObject.burl) {
|
134
|
+
let triggerBurl = function () { triggerPixel(bidObject.burl); };
|
135
|
+
if (isMobileApp) {
|
136
|
+
let mraidScript = loadScript(window, 'mraid.js',
|
137
|
+
function () { // Success loading MRAID
|
138
|
+
let result = registerMRAIDViewableEvent(triggerBurl);
|
139
|
+
if (!result) {
|
140
|
+
triggerBurl(); // Error registering event
|
141
|
+
}
|
142
|
+
},
|
143
|
+
triggerBurl // Error loading MRAID
|
144
|
+
);
|
145
|
+
} else {
|
146
|
+
triggerBurl(); // Not a mobile app
|
147
|
+
}
|
148
|
+
}
|
149
|
+
writeAdHtml(ad);
|
150
|
+
} else if (bidObject.nurl) {
|
151
|
+
if (isMobileApp) {
|
152
|
+
let adhtml = loadScript(window, bidObject.nurl);
|
153
|
+
ad += constructMarkup(adhtml.outerHTML, width, height);
|
154
|
+
|
155
|
+
writeAdHtml(ad);
|
156
|
+
} else {
|
157
|
+
let nurl = bidObject.nurl;
|
158
|
+
let commentElm = getCreativeComment(bidObject);
|
159
|
+
insertElement(commentElm, document, 'body');
|
160
|
+
writeAdUrl(nurl, width, height);
|
161
|
+
}
|
162
|
+
}
|
163
|
+
}
|
164
|
+
};
|
165
|
+
|
166
|
+
/**
|
167
|
+
* Parse response
|
168
|
+
* @param {string} response
|
169
|
+
* @returns {Object} bidObject parsed response
|
170
|
+
*/
|
171
|
+
function parseResponse(response) {
|
172
|
+
let bidObject;
|
173
|
+
try {
|
174
|
+
bidObject = JSON.parse(response);
|
175
|
+
} catch (error) {
|
176
|
+
console.log(`Error parsing response from cache host: ${error}`);
|
177
|
+
}
|
178
|
+
return bidObject;
|
179
|
+
}
|
180
|
+
|
181
|
+
/**
|
182
|
+
* Wrap mobile app creative in div
|
183
|
+
* @param {string} ad html for creative
|
184
|
+
* @param {Number} width width of creative
|
185
|
+
* @param {Number} height height of creative
|
186
|
+
* @returns {string} creative markup
|
187
|
+
*/
|
188
|
+
function constructMarkup(ad, width, height) {
|
189
|
+
let id = getUUID();
|
190
|
+
return `<div id="${id}" style="border-style: none; position: absolute; width:100%; height:100%;">
|
191
|
+
<div id="${id}_inner" style="margin: 0 auto; width:${width}px; height:${height}px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">${ad}</div>
|
192
|
+
</div>`;
|
193
|
+
}
|
194
|
+
|
195
|
+
|
196
|
+
function registerMRAIDViewableEvent(callback) {
|
197
|
+
|
198
|
+
function exposureChangeListener(exposure) {
|
199
|
+
if (exposure > 0) {
|
200
|
+
mraid.removeEventListener('exposureChange', exposureChangeListener);
|
201
|
+
callback();
|
202
|
+
}
|
203
|
+
}
|
204
|
+
|
205
|
+
function viewableChangeListener(viewable) {
|
206
|
+
if (viewable) {
|
207
|
+
mraid.removeEventListener('viewableChange', viewableChangeListener);
|
208
|
+
callback();
|
209
|
+
}
|
210
|
+
}
|
211
|
+
|
212
|
+
function registerViewableChecks() {
|
213
|
+
if (window.MRAID_ENV && parseFloat(window.MRAID_ENV.version) >= 3) {
|
214
|
+
mraid.addEventListener('exposureChange', exposureChangeListener);
|
215
|
+
} else if (window.MRAID_ENV && parseFloat(window.MRAID_ENV.version) < 3) {
|
216
|
+
if (mraid.isViewable()) {
|
217
|
+
callback();
|
218
|
+
} else {
|
219
|
+
mraid.addEventListener('viewableChange', viewableChangeListener);
|
220
|
+
}
|
221
|
+
}
|
222
|
+
}
|
223
|
+
|
224
|
+
function readyListener() {
|
225
|
+
mraid.removeEventListener('ready', readyListener);
|
226
|
+
registerViewableChecks();
|
227
|
+
}
|
228
|
+
|
229
|
+
if (window.mraid && window.MRAID_ENV) {
|
230
|
+
if (mraid.getState() == 'loading') {
|
231
|
+
mraid.addEventListener('ready', readyListener);
|
232
|
+
} else {
|
233
|
+
registerViewableChecks();
|
234
|
+
}
|
235
|
+
return true;
|
236
|
+
} else {
|
237
|
+
return false;
|
238
|
+
}
|
239
|
+
}
|
@@ -6,9 +6,7 @@
|
|
6
6
|
import { addNativeClickTrackers, fireNativeImpressionTrackers } from './nativeORTBTrackerManager';
|
7
7
|
import { sendRequest, loadScript } from './utils';
|
8
8
|
import {prebidMessenger} from './messaging.js';
|
9
|
-
import {
|
10
|
-
|
11
|
-
const envionment = newEnvironment(window);
|
9
|
+
import { isSafeFrame } from './environment.js';
|
12
10
|
/*
|
13
11
|
* Native asset->key mapping from Prebid.js/src/constants.json
|
14
12
|
* https://github.com/prebid/Prebid.js/blob/8635c91942de9df4ec236672c39b19448545a812/src/constants.json#L67
|
@@ -18,6 +16,7 @@ const NATIVE_KEYS = {
|
|
18
16
|
body: 'hb_native_body',
|
19
17
|
body2: 'hb_native_body2',
|
20
18
|
privacyLink: 'hb_native_privacy',
|
19
|
+
privacyIcon: 'hb_native_privicon',
|
21
20
|
sponsoredBy: 'hb_native_brand',
|
22
21
|
image: 'hb_native_image',
|
23
22
|
icon: 'hb_native_icon',
|
@@ -60,9 +59,22 @@ const assetTypeMapping = {
|
|
60
59
|
const DEFAULT_CACHE_HOST = 'prebid.adnxs.com';
|
61
60
|
const DEFAULT_CACHE_PATH = '/pbc/v1/cache';
|
62
61
|
|
63
|
-
|
64
|
-
|
65
|
-
|
62
|
+
const CLICK_URL_UNESC = `%%CLICK_URL_UNESC%%`;
|
63
|
+
|
64
|
+
let clickUrlUnesc = '';
|
65
|
+
|
66
|
+
export function newNativeAssetManager(win, nativeTag, mkMessenger = prebidMessenger) {
|
67
|
+
|
68
|
+
// clickUrlUnesc contains the url to track clicks in GAM. we check if it
|
69
|
+
// has been transformed, by GAM, in an URL.
|
70
|
+
// if CLICK_URL_UNESC is the string "%%CLICK_URL_UNESC%%", we're not in GAM.
|
71
|
+
if (nativeTag.clickUrlUnesc && nativeTag.clickUrlUnesc !== CLICK_URL_UNESC) {
|
72
|
+
clickUrlUnesc = nativeTag.clickUrlUnesc;
|
73
|
+
}
|
74
|
+
const {pubUrl} = nativeTag;
|
75
|
+
|
76
|
+
const sendMessage = mkMessenger(pubUrl, win);
|
77
|
+
let callback, errCallback;
|
66
78
|
let errorCountEscapeHatch = 0;
|
67
79
|
let cancelMessageListener;
|
68
80
|
|
@@ -184,7 +196,8 @@ export function newNativeAssetManager(win, pubUrl) {
|
|
184
196
|
* and requestAllAssets flag is set in the tag, postmessage roundtrip
|
185
197
|
* to retrieve native assets that have a value on the corresponding bid
|
186
198
|
*/
|
187
|
-
function loadAssets(adId, cb) {
|
199
|
+
function loadAssets(adId, cb, onError) {
|
200
|
+
errCallback = onError;
|
188
201
|
const placeholders = scanDOMForPlaceHolders(adId);
|
189
202
|
|
190
203
|
if (hasPbNativeData() && win.pbNativeData.hasOwnProperty('assetsToReplace')) {
|
@@ -200,6 +213,8 @@ export function newNativeAssetManager(win, pubUrl) {
|
|
200
213
|
} else if (placeholders.length > 0) {
|
201
214
|
callback = cb;
|
202
215
|
cancelMessageListener = requestAssets(adId, placeholders);
|
216
|
+
} else {
|
217
|
+
onError && onError(new Error('No assets to load: no placeholders found in template'));
|
203
218
|
}
|
204
219
|
}
|
205
220
|
|
@@ -274,69 +289,83 @@ export function newNativeAssetManager(win, pubUrl) {
|
|
274
289
|
* Postmessage listener for when Prebid responds with requested native assets.
|
275
290
|
*/
|
276
291
|
function replaceAssets(event) {
|
277
|
-
var data = {};
|
278
|
-
|
279
292
|
try {
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
293
|
+
|
294
|
+
var data = {};
|
295
|
+
|
296
|
+
try {
|
297
|
+
data = JSON.parse(event.data);
|
298
|
+
} catch (e) {
|
299
|
+
if (errorCountEscapeHatch++ > 10) {
|
300
|
+
// TODO: this should be a timeout, not an arbitrary cap on the number of messages received
|
301
|
+
/*
|
302
|
+
* if for some reason Prebid never responds with the native assets,
|
303
|
+
* get rid of this listener because other messages won't stop coming
|
304
|
+
*/
|
305
|
+
stopListening();
|
306
|
+
throw e;
|
307
|
+
}
|
308
|
+
return;
|
288
309
|
}
|
289
|
-
return;
|
290
|
-
}
|
291
310
|
|
292
311
|
if (data.message === 'assetResponse') {
|
312
|
+
// add GAM %%CLICK_URL_UNESC%% to the data object to be eventually used in renderers
|
313
|
+
data.clickUrlUnesc = clickUrlUnesc;
|
293
314
|
const body = win.document.body.innerHTML;
|
294
315
|
const head = win.document.head.innerHTML;
|
295
316
|
|
296
|
-
|
317
|
+
if (hasPbNativeData() && data.adId !== win.pbNativeData.adId) return;
|
297
318
|
|
298
|
-
|
319
|
+
callback = ((cb) => {
|
320
|
+
return () => {
|
321
|
+
fireNativeImpressionTrackers(data.adId, sendMessage);
|
322
|
+
addNativeClickTrackers(data.adId, sendMessage);
|
323
|
+
cb && cb();
|
324
|
+
}
|
325
|
+
})(callback);
|
299
326
|
|
300
|
-
|
301
|
-
let renderPayload = data.assets;
|
302
|
-
if (data.ortb) {
|
303
|
-
renderPayload.ortb = data.ortb;
|
304
|
-
callback = () => {
|
305
|
-
fireNativeImpressionTrackers(data.adId, sendMessage);
|
306
|
-
addNativeClickTrackers(data.adId, data.ortb, sendMessage);
|
307
|
-
}
|
308
|
-
}
|
327
|
+
if (head) win.document.head.innerHTML = replace(head, data);
|
309
328
|
|
310
|
-
if ((data.hasOwnProperty('rendererUrl') && data.rendererUrl) || (hasPbNativeData() && win.pbNativeData.hasOwnProperty('rendererUrl'))) {
|
311
|
-
if (win.renderAd) {
|
312
|
-
const newHtml = (win.renderAd && win.renderAd(renderPayload)) || '';
|
313
329
|
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
330
|
+
data.assets = data.assets || [];
|
331
|
+
let renderPayload = data.assets;
|
332
|
+
if (data.ortb) {
|
333
|
+
renderPayload.ortb = data.ortb;
|
334
|
+
}
|
318
335
|
|
319
|
-
|
320
|
-
|
321
|
-
} else {
|
322
|
-
loadScript(win, ((hasPbNativeData() && win.pbNativeData.hasOwnProperty('rendererUrl') && win.pbNativeData.rendererUrl) || data.rendererUrl), function() {
|
336
|
+
if ((data.hasOwnProperty('rendererUrl') && data.rendererUrl) || (hasPbNativeData() && win.pbNativeData.hasOwnProperty('rendererUrl'))) {
|
337
|
+
if (win.renderAd) {
|
323
338
|
const newHtml = (win.renderAd && win.renderAd(renderPayload)) || '';
|
324
339
|
|
325
340
|
renderAd(newHtml, data);
|
326
|
-
})
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
341
|
+
} else if (document.getElementById('pb-native-renderer')) {
|
342
|
+
document.getElementById('pb-native-renderer').addEventListener('load', function () {
|
343
|
+
const newHtml = (win.renderAd && win.renderAd(renderPayload)) || '';
|
344
|
+
|
345
|
+
renderAd(newHtml, data);
|
346
|
+
});
|
347
|
+
} else {
|
348
|
+
loadScript(win, ((hasPbNativeData() && win.pbNativeData.hasOwnProperty('rendererUrl') && win.pbNativeData.rendererUrl) || data.rendererUrl), function () {
|
349
|
+
const newHtml = (win.renderAd && win.renderAd(renderPayload)) || '';
|
350
|
+
|
351
|
+
renderAd(newHtml, data);
|
352
|
+
});
|
353
|
+
}
|
354
|
+
} else if ((data.hasOwnProperty('adTemplate') && data.adTemplate) || (hasPbNativeData() && win.pbNativeData.hasOwnProperty('adTemplate'))) {
|
355
|
+
const template = (hasPbNativeData() && win.pbNativeData.hasOwnProperty('adTemplate') && win.pbNativeData.adTemplate) || data.adTemplate;
|
356
|
+
const newHtml = replace(template, data);
|
331
357
|
|
332
|
-
|
333
|
-
|
334
|
-
|
358
|
+
renderAd(newHtml, data);
|
359
|
+
} else {
|
360
|
+
const newHtml = replace(body, data);
|
335
361
|
|
336
|
-
|
337
|
-
|
338
|
-
|
362
|
+
win.document.body.innerHTML = newHtml;
|
363
|
+
callback && callback();
|
364
|
+
stopListening();
|
365
|
+
}
|
339
366
|
}
|
367
|
+
} catch (e) {
|
368
|
+
errCallback && errCallback(e);
|
340
369
|
}
|
341
370
|
}
|
342
371
|
|
@@ -366,7 +395,7 @@ export function newNativeAssetManager(win, pubUrl) {
|
|
366
395
|
// current iframe width to the width of the container. This
|
367
396
|
// is to handle the case where the native ad is rendered inside
|
368
397
|
// a GAM display ad.
|
369
|
-
if (!
|
398
|
+
if (!isSafeFrame(window)) {
|
370
399
|
let iframeContainer = getCurrentFrameContainer(win);
|
371
400
|
if (iframeContainer && iframeContainer.children && iframeContainer.children[0]) {
|
372
401
|
const iframe = iframeContainer.children[0];
|
@@ -376,11 +405,16 @@ export function newNativeAssetManager(win, pubUrl) {
|
|
376
405
|
}
|
377
406
|
}
|
378
407
|
}
|
408
|
+
|
409
|
+
//substitute CLICK_URL_UNESC with actual value
|
410
|
+
html = html.replaceAll(CLICK_URL_UNESC, bid.clickUrlUnesc || "");
|
411
|
+
|
379
412
|
win.document.body.innerHTML += html;
|
380
413
|
callback && callback();
|
381
414
|
win.removeEventListener('message', replaceAssets);
|
382
415
|
stopListening();
|
383
|
-
requestHeightResize(bid.adId, (document.body.clientHeight || document.body.offsetHeight), document.body.clientWidth);
|
416
|
+
const resize = () => requestHeightResize(bid.adId, (document.body.clientHeight || document.body.offsetHeight), document.body.clientWidth);
|
417
|
+
document.readyState === 'complete' ? resize() : window.onload = resize;
|
384
418
|
|
385
419
|
if (typeof window.postRenderAd === 'function') {
|
386
420
|
window.postRenderAd(bid);
|
@@ -406,16 +440,16 @@ export function newNativeAssetManager(win, pubUrl) {
|
|
406
440
|
}
|
407
441
|
|
408
442
|
ortb.assets.forEach(asset => {
|
409
|
-
html = html.
|
443
|
+
html = html.replaceAll(`##hb_native_asset_id_${asset.id}##`, getAssetValue(asset));
|
410
444
|
if (asset.link && asset.link.url) {
|
411
|
-
html = html.
|
445
|
+
html = html.replaceAll(`##hb_native_asset_link_id_${asset.id}##`, asset.link.url);
|
412
446
|
}
|
413
447
|
});
|
414
448
|
|
415
449
|
html = html.replaceAll(/##hb_native_asset_id_\d+##/gm, '');
|
416
450
|
|
417
451
|
if (ortb.privacy) {
|
418
|
-
html = html.
|
452
|
+
html = html.replaceAll("##hb_native_privacy##", ortb.privacy);
|
419
453
|
}
|
420
454
|
|
421
455
|
if (ortb.link) {
|
@@ -12,11 +12,11 @@ export function fireNativeImpressionTrackers(adId, sendMessage) {
|
|
12
12
|
sendMessage(message);
|
13
13
|
}
|
14
14
|
|
15
|
-
export function addNativeClickTrackers(adId,
|
15
|
+
export function addNativeClickTrackers(adId, sendMessage) {
|
16
16
|
const message = {
|
17
17
|
message: 'Prebid Native',
|
18
18
|
action: 'click',
|
19
|
-
adId
|
19
|
+
adId
|
20
20
|
};
|
21
21
|
const adElements = document.getElementsByClassName(AD_ANCHOR_CLASS_NAME) || [];
|
22
22
|
// get all assets that have 'link' property, map asset.id -> asset.link
|
package/src/nativeRender.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import { newNativeRenderManager } from './nativeRenderManager';
|
2
2
|
|
3
|
-
window.pbNativeTag = (window.pbNativeTag || {});
|
4
3
|
const nativeRenderManager = newNativeRenderManager(window);
|
5
4
|
|
6
|
-
window.
|
5
|
+
window.ucTag = (window.ucTag || {});
|
7
6
|
|
7
|
+
window.ucTag.renderAd = nativeRenderManager.renderNativeAd;
|
@@ -4,73 +4,50 @@
|
|
4
4
|
import {newNativeAssetManager} from './nativeAssetManager';
|
5
5
|
import {prebidMessenger} from './messaging.js';
|
6
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
|
-
sendMessage = prebidMessenger(nativeTag.pubUrl, win);
|
54
|
-
const nativeAssetManager = newNativeAssetManager(window, nativeTag.pubUrl);
|
55
|
-
|
56
|
-
if (nativeTag.hasOwnProperty('adId')) {
|
57
|
-
|
58
|
-
if (nativeTag.hasOwnProperty('rendererUrl') && !nativeTag.rendererUrl.match(/##.*##/i)) {
|
59
|
-
const scr = document.createElement('SCRIPT');
|
60
|
-
scr.src = nativeTag.rendererUrl,
|
61
|
-
scr.id = 'pb-native-renderer';
|
62
|
-
document.body.appendChild(scr);
|
63
|
-
}
|
64
|
-
nativeAssetManager.loadAssets(nativeTag.adId, () => {
|
65
|
-
fireNativeImpTracker(nativeTag.adId);
|
66
|
-
fireNativeCallback();
|
67
|
-
});
|
68
|
-
} else {
|
69
|
-
console.warn('Prebid Native Tag object was missing \'adId\'.');
|
70
|
-
}
|
71
|
-
}
|
72
|
-
|
73
|
-
return {
|
74
|
-
renderNativeAd
|
75
|
-
}
|
7
|
+
export function newNativeRenderManager(win, mkMessenger = prebidMessenger, assetMgr = newNativeAssetManager) {
|
8
|
+
let sendMessage;
|
9
|
+
|
10
|
+
|
11
|
+
let renderNativeAd = function (doc, nativeTag) {
|
12
|
+
window.pbNativeData = nativeTag;
|
13
|
+
sendMessage = mkMessenger(nativeTag.pubUrl, win);
|
14
|
+
|
15
|
+
function signalResult(adId, success, info) {
|
16
|
+
sendMessage({
|
17
|
+
message: 'Prebid Event',
|
18
|
+
adId,
|
19
|
+
event: success ? 'adRenderSucceeded' : 'adRenderFailed',
|
20
|
+
info
|
21
|
+
});
|
22
|
+
}
|
23
|
+
|
24
|
+
try {
|
25
|
+
const nativeAssetManager = assetMgr(window, nativeTag);
|
26
|
+
|
27
|
+
if (nativeTag.adId != null) {
|
28
|
+
|
29
|
+
if (nativeTag.hasOwnProperty('rendererUrl') && !nativeTag.rendererUrl.match(/##.*##/i)) {
|
30
|
+
const scr = doc.createElement('SCRIPT');
|
31
|
+
scr.src = nativeTag.rendererUrl,
|
32
|
+
scr.id = 'pb-native-renderer';
|
33
|
+
doc.body.appendChild(scr);
|
34
|
+
}
|
35
|
+
nativeAssetManager.loadAssets(nativeTag.adId, () => {
|
36
|
+
signalResult(nativeTag.adId, true);
|
37
|
+
}, (e) => {
|
38
|
+
signalResult(nativeTag.adId, false, {reason: 'exception', message: e.message});
|
39
|
+
});
|
40
|
+
} else {
|
41
|
+
signalResult(null, false, {reason: 'missingDocOrAdid'});
|
42
|
+
console.warn('Prebid Native Tag object was missing \'adId\'.');
|
43
|
+
}
|
44
|
+
} catch (e) {
|
45
|
+
signalResult(nativeTag && nativeTag.adId, false, {reason: 'exception', message: e.message});
|
46
|
+
console.error('Error rendering ad', e);
|
47
|
+
}
|
48
|
+
};
|
49
|
+
|
50
|
+
return {
|
51
|
+
renderNativeAd
|
52
|
+
};
|
76
53
|
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import postscribe from 'postscribe';
|
2
|
+
|
3
|
+
export function writeAdHtml(markup, ps = postscribe) {
|
4
|
+
// remove <?xml> and <!doctype> tags
|
5
|
+
// https://github.com/prebid/prebid-universal-creative/issues/134
|
6
|
+
markup = markup.replace(/\<(\?xml|(\!DOCTYPE[^\>\[]+(\[[^\]]+)?))+[^>]+\>/g, '');
|
7
|
+
ps(document.body, markup, {
|
8
|
+
error: console.error
|
9
|
+
});
|
10
|
+
}
|