prebid-universal-creative 1.15.0 → 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/.github/workflows/issue_tracker.yml +32 -16
- package/README.md +2 -0
- package/dist/amp.js +3 -3
- package/dist/banner.js +3 -3
- package/dist/creative.js +3 -3
- package/dist/creative.max.js +11 -10
- package/dist/mobile.js +3 -3
- package/dist/native-render.js +3 -3
- package/dist/native-trk.js +3 -3
- package/dist/native.js +3 -3
- package/dist/uid.js +2 -2
- package/dist/video.js +3 -3
- package/gulpfile.js +3 -7
- package/karma.conf.maker.js +4 -6
- package/package.json +4 -4
- package/src/nativeAssetManager.js +91 -56
- package/src/nativeORTBTrackerManager.js +2 -2
- package/src/nativeRenderManager.js +47 -70
- package/src/postscribeRender.js +7 -5
- package/src/renderingManager.js +5 -0
- package/src/utils.js +0 -9
- package/test/spec/mobileAndAmpRender_spec.js +8 -0
- package/test/spec/nativeAssetManager_spec.js +226 -79
- package/test/spec/nativeORTBTrackerManager_spec.js +3 -19
- package/test/spec/nativeRenderManager_spec.js +77 -56
@@ -59,9 +59,22 @@ const assetTypeMapping = {
|
|
59
59
|
const DEFAULT_CACHE_HOST = 'prebid.adnxs.com';
|
60
60
|
const DEFAULT_CACHE_PATH = '/pbc/v1/cache';
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
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;
|
65
78
|
let errorCountEscapeHatch = 0;
|
66
79
|
let cancelMessageListener;
|
67
80
|
|
@@ -183,7 +196,8 @@ export function newNativeAssetManager(win, pubUrl) {
|
|
183
196
|
* and requestAllAssets flag is set in the tag, postmessage roundtrip
|
184
197
|
* to retrieve native assets that have a value on the corresponding bid
|
185
198
|
*/
|
186
|
-
function loadAssets(adId, cb) {
|
199
|
+
function loadAssets(adId, cb, onError) {
|
200
|
+
errCallback = onError;
|
187
201
|
const placeholders = scanDOMForPlaceHolders(adId);
|
188
202
|
|
189
203
|
if (hasPbNativeData() && win.pbNativeData.hasOwnProperty('assetsToReplace')) {
|
@@ -199,6 +213,8 @@ export function newNativeAssetManager(win, pubUrl) {
|
|
199
213
|
} else if (placeholders.length > 0) {
|
200
214
|
callback = cb;
|
201
215
|
cancelMessageListener = requestAssets(adId, placeholders);
|
216
|
+
} else {
|
217
|
+
onError && onError(new Error('No assets to load: no placeholders found in template'));
|
202
218
|
}
|
203
219
|
}
|
204
220
|
|
@@ -273,69 +289,83 @@ export function newNativeAssetManager(win, pubUrl) {
|
|
273
289
|
* Postmessage listener for when Prebid responds with requested native assets.
|
274
290
|
*/
|
275
291
|
function replaceAssets(event) {
|
276
|
-
var data = {};
|
277
|
-
|
278
292
|
try {
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
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;
|
287
309
|
}
|
288
|
-
return;
|
289
|
-
}
|
290
310
|
|
291
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;
|
292
314
|
const body = win.document.body.innerHTML;
|
293
315
|
const head = win.document.head.innerHTML;
|
294
316
|
|
295
|
-
|
317
|
+
if (hasPbNativeData() && data.adId !== win.pbNativeData.adId) return;
|
296
318
|
|
297
|
-
|
319
|
+
callback = ((cb) => {
|
320
|
+
return () => {
|
321
|
+
fireNativeImpressionTrackers(data.adId, sendMessage);
|
322
|
+
addNativeClickTrackers(data.adId, sendMessage);
|
323
|
+
cb && cb();
|
324
|
+
}
|
325
|
+
})(callback);
|
298
326
|
|
299
|
-
|
300
|
-
let renderPayload = data.assets;
|
301
|
-
if (data.ortb) {
|
302
|
-
renderPayload.ortb = data.ortb;
|
303
|
-
callback = () => {
|
304
|
-
fireNativeImpressionTrackers(data.adId, sendMessage);
|
305
|
-
addNativeClickTrackers(data.adId, data.ortb, sendMessage);
|
306
|
-
}
|
307
|
-
}
|
327
|
+
if (head) win.document.head.innerHTML = replace(head, data);
|
308
328
|
|
309
|
-
if ((data.hasOwnProperty('rendererUrl') && data.rendererUrl) || (hasPbNativeData() && win.pbNativeData.hasOwnProperty('rendererUrl'))) {
|
310
|
-
if (win.renderAd) {
|
311
|
-
const newHtml = (win.renderAd && win.renderAd(renderPayload)) || '';
|
312
329
|
|
313
|
-
|
314
|
-
|
315
|
-
|
330
|
+
data.assets = data.assets || [];
|
331
|
+
let renderPayload = data.assets;
|
332
|
+
if (data.ortb) {
|
333
|
+
renderPayload.ortb = data.ortb;
|
334
|
+
}
|
335
|
+
|
336
|
+
if ((data.hasOwnProperty('rendererUrl') && data.rendererUrl) || (hasPbNativeData() && win.pbNativeData.hasOwnProperty('rendererUrl'))) {
|
337
|
+
if (win.renderAd) {
|
316
338
|
const newHtml = (win.renderAd && win.renderAd(renderPayload)) || '';
|
317
339
|
|
318
340
|
renderAd(newHtml, data);
|
319
|
-
})
|
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);
|
357
|
+
|
358
|
+
renderAd(newHtml, data);
|
320
359
|
} else {
|
321
|
-
|
322
|
-
const newHtml = (win.renderAd && win.renderAd(renderPayload)) || '';
|
360
|
+
const newHtml = replace(body, data);
|
323
361
|
|
324
|
-
|
325
|
-
|
362
|
+
win.document.body.innerHTML = newHtml;
|
363
|
+
callback && callback();
|
364
|
+
stopListening();
|
326
365
|
}
|
327
|
-
} else if ((data.hasOwnProperty('adTemplate') && data.adTemplate)||(hasPbNativeData() && win.pbNativeData.hasOwnProperty('adTemplate'))) {
|
328
|
-
const template = (hasPbNativeData() && win.pbNativeData.hasOwnProperty('adTemplate') && win.pbNativeData.adTemplate) || data.adTemplate;
|
329
|
-
const newHtml = replace(template, data);
|
330
|
-
|
331
|
-
renderAd(newHtml, data);
|
332
|
-
} else {
|
333
|
-
const newHtml = replace(body, data);
|
334
|
-
|
335
|
-
win.document.body.innerHTML = newHtml;
|
336
|
-
callback && callback();
|
337
|
-
stopListening();
|
338
366
|
}
|
367
|
+
} catch (e) {
|
368
|
+
errCallback && errCallback(e);
|
339
369
|
}
|
340
370
|
}
|
341
371
|
|
@@ -349,7 +379,7 @@ export function newNativeAssetManager(win, pubUrl) {
|
|
349
379
|
if (!currentParentWindow.frames || !currentParentWindow.frames.length) return null;
|
350
380
|
for (let idx = 0; idx < currentParentWindow.frames.length; idx++)
|
351
381
|
if (currentParentWindow.frames[idx] === currentWindow) {
|
352
|
-
if (!currentParentWindow.document) return null;
|
382
|
+
if (!currentParentWindow.document) return null;
|
353
383
|
for (let frameElement of currentParentWindow.document.getElementsByTagName('iframe')) {
|
354
384
|
if (!frameElement.contentWindow) return null;
|
355
385
|
if (frameElement.contentWindow === currentWindow) {
|
@@ -368,18 +398,23 @@ export function newNativeAssetManager(win, pubUrl) {
|
|
368
398
|
if (!isSafeFrame(window)) {
|
369
399
|
let iframeContainer = getCurrentFrameContainer(win);
|
370
400
|
if (iframeContainer && iframeContainer.children && iframeContainer.children[0]) {
|
371
|
-
const iframe = iframeContainer.children[0];
|
401
|
+
const iframe = iframeContainer.children[0];
|
372
402
|
if (iframe.width === '1' && iframe.height === '1') {
|
373
403
|
let width = iframeContainer.getBoundingClientRect().width;
|
374
404
|
win.document.body.style.width = `${width}px`;
|
375
405
|
}
|
376
406
|
}
|
377
407
|
}
|
408
|
+
|
409
|
+
//substitute CLICK_URL_UNESC with actual value
|
410
|
+
html = html.replaceAll(CLICK_URL_UNESC, bid.clickUrlUnesc || "");
|
411
|
+
|
378
412
|
win.document.body.innerHTML += html;
|
379
413
|
callback && callback();
|
380
414
|
win.removeEventListener('message', replaceAssets);
|
381
415
|
stopListening();
|
382
|
-
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;
|
383
418
|
|
384
419
|
if (typeof window.postRenderAd === 'function') {
|
385
420
|
window.postRenderAd(bid);
|
@@ -405,22 +440,22 @@ export function newNativeAssetManager(win, pubUrl) {
|
|
405
440
|
}
|
406
441
|
|
407
442
|
ortb.assets.forEach(asset => {
|
408
|
-
html = html.
|
443
|
+
html = html.replaceAll(`##hb_native_asset_id_${asset.id}##`, getAssetValue(asset));
|
409
444
|
if (asset.link && asset.link.url) {
|
410
|
-
html = html.
|
445
|
+
html = html.replaceAll(`##hb_native_asset_link_id_${asset.id}##`, asset.link.url);
|
411
446
|
}
|
412
447
|
});
|
413
448
|
|
414
449
|
html = html.replaceAll(/##hb_native_asset_id_\d+##/gm, '');
|
415
450
|
|
416
451
|
if (ortb.privacy) {
|
417
|
-
html = html.
|
452
|
+
html = html.replaceAll("##hb_native_privacy##", ortb.privacy);
|
418
453
|
}
|
419
454
|
|
420
455
|
if (ortb.link) {
|
421
456
|
html = html.replaceAll("##hb_native_linkurl##", ortb.link.url);
|
422
457
|
}
|
423
|
-
|
458
|
+
|
424
459
|
return html;
|
425
460
|
}
|
426
461
|
|
@@ -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
|
@@ -1,76 +1,53 @@
|
|
1
1
|
/*
|
2
2
|
* Script to handle firing impression and click trackers from native teamplates
|
3
3
|
*/
|
4
|
-
import {
|
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 = doc.createElement('SCRIPT');
|
60
|
-
scr.src = nativeTag.rendererUrl,
|
61
|
-
scr.id = 'pb-native-renderer';
|
62
|
-
doc.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
|
}
|
package/src/postscribeRender.js
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
import postscribe from 'postscribe';
|
2
2
|
|
3
|
-
export function writeAdHtml(markup) {
|
4
|
-
|
5
|
-
|
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
|
6
9
|
});
|
7
|
-
|
8
|
-
|
10
|
+
}
|
package/src/renderingManager.js
CHANGED
@@ -19,11 +19,13 @@ export function renderBannerOrDisplayAd(doc, dataObject) {
|
|
19
19
|
* @param {string} adId Id of creative to render
|
20
20
|
*/
|
21
21
|
export function renderLegacy(doc, adId) {
|
22
|
+
let found = false;
|
22
23
|
let w = window;
|
23
24
|
for (let i = 0; i < 10; i++) {
|
24
25
|
w = w.parent;
|
25
26
|
if (w.$$PREBID_GLOBAL$$) {
|
26
27
|
try {
|
28
|
+
found = true;
|
27
29
|
w.$$PREBID_GLOBAL$$.renderAd(doc, adId);
|
28
30
|
break;
|
29
31
|
} catch (e) {
|
@@ -31,6 +33,9 @@ export function renderLegacy(doc, adId) {
|
|
31
33
|
}
|
32
34
|
}
|
33
35
|
}
|
36
|
+
if (!found) {
|
37
|
+
console.error("Unable to locate $$PREBID_GLOBAL$$.renderAd function!");
|
38
|
+
}
|
34
39
|
}
|
35
40
|
|
36
41
|
/**
|
package/src/utils.js
CHANGED
@@ -30,15 +30,6 @@ export function writeAdUrl(adUrl, width, height) {
|
|
30
30
|
document.body.appendChild(iframe);
|
31
31
|
}
|
32
32
|
|
33
|
-
export function writeAdHtml(markup) {
|
34
|
-
// remove <?xml> and <!doctype> tags
|
35
|
-
// https://github.com/prebid/prebid-universal-creative/issues/134
|
36
|
-
markup = markup.replace(/\<(\?xml|(\!DOCTYPE[^\>\[]+(\[[^\]]+)?))+[^>]+\>/g, '');
|
37
|
-
postscribe(document.body, markup, {
|
38
|
-
error: console.error
|
39
|
-
});
|
40
|
-
}
|
41
|
-
|
42
33
|
export function sendRequest(url, callback) {
|
43
34
|
function reqListener() {
|
44
35
|
callback(oReq.responseText);
|
@@ -4,6 +4,7 @@ import * as utils from 'src/utils';
|
|
4
4
|
import { expect } from 'chai';
|
5
5
|
import { mocks } from 'test/helpers/mocks';
|
6
6
|
import { merge } from 'lodash';
|
7
|
+
import {writeAdHtml} from 'src/postscribeRender';
|
7
8
|
|
8
9
|
|
9
10
|
function renderingMocks() {
|
@@ -306,3 +307,10 @@ describe("renderingManager", function () {
|
|
306
307
|
});
|
307
308
|
});
|
308
309
|
|
310
|
+
describe('writeAdHtml', () => {
|
311
|
+
it('removes DOCTYPE from markup', () => {
|
312
|
+
const ps = sinon.stub();
|
313
|
+
writeAdHtml('<!DOCTYPE html><div>mock-ad</div>', ps);
|
314
|
+
sinon.assert.calledWith(ps, sinon.match.any, '<div>mock-ad</div>')
|
315
|
+
})
|
316
|
+
})
|