prebid-universal-creative 1.14.2 → 1.15.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.
@@ -1,385 +1,128 @@
1
- import * as utils from './utils';
2
- import * as domHelper from './domHelper';
3
- import {triggerPixel} from './utils';
1
+ import { parseUrl, transformAuctionTargetingData } from './utils';
2
+ import { canLocatePrebid } from './environment';
3
+ import { insertElement, getEmptyIframe } from './domHelper';
4
4
  import {prebidMessenger} from './messaging.js';
5
5
 
6
- const DEFAULT_CACHE_HOST = 'prebid.adnxs.com';
7
- const DEFAULT_CACHE_PATH = '/pbc/v1/cache';
6
+ export function renderBannerOrDisplayAd(doc, dataObject) {
7
+ const targetingData = transformAuctionTargetingData(dataObject);
8
8
 
9
- /**
10
- *
11
- * @param {Object} win Window object
12
- * @param {Object} environment Environment object
13
- * @returns {Object}
14
- */
15
- export function newRenderingManager(win, environment) {
16
- /**
17
- * DataObject passed to render the ad
18
- * @typedef {Object} dataObject
19
- * @property {string} host - Prebid cache host
20
- * @property {string} uuid - ID to fetch the value from prebid cache
21
- * @property {string} mediaType - Creative media type, It can be banner, native or video
22
- * @property {string} pubUrl - Publisher url
23
- * @property {string} winurl
24
- * @property {string} winbidid
25
- */
26
-
27
- /**
28
- * Public render ad function to be used in dfp creative setup
29
- * @param {object} doc
30
- * @param {dataObject} dataObject
31
- */
32
- let renderAd = function(doc, dataObject) {
33
- const targetingData = utils.transformAuctionTargetingData(dataObject);
34
-
35
- if (environment.isMobileApp(targetingData.env)) {
36
- renderAmpOrMobileAd(targetingData.cacheHost, targetingData.cachePath, targetingData.uuid, targetingData.size, targetingData.hbPb, true);
37
- } else if (environment.isAmp(targetingData.uuid)) {
38
- renderAmpOrMobileAd(targetingData.cacheHost, targetingData.cachePath, targetingData.uuid, targetingData.size, targetingData.hbPb);
39
- } else if (!environment.canLocatePrebid()) {
40
- renderCrossDomain(targetingData.adId, targetingData.adServerDomain, targetingData.pubUrl);
41
- } else {
42
- renderLegacy(doc, targetingData.adId);
43
- }
44
- };
45
-
46
- /**
47
- * Calls prebid.js renderAd function to render ad
48
- * @param {Object} doc Document
49
- * @param {string} adId Id of creative to render
50
- */
51
- function renderLegacy(doc, adId) {
52
- let w = win;
53
- for (let i = 0; i < 10; i++) {
54
- w = w.parent;
55
- if (w.$$PREBID_GLOBAL$$) {
56
- try {
57
- w.$$PREBID_GLOBAL$$.renderAd(doc, adId);
58
- break;
59
- } catch (e) {
60
- continue;
61
- }
62
- }
63
- }
9
+ if (!canLocatePrebid(window)) {
10
+ renderCrossDomain(window, targetingData.adId, targetingData.adServerDomain, targetingData.pubUrl);
11
+ } else {
12
+ renderLegacy(doc, targetingData.adId);
64
13
  }
14
+ }
65
15
 
66
- /**
67
- * Render ad in safeframe using postmessage
68
- * @param {string} adId Id of creative to render
69
- * @param {string} pubAdServerDomain publisher adserver domain name
70
- * @param {string} pubUrl Url of publisher page
71
- */
72
- function renderCrossDomain(adId, pubAdServerDomain = '', pubUrl) {
73
- let windowLocation = win.location;
74
- let adServerDomain = pubAdServerDomain || win.location.hostname;
75
- let fullAdServerDomain = windowLocation.protocol + '//' + adServerDomain;
76
- const sendMessage = prebidMessenger(pubUrl, win);
77
-
78
- function renderAd(ev) {
79
- let key = ev.message ? 'message' : 'data';
80
- let adObject = {};
16
+ /**
17
+ * Calls prebid.js renderAd function to render ad
18
+ * @param {Object} doc Document
19
+ * @param {string} adId Id of creative to render
20
+ */
21
+ export function renderLegacy(doc, adId) {
22
+ let w = window;
23
+ for (let i = 0; i < 10; i++) {
24
+ w = w.parent;
25
+ if (w.$$PREBID_GLOBAL$$) {
81
26
  try {
82
- adObject = JSON.parse(ev[key]);
27
+ w.$$PREBID_GLOBAL$$.renderAd(doc, adId);
28
+ break;
83
29
  } catch (e) {
84
- return;
30
+ continue;
85
31
  }
86
-
87
- if (adObject.message && adObject.message === 'Prebid Response' &&
88
- adObject.adId === adId) {
89
- try {
90
- let body = win.document.body;
91
- let ad = adObject.ad;
92
- let url = adObject.adUrl;
93
- let width = adObject.width;
94
- let height = adObject.height;
95
-
96
- if (adObject.mediaType === 'video') {
97
- signalRenderResult(false, {
98
- reason: 'preventWritingOnMainDocument',
99
- message: `Cannot render video ad ${adId}`
100
- });
101
- console.log('Error trying to write ad.');
102
- } else if (ad) {
103
- const iframe = domHelper.getEmptyIframe(adObject.height, adObject.width);
104
- body.appendChild(iframe);
105
- iframe.contentDocument.open();
106
- iframe.contentDocument.write(ad);
107
- iframe.contentDocument.close();
108
- signalRenderResult(true);
109
- } else if (url) {
110
- const iframe = domHelper.getEmptyIframe(height, width);
111
- iframe.style.display = 'inline';
112
- iframe.style.overflow = 'hidden';
113
- iframe.src = url;
114
-
115
- domHelper.insertElement(iframe, document, 'body');
116
- signalRenderResult(true);
117
- } else {
118
- signalRenderResult(false, {
119
- reason: 'noAd',
120
- message: `No ad for ${adId}`
121
- });
122
- console.log(`Error trying to write ad. No ad markup or adUrl for ${adId}`);
123
- }
124
- } catch (e) {
125
- signalRenderResult(false, {reason: "exception", message: e.message});
126
- console.log(`Error in rendering ad`, e);
127
- }
128
- }
129
-
130
- function signalRenderResult(success, {reason, message} = {}) {
131
- const payload = {
132
- message: 'Prebid Event',
133
- adId,
134
- event: success ? 'adRenderSucceeded' : 'adRenderFailed',
135
- }
136
- if (!success) {
137
- payload.info = {reason, message};
138
- }
139
- sendMessage(payload);
140
- }
141
- }
142
-
143
-
144
- function requestAdFromPrebid() {
145
- let message = {
146
- message: 'Prebid Request',
147
- adId: adId,
148
- adServerDomain: fullAdServerDomain
149
- }
150
- sendMessage(message, renderAd);
151
- }
152
-
153
- requestAdFromPrebid();
154
- }
155
-
156
- /**
157
- * Returns cache endpoint concatenated with cache path
158
- * @param {string} cacheHost Cache Endpoint host
159
- * @param {string} cachePath Cache Endpoint path
160
- */
161
- function getCacheEndpoint(cacheHost, cachePath) {
162
- let host = (typeof cacheHost === 'undefined' || cacheHost === "") ? DEFAULT_CACHE_HOST : cacheHost;
163
- let path = (typeof cachePath === 'undefined' || cachePath === "") ? DEFAULT_CACHE_PATH : cachePath;
164
-
165
- return `https://${host}${path}`;
166
- }
167
-
168
- /**
169
- * update iframe by using size string to resize
170
- * @param {string} size
171
- */
172
- function updateIframe(size) {
173
- if (size) {
174
- const sizeArr = size.split('x').map(Number);
175
- resizeIframe(sizeArr[0], sizeArr[1]);
176
- } else {
177
- console.log('Targeting key hb_size not found to resize creative');
178
32
  }
179
33
  }
34
+ }
180
35
 
181
- /**
182
- * Render mobile or amp ad
183
- * @param {string} cacheHost Cache host
184
- * @param {string} cachePath Cache path
185
- * @param {string} uuid id to render response from cache endpoint
186
- * @param {string} size size of the creative
187
- * @param {string} hbPb final price of the winning bid
188
- * @param {Bool} isMobileApp flag to detect mobile app
189
- */
190
- function renderAmpOrMobileAd(cacheHost, cachePath, uuid = '', size, hbPb, isMobileApp) {
191
- // For MoPub, creative is stored in localStorage via SDK.
192
- let search = 'Prebid_';
193
- if(uuid.substr(0, search.length) === search) {
194
- loadFromLocalCache(uuid);
195
- //register creative right away to not miss initial geom-update
196
- updateIframe(size);
197
- } else {
198
- let adUrl = `${getCacheEndpoint(cacheHost, cachePath)}?uuid=${uuid}`;
199
- //register creative right away to not miss initial geom-update
200
- updateIframe(size);
201
- utils.sendRequest(adUrl, responseCallback(isMobileApp, hbPb));
36
+ /**
37
+ * Render ad in safeframe using postmessage
38
+ * @param {string} adId Id of creative to render
39
+ * @param {string} pubAdServerDomain publisher adserver domain name
40
+ * @param {string} pubUrl Url of publisher page
41
+ */
42
+ export function renderCrossDomain(win, adId, pubAdServerDomain = '', pubUrl) {
43
+ let windowLocation = win.location;
44
+ let adServerDomain = pubAdServerDomain || win.location.hostname;
45
+ let fullAdServerDomain = windowLocation.protocol + '//' + adServerDomain;
46
+ const sendMessage = prebidMessenger(pubUrl, win);
47
+
48
+ function renderAd(ev) {
49
+ let key = ev.message ? "message" : "data";
50
+ let adObject = {};
51
+ try {
52
+ adObject = JSON.parse(ev[key]);
53
+ } catch (e) {
54
+ return;
202
55
  }
203
- }
204
-
205
- /**
206
- * Cache request Callback to display creative
207
- * @param {Bool} isMobileApp
208
- * @param {string} hbPb final price of the winning bid
209
- * @returns {function} a callback function that parses response
210
- */
211
- function responseCallback(isMobileApp, hbPb) {
212
- return function(response) {
213
- let bidObject = parseResponse(response);
214
- let auctionPrice = bidObject.price || hbPb;
215
- let ad = utils.getCreativeCommentMarkup(bidObject);
216
- let width = (bidObject.width) ? bidObject.width : bidObject.w;
217
- let height = (bidObject.height) ? bidObject.height : bidObject.h;
218
56
 
219
- // 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.
220
- if (bidObject.wurl) {
221
- triggerPixel(decodeURIComponent(bidObject.wurl));
222
- }
223
-
224
- if (bidObject.adm) {
225
- if(auctionPrice) { // replace ${AUCTION_PRICE} macro with the bidObject.price or hb_pb.
226
- bidObject.adm = bidObject.adm.replace('${AUCTION_PRICE}', auctionPrice);
227
- } else {
228
- /*
229
- 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).
230
- */
231
- bidObject.adm = bidObject.adm.replace('${AUCTION_PRICE}', '');
232
- }
233
- ad += (isMobileApp) ? constructMarkup(bidObject.adm, width, height) : bidObject.adm;
234
- if (bidObject.nurl) {
235
- ad += utils.createTrackPixelHtml(decodeURIComponent(bidObject.nurl));
236
- }
237
- if (bidObject.burl) {
238
- let triggerBurl = function(){ utils.triggerPixel(bidObject.burl); };
239
- if(isMobileApp) {
240
- let mraidScript = utils.loadScript(win, 'mraid.js',
241
- function() { // Success loading MRAID
242
- let result = registerMRAIDViewableEvent(triggerBurl);
243
- if (!result) {
244
- triggerBurl(); // Error registering event
245
- }
246
- },
247
- triggerBurl // Error loading MRAID
248
- );
249
- } else {
250
- triggerBurl(); // Not a mobile app
251
- }
252
- }
253
- utils.writeAdHtml(ad);
254
- } else if (bidObject.nurl) {
255
- if(isMobileApp) {
256
- let adhtml = utils.loadScript(win, bidObject.nurl);
257
- ad += constructMarkup(adhtml.outerHTML, width, height);
258
- utils.writeAdHtml(ad);
57
+ if (
58
+ adObject.message &&
59
+ adObject.message === "Prebid Response" &&
60
+ adObject.adId === adId
61
+ ) {
62
+ try {
63
+ let body = win.document.body;
64
+ let ad = adObject.ad;
65
+ let url = adObject.adUrl;
66
+ let width = adObject.width;
67
+ let height = adObject.height;
68
+
69
+ if (adObject.mediaType === "video") {
70
+ signalRenderResult(false, {
71
+ reason: "preventWritingOnMainDocument",
72
+ message: `Cannot render video ad ${adId}`,
73
+ });
74
+ console.log("Error trying to write ad.");
75
+ } else if (ad) {
76
+ const iframe = getEmptyIframe(adObject.height, adObject.width);
77
+ body.appendChild(iframe);
78
+ iframe.contentDocument.open();
79
+ iframe.contentDocument.write(ad);
80
+ iframe.contentDocument.close();
81
+ signalRenderResult(true);
82
+ } else if (url) {
83
+ const iframe = getEmptyIframe(height, width);
84
+ iframe.style.display = "inline";
85
+ iframe.style.overflow = "hidden";
86
+ iframe.src = url;
87
+
88
+ insertElement(iframe, document, "body");
89
+ signalRenderResult(true);
259
90
  } else {
260
- let nurl = bidObject.nurl;
261
- let commentElm = utils.getCreativeComment(bidObject);
262
- domHelper.insertElement(commentElm, document, 'body');
263
- utils.writeAdUrl(nurl, width, height);
91
+ signalRenderResult(false, {
92
+ reason: "noAd",
93
+ message: `No ad for ${adId}`,
94
+ });
95
+ console.log(
96
+ `Error trying to write ad. No ad markup or adUrl for ${adId}`
97
+ );
264
98
  }
99
+ } catch (e) {
100
+ signalRenderResult(false, { reason: "exception", message: e.message });
101
+ console.log(`Error in rendering ad`, e);
265
102
  }
266
103
  }
267
- };
268
-
269
- /**
270
- * Load response from localStorage. In case of MoPub, sdk caches response
271
- * @param {string} cacheId
272
- */
273
- function loadFromLocalCache(cacheId) {
274
- let bid = win.localStorage.getItem(cacheId);
275
- let displayFn = responseCallback(true);
276
- displayFn(bid);
277
- }
278
-
279
- /**
280
- * Parse response
281
- * @param {string} response
282
- * @returns {Object} bidObject parsed response
283
- */
284
- function parseResponse(response) {
285
- let bidObject;
286
- try {
287
- bidObject = JSON.parse(response);
288
- } catch (error) {
289
- console.log(`Error parsing response from cache host: ${error}`);
290
- }
291
- return bidObject;
292
- }
293
-
294
- /**
295
- * Wrap mobile app creative in div
296
- * @param {string} ad html for creative
297
- * @param {Number} width width of creative
298
- * @param {Number} height height of creative
299
- * @returns {string} creative markup
300
- */
301
- function constructMarkup(ad, width, height) {
302
- let id = utils.getUUID();
303
- return `<div id="${id}" style="border-style: none; position: absolute; width:100%; height:100%;">
304
- <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>
305
- </div>`;
306
- }
307
-
308
- /**
309
- * Resize container iframe
310
- * @param {Number} width width of creative
311
- * @param {Number} height height of creative
312
- */
313
- function resizeIframe(width, height) {
314
- if (environment.isSafeFrame()) {
315
- const iframeWidth = win.innerWidth;
316
- const iframeHeight = win.innerHeight;
317
104
 
318
- function resize(status) {
319
- let newWidth = width - iframeWidth;
320
- let newHeight = height - iframeHeight;
321
- win.$sf.ext.expand({r:newWidth, b:newHeight, push: true});
322
- }
323
-
324
- if (iframeWidth !== width || iframeHeight !== height) {
325
- win.$sf.ext.register(width, height, resize);
326
- // we need to resize the DFP container as well
327
- win.parent.postMessage({
328
- sentinel: 'amp',
329
- type: 'embed-size',
330
- width: width,
331
- height: height
332
- }, '*');
105
+ function signalRenderResult(success, { reason, message } = {}) {
106
+ const payload = {
107
+ message: "Prebid Event",
108
+ adId,
109
+ event: success ? "adRenderSucceeded" : "adRenderFailed",
110
+ };
111
+ if (!success) {
112
+ payload.info = { reason, message };
333
113
  }
114
+ sendMessage(payload);
334
115
  }
335
116
  }
336
117
 
337
- function registerMRAIDViewableEvent(callback) {
338
-
339
- function exposureChangeListener(exposure) {
340
- if (exposure > 0) {
341
- mraid.removeEventListener('exposureChange', exposureChangeListener);
342
- callback();
343
- }
344
- }
345
-
346
- function viewableChangeListener(viewable) {
347
- if (viewable) {
348
- mraid.removeEventListener('viewableChange', viewableChangeListener);
349
- callback();
350
- }
351
- }
352
-
353
- function registerViewableChecks() {
354
- if (win.MRAID_ENV && parseFloat(win.MRAID_ENV.version) >= 3) {
355
- mraid.addEventListener('exposureChange', exposureChangeListener);
356
- } else if(win.MRAID_ENV && parseFloat(win.MRAID_ENV.version) < 3) {
357
- if (mraid.isViewable()) {
358
- callback();
359
- } else {
360
- mraid.addEventListener('viewableChange', viewableChangeListener);
361
- }
362
- }
363
- }
364
-
365
- function readyListener() {
366
- mraid.removeEventListener('ready', readyListener);
367
- registerViewableChecks();
368
- }
369
-
370
- if (win.mraid && win.MRAID_ENV) {
371
- if (mraid.getState() == 'loading') {
372
- mraid.addEventListener('ready', readyListener);
373
- } else {
374
- registerViewableChecks();
375
- }
376
- return true;
377
- } else {
378
- return false;
118
+ function requestAdFromPrebid() {
119
+ let message = {
120
+ message: 'Prebid Request',
121
+ adId: adId,
122
+ adServerDomain: fullAdServerDomain
379
123
  }
124
+ sendMessage(message, renderAd);
380
125
  }
381
126
 
382
- return {
383
- renderAd
384
- }
127
+ requestAdFromPrebid();
385
128
  }
package/src/utils.js CHANGED
@@ -1,4 +1,3 @@
1
- const postscribe = require('postscribe');
2
1
  import * as domHelper from './domHelper';
3
2
 
4
3
  /**
@@ -115,7 +114,7 @@ export function getCreativeComment(bid) {
115
114
  * @param {*} bid
116
115
  */
117
116
  export function getCreativeCommentMarkup(bid) {
118
- let creativeComment = exports.getCreativeComment(bid);
117
+ let creativeComment = getCreativeComment(bid);
119
118
  let wrapper = document.createElement('div');
120
119
  wrapper.appendChild(creativeComment);
121
120
  return wrapper.innerHTML;
@@ -1,4 +1,4 @@
1
- <script src = "https://cdn.jsdelivr.net/npm/prebid-universal-creative@latest/dist/creative.js"></script>
1
+ <script src = "https://cdn.jsdelivr.net/npm/prebid-universal-creative@latest/dist/%%PATTERN::hb_format%%.js"></script>
2
2
  <script>
3
3
  var ucTagData = {};
4
4
  ucTagData.adServerDomain = "";
@@ -1,5 +1,5 @@
1
1
  import { expect } from 'chai';
2
- import { newEnvironment } from 'src/environment';
2
+ import * as env from 'src/environment';
3
3
  import { mocks } from 'test/helpers/mocks';
4
4
  import { merge } from 'lodash';
5
5
 
@@ -16,8 +16,6 @@ const envMocks = {
16
16
  describe('environment module', function() {
17
17
 
18
18
  it('should return env object with proper public api', function() {
19
- const mockWin = merge(mocks.createFakeWindow('http://appnexus.com'), envMocks.getWindowObject());
20
- const env = newEnvironment(mockWin);
21
19
  expect(env.isMobileApp).to.exist;
22
20
  expect(env.isCrossDomain).to.exist;
23
21
  expect(env.isSafeFrame).to.exist;
@@ -26,8 +24,7 @@ describe('environment module', function() {
26
24
 
27
25
  it('should detect safeframe', function() {
28
26
  const mockWin = merge(mocks.createFakeWindow('http://appnexus.com'), envMocks.getWindowObject());
29
- const env = newEnvironment(mockWin);
30
- expect(env.isSafeFrame()).to.equal(true);
27
+ expect(env.isSafeFrame(mockWin)).to.equal(true);
31
28
  });
32
29
 
33
30
  it('should detect amp', function() {
@@ -39,8 +36,7 @@ describe('environment module', function() {
39
36
  }
40
37
  }
41
38
  const mockWin = merge(mocks.createFakeWindow('http://appnexus.com'), envMocks.getWindowObject(), localWindow);
42
- const env = newEnvironment(mockWin);
43
- expect(env.isAmp('some-uuid')).to.equal(true);
39
+ expect(env.isAmp('some-uuid', mockWin)).to.equal(true);
44
40
  });
45
41
 
46
42
  it('should detect Prebid in higher window', function() {
@@ -54,13 +50,10 @@ describe('environment module', function() {
54
50
  }
55
51
  };
56
52
  const mockWin = merge(mocks.createFakeWindow('http://appnexus.com'), envMocks.getWindowObject(), localWindow);
57
- const env = newEnvironment(mockWin);
58
- expect(env.canLocatePrebid()).to.equal(true);
53
+ expect(env.canLocatePrebid(mockWin)).to.equal(true);
59
54
  });
60
55
 
61
56
  it('should detect mobile app', function() {
62
- const mockWin = merge(mocks.createFakeWindow('http://appnexus.com'), envMocks.getWindowObject());
63
- const env = newEnvironment(mockWin);
64
57
  expect(env.isMobileApp('mobile-app')).to.equal(true);
65
58
  });
66
59
  });
@@ -0,0 +1,25 @@
1
+ import '../../src/legacyNativeRender';
2
+
3
+ describe('legacyNativeRender', () => {
4
+
5
+ after(() => {
6
+ delete window.pbNativeTag;
7
+ window.nativeRenderManager.renderNativeAd.reset();
8
+ })
9
+ it('should accept only one argument', () => {
10
+
11
+ expect(window.pbNativeTag.renderNativeAd).to.exist;
12
+ //expect exactly one argument by this function
13
+ expect(window.pbNativeTag.renderNativeAd.length).to.equal(1);
14
+
15
+ const args = {
16
+ pubUrl: 'http://prebidjs.com',
17
+ adId: 'abc123'
18
+ };
19
+ window.nativeRenderManager.renderNativeAd = sinon.stub();
20
+
21
+ window.pbNativeTag.renderNativeAd(args);
22
+ expect(nativeRenderManager.renderNativeAd.calledOnceWith(document, args)).to.be.true;
23
+
24
+ })
25
+ })