diff --git a/modules/onetagBidAdapter.js b/modules/onetagBidAdapter.js index f2cea34bb1a..6280dd12268 100644 --- a/modules/onetagBidAdapter.js +++ b/modules/onetagBidAdapter.js @@ -1,6 +1,9 @@ 'use strict'; import { BANNER, VIDEO } from '../src/mediaTypes.js'; +import { INSTREAM, OUTSTREAM } from '../src/video.js'; +import { Renderer } from '../src/Renderer.js'; +import find from 'core-js-pure/features/array/find.js'; const { registerBidder } = require('../src/adapters/bidderFactory.js'); const ENDPOINT = 'https://onetag-sys.com/prebid-request'; @@ -29,9 +32,7 @@ export function isValid(type, bid) { return parseSizes(bid).length > 0; } else if (type === VIDEO && hasTypeVideo(bid)) { const context = bid.mediaTypes.video.context; - if (context === 'outstream') { - return parseVideoSize(bid).length > 0 && typeof bid.renderer !== 'undefined' && typeof bid.renderer.render !== 'undefined' && typeof bid.renderer.url !== 'undefined'; - } else if (context === 'instream') { + if (context === 'outstream' || context === 'instream') { return parseVideoSize(bid).length > 0; } } @@ -44,31 +45,27 @@ export function isValid(type, bid) { * @param {validBidRequests[]} - an array of bids * @return ServerRequest Info describing the request to the server. */ - function buildRequests(validBidRequests, bidderRequest) { - const bids = requestsToBids(validBidRequests); - const bidObject = {'bids': bids}; - const pageInfo = getPageInfo(); - - const payload = Object.assign(bidObject, pageInfo); - + const payload = { + bids: requestsToBids(validBidRequests), + ...getPageInfo() + }; if (bidderRequest && bidderRequest.gdprConsent) { payload.gdprConsent = { consentString: bidderRequest.gdprConsent.consentString, consentRequired: bidderRequest.gdprConsent.gdprApplies }; } - if (bidderRequest && bidderRequest.uspConsent) { payload.usPrivacy = bidderRequest.uspConsent; } - if (bidderRequest && bidderRequest.userId) { payload.userId = bidderRequest.userId; } - + if (window.localStorage) { + payload.onetagSid = window.localStorage.getItem('onetag_sid'); + } const payloadString = JSON.stringify(payload); - return { method: 'POST', url: ENDPOINT, @@ -76,108 +73,150 @@ function buildRequests(validBidRequests, bidderRequest) { } } -function interpretResponse(serverResponse, request) { - let body = serverResponse.body; +function interpretResponse(serverResponse, bidderRequest) { + const body = serverResponse.body; const bids = []; - - if (typeof serverResponse === 'string') { - try { - body = JSON.parse(serverResponse); - } catch (e) { - return bids; - } - } - + const requestData = JSON.parse(bidderRequest.data); if (!body || (body.nobid && body.nobid === true)) { return bids; } if (!body.bids || !Array.isArray(body.bids) || body.bids.length === 0) { return bids; } - - body.bids.forEach(function(bid) { - let responseBid = { - requestId: bid.requestId, - cpm: bid.cpm, - width: bid.width, - height: bid.height, - creativeId: bid.creativeId, - dealId: bid.dealId ? bid.dealId : '', - currency: bid.currency, + body.bids.forEach(({ + requestId, + cpm, + width, + height, + creativeId, + dealId, + currency, + mediaType, + ttl, + rendererUrl, + ad, + vastUrl, + videoCacheKey + }) => { + const responseBid = { + requestId, + cpm, + width, + height, + creativeId, + dealId: dealId == null ? dealId : '', + currency, netRevenue: false, - mediaType: bid.mediaType, - ttl: bid.ttl || 300 + meta: { + mediaType + }, + ttl: ttl || 300 }; - - if (bid.mediaType === BANNER) { - responseBid.ad = bid.ad; - } else if (bid.mediaType === VIDEO) { - responseBid.vastXml = bid.ad; + if (mediaType === BANNER) { + responseBid.ad = ad; + } else if (mediaType === VIDEO) { + const {context, adUnitCode} = find(requestData.bids, (item) => item.bidId === requestId); + if (context === INSTREAM) { + responseBid.vastUrl = vastUrl; + responseBid.videoCacheKey = videoCacheKey; + } else if (context === OUTSTREAM) { + responseBid.vastXml = ad; + responseBid.vastUrl = vastUrl; + if (rendererUrl) { + responseBid.renderer = createRenderer({requestId, rendererUrl, adUnitCode}); + } + } } - bids.push(responseBid); }); - return bids; } -/** - * Returns information about the page needed by the server in an object to be converted in JSON - * @returns {{location: *, referrer: (*|string), masked: *, wWidth: (*|Number), wHeight: (*|Number), sWidth, sHeight, date: string, timeOffset: number}} - */ -function getPageInfo() { - let w, d, l, r, m, p, e, t, s; - for (w = window, d = w.document, l = d.location.href, r = d.referrer, m = 0, e = encodeURIComponent, t = new Date(), s = screen; w !== w.parent;) { - try { - p = w.parent; l = p.location.href; r = p.document.referrer; w = p; - } catch (e) { - m = top !== w.parent ? 2 : 1; - break - } +function createRenderer(bid, rendererOptions = {}) { + const renderer = Renderer.install({ + id: bid.requestId, + url: bid.rendererUrl, + config: rendererOptions, + adUnitCode: bid.adUnitCode, + loaded: false + }); + try { + renderer.setRender(onetagRenderer); + } catch (e) { + } - let isDocHidden; - let xOffset; - let yOffset; + return renderer; +} + +function onetagRenderer({renderer, width, height, vastXml, adUnitCode}) { + renderer.push(() => { + window.onetag.Player.init({ + width, + height, + vastXml, + nodeId: adUnitCode, + config: renderer.getConfig() + }); + }); +} + +function getFrameNesting() { + let frame = window; try { - if (typeof w.document.hidden !== 'undefined') { - isDocHidden = w.document.hidden; - } else if (typeof w.document['msHidden'] !== 'undefined') { - isDocHidden = w.document['msHidden']; - } else if (typeof w.document['webkitHidden'] !== 'undefined') { - isDocHidden = w.document['webkitHidden']; - } else { - isDocHidden = null; + while (frame !== frame.top) { + // eslint-disable-next-line no-unused-expressions + frame.location.href; + frame = frame.parent; } - } catch (e) { - isDocHidden = null; + } catch (e) {} + return { + topmostFrame: frame, + currentFrameNesting: frame.top === frame ? 1 : 2 } +} + +function getDocumentVisibility(window) { try { - xOffset = w.pageXOffset; - yOffset = w.pageYOffset; + if (typeof window.document.hidden !== 'undefined') { + return window.document.hidden; + } else if (typeof window.document['msHidden'] !== 'undefined') { + return window.document['msHidden']; + } else if (typeof window.document['webkitHidden'] !== 'undefined') { + return window.document['webkitHidden']; + } else { + return null; + } } catch (e) { - xOffset = null; - yOffset = null; + return null; } +} + +/** + * Returns information about the page needed by the server in an object to be converted in JSON + * @returns {{location: *, referrer: (*|string), masked: *, wWidth: (*|Number), wHeight: (*|Number), sWidth, sHeight, date: string, timeOffset: number}} + */ +function getPageInfo() { + const { topmostFrame, currentFrameNesting } = getFrameNesting(); return { - location: e(l), - referrer: e(r) || '0', - masked: m, - wWidth: w.innerWidth, - wHeight: w.innerHeight, - oWidth: w.outerWidth, - oHeight: w.outerHeight, - sWidth: s.width, - sHeight: s.height, - aWidth: s.availWidth, - aHeight: s.availHeight, - sLeft: 'screenLeft' in w ? w.screenLeft : w.screenX, - sTop: 'screenTop' in w ? w.screenTop : w.screenY, - xOffset: xOffset, - yOffset: yOffset, - docHidden: isDocHidden, + location: encodeURIComponent(topmostFrame.location.href), + referrer: encodeURIComponent(topmostFrame.document.referrer) || '0', + masked: currentFrameNesting, + wWidth: topmostFrame.innerWidth, + wHeight: topmostFrame.innerHeight, + oWidth: topmostFrame.outerWidth, + oHeight: topmostFrame.outerHeight, + sWidth: topmostFrame.screen.width, + sHeight: topmostFrame.screen.height, + aWidth: topmostFrame.screen.availWidth, + aHeight: topmostFrame.screen.availHeight, + sLeft: 'screenLeft' in topmostFrame ? topmostFrame.screenLeft : topmostFrame.screenX, + sTop: 'screenTop' in topmostFrame ? topmostFrame.screenTop : topmostFrame.screenY, + xOffset: topmostFrame.pageXOffset, + yOffset: topmostFrame.pageYOffset, + docHidden: getDocumentVisibility(topmostFrame), + docHeight: topmostFrame.document.body ? topmostFrame.document.body.scrollHeight : null, hLength: history.length, - date: t.toUTCString(), - timeOffset: t.getTimezoneOffset() + timing: getTiming() }; } @@ -223,6 +262,46 @@ function setGeneralInfo(bidRequest) { if (params.dealId) { this['dealId'] = params.dealId; } + const coords = getSpaceCoords(bidRequest.adUnitCode); + if (coords) { + this['coords'] = coords; + } +} + +function getSpaceCoords(id) { + const space = document.getElementById(id); + try { + const { top, left, width, height } = space.getBoundingClientRect(); + let window = space.ownerDocument.defaultView; + const coords = { top: top + window.pageYOffset, left: left + window.pageXOffset, width, height }; + let frame = window.frameElement; + while (frame != null) { + const { top, left } = frame.getBoundingClientRect(); + coords.top += top + window.pageYOffset; + coords.left += left + window.pageXOffset; + window = window.parent; + frame = window.frameElement; + } + return coords; + } catch (e) { + return null; + } +} + +function getTiming() { + try { + if (window.performance != null && window.performance.timing != null) { + const timing = {}; + const perf = window.performance.timing; + timing.pageLoadTime = perf.loadEventEnd - perf.navigationStart; + timing.connectTime = perf.responseEnd - perf.requestStart; + timing.renderTime = perf.domComplete - perf.domLoading; + return timing; + } + } catch (e) { + return null; + } + return null; } function parseVideoSize(bid) { @@ -255,35 +334,35 @@ function getSizes(sizes) { } function getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) { - const syncs = []; - if (syncOptions.iframeEnabled) { - const rnd = new Date().getTime(); - let params = '?cb=' + rnd; - - if (gdprConsent && typeof gdprConsent.consentString === 'string') { - params += '&gdpr_consent=' + gdprConsent.consentString; - if (typeof gdprConsent.gdprApplies === 'boolean') { - params += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); - } - } - - if (uspConsent && typeof uspConsent === 'string') { - params += '&us_privacy=' + uspConsent; + let syncs = []; + let params = ''; + if (gdprConsent && typeof gdprConsent.consentString === 'string') { + params += '&gdpr_consent=' + gdprConsent.consentString; + if (typeof gdprConsent.gdprApplies === 'boolean') { + params += '&gdpr=' + (gdprConsent.gdprApplies ? 1 : 0); } - + } + if (uspConsent && typeof uspConsent === 'string') { + params += '&us_privacy=' + uspConsent; + } + if (syncOptions.iframeEnabled) { syncs.push({ type: 'iframe', - url: USER_SYNC_ENDPOINT + params + url: USER_SYNC_ENDPOINT + '?cb=' + new Date().getTime() + params + }); + } + if (syncOptions.pixelEnabled) { + syncs.push({ + type: 'image', + url: USER_SYNC_ENDPOINT + '?tag=img' + params }); } return syncs; } export const spec = { - code: BIDDER_CODE, supportedMediaTypes: [BANNER, VIDEO], - isBidRequestValid: isBidRequestValid, buildRequests: buildRequests, interpretResponse: interpretResponse, @@ -291,5 +370,4 @@ export const spec = { }; -// Starting point registerBidder(spec); diff --git a/modules/onetagBidAdapter.md b/modules/onetagBidAdapter.md index 9757be4d440..7814403afbe 100644 --- a/modules/onetagBidAdapter.md +++ b/modules/onetagBidAdapter.md @@ -27,7 +27,7 @@ OneTag Bid Adapter supports banner and video at present. } }] }, { - code: 'video-space', + code: 'video-instream-space', mediaTypes: { video: { context: "instream", @@ -41,6 +41,20 @@ OneTag Bid Adapter supports banner and video at present. pubId: "your_publisher_id" // required, testing pubId: "386276e072" } }] + }, { + code: 'video-outstream-space', + mediaTypes: { + video: { + context: "outstream", + playerSize: [640,480] + } + }, + bids: [{ + bidder: "onetag", + params: { + pubId: "your_publisher_id" // required, testing pubId: "386276e072" + } + }] }]; ``` diff --git a/test/spec/modules/onetagBidAdapter_spec.js b/test/spec/modules/onetagBidAdapter_spec.js index 2b31c875502..a951c74b20b 100644 --- a/test/spec/modules/onetagBidAdapter_spec.js +++ b/test/spec/modules/onetagBidAdapter_spec.js @@ -1,6 +1,8 @@ import { spec, isValid, hasTypeVideo } from 'modules/onetagBidAdapter.js'; import { expect } from 'chai'; +import find from 'core-js-pure/features/array/find.js'; import { BANNER, VIDEO } from 'src/mediaTypes.js'; +import {INSTREAM, OUTSTREAM} from 'src/video.js'; describe('onetag', function () { function createBid() { @@ -26,7 +28,7 @@ describe('onetag', function () { return bid; } - function createVideoBid(bidRequest) { + function createInstreamVideoBid(bidRequest) { const bid = bidRequest || createBid(); bid.mediaTypes = bid.mediaTypes || {}; bid.mediaTypes.video = { @@ -37,7 +39,7 @@ describe('onetag', function () { return bid; } - function createWrongVideoOutstreamBid(bidRequest) { + function createOutstreamVideoBid(bidRequest) { const bid = bidRequest || createBid(); bid.mediaTypes = bid.mediaTypes || {}; bid.mediaTypes.video = { @@ -49,12 +51,12 @@ describe('onetag', function () { } function createMultiFormatBid() { - return createVideoBid(createBannerBid()); + return createInstreamVideoBid(createBannerBid()); } const bannerBid = createBannerBid(); - const videoBid = createVideoBid(); - const outstreamVideoBid = createWrongVideoOutstreamBid(); + const instreamVideoBid = createInstreamVideoBid(); + const outstreamVideoBid = createOutstreamVideoBid(); describe('isBidRequestValid', function () { it('Should return true when required params are found', function () { @@ -76,30 +78,30 @@ describe('onetag', function () { }); describe('video bidRequest', function () { it('Should return false when the context is undefined', function () { - videoBid.mediaTypes.video.context = undefined; - expect(spec.isBidRequestValid(videoBid)).to.be.false; + instreamVideoBid.mediaTypes.video.context = undefined; + expect(spec.isBidRequestValid(instreamVideoBid)).to.be.false; }); it('Should return false when the context is not instream or outstream', function () { - videoBid.mediaTypes.video.context = 'wrong'; - expect(spec.isBidRequestValid(videoBid)).to.be.false; + instreamVideoBid.mediaTypes.video.context = 'wrong'; + expect(spec.isBidRequestValid(instreamVideoBid)).to.be.false; }); it('Should return false when playerSize is undefined', function () { - const videoBid = createVideoBid(); + const videoBid = createInstreamVideoBid(); videoBid.mediaTypes.video.playerSize = undefined; expect(spec.isBidRequestValid(videoBid)).to.be.false; }); it('Should return false when playerSize is not an array', function () { - const videoBid = createVideoBid(); + const videoBid = createInstreamVideoBid(); videoBid.mediaTypes.video.playerSize = 30; expect(spec.isBidRequestValid(videoBid)).to.be.false; }); it('Should return false when playerSize is an empty array', function () { - const videoBid = createVideoBid(); + const videoBid = createInstreamVideoBid(); videoBid.mediaTypes.video.playerSize = []; expect(spec.isBidRequestValid(videoBid)).to.be.false; }); - it('Should return false when context is outstream but no renderer object is defined', function () { - expect(spec.isBidRequestValid(outstreamVideoBid)).to.be.false; + it('Should return true when context is outstream', function () { + expect(spec.isBidRequestValid(outstreamVideoBid)).to.be.true; }); }); describe('multi format bidRequest', function () { @@ -111,7 +113,7 @@ describe('onetag', function () { }); describe('buildRequests', function () { - let serverRequest = spec.buildRequests([bannerBid, videoBid]); + let serverRequest = spec.buildRequests([bannerBid, instreamVideoBid]); it('Creates a ServerRequest object with method, URL and data', function () { expect(serverRequest).to.exist; expect(serverRequest.method).to.exist; @@ -128,9 +130,9 @@ describe('onetag', function () { const d = serverRequest.data; try { const data = JSON.parse(d); - it('Should contains all keys', function () { + it('Should contain all keys', function () { expect(data).to.be.an('object'); - expect(data).to.have.all.keys('location', 'masked', 'referrer', 'sHeight', 'sWidth', 'timeOffset', 'date', 'wHeight', 'wWidth', 'oHeight', 'oWidth', 'aWidth', 'aHeight', 'sLeft', 'sTop', 'hLength', 'bids', 'docHidden', 'xOffset', 'yOffset'); + expect(data).to.include.all.keys('location', 'referrer', 'masked', 'sHeight', 'sWidth', 'docHeight', 'wHeight', 'wWidth', 'oHeight', 'oWidth', 'aWidth', 'aHeight', 'sLeft', 'sTop', 'hLength', 'bids', 'docHidden', 'xOffset', 'yOffset', 'onetagSid'); expect(data.location).to.be.a('string'); expect(data.masked).to.be.a('number'); expect(data.referrer).to.be.a('string'); @@ -145,10 +147,7 @@ describe('onetag', function () { expect(data.sLeft).to.be.a('number'); expect(data.sTop).to.be.a('number'); expect(data.hLength).to.be.a('number'); - expect(data.timeOffset).to.be.a('number'); - expect(data.date).to.be.a('string'); expect(data.bids).to.be.an('array'); - const bids = data['bids']; for (let i = 0; i < bids.length; i++) { const bid = bids[i]; @@ -190,7 +189,7 @@ describe('onetag', function () { expect(payload.gdprConsent.consentString).to.exist.and.to.equal(consentString); expect(payload.gdprConsent.consentRequired).to.exist.and.to.be.true; }); - it('should send us privacy string', function () { + it('Should send us privacy string', function () { let consentString = 'us_foo'; let bidderRequest = { 'bidderCode': 'onetag', @@ -207,70 +206,44 @@ describe('onetag', function () { }); }); describe('interpretResponse', function () { - function getBannerRes() { - return { - ad: '
Advertising
', - cpm: 13, - width: 300, - height: 250, - creativeId: '1820', - dealId: 'dishfo', - currency: 'USD', - requestId: 'sdiceobxcw', - mediaType: BANNER - } - } - function getVideoRes() { - return { - ad: '', - cpm: 13, - width: 300, - height: 250, - creativeId: '1820', - dealId: 'dishfo', - currency: 'USD', - requestId: 'sdiceobxcw', - mediaType: VIDEO - } - } - function getBannerAdnVideoRes() { - return { - body: { - nobid: false, - bids: [getBannerRes(), getVideoRes()] - } - }; - } - const responseObj = getBannerAdnVideoRes(); + const request = getBannerVideoRequest(); + const response = getBannerVideoResponse(); + const requestData = JSON.parse(request.data); it('Returns an array of valid server responses if response object is valid', function () { - const serverResponses = spec.interpretResponse(responseObj); - - expect(serverResponses).to.be.an('array').that.is.not.empty; - for (let i = 0; i < serverResponses.length; i++) { - let dataItem = serverResponses[i]; - if (dataItem.mediaType === VIDEO) { - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'vastXml', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'dealId'); - } else if (dataItem.mediaType === BANNER) { - expect(dataItem).to.have.all.keys('requestId', 'cpm', 'width', 'height', 'ad', 'ttl', 'creativeId', 'netRevenue', 'currency', 'mediaType', 'dealId'); + const interpretedResponse = spec.interpretResponse(response, request); + expect(interpretedResponse).to.be.an('array').that.is.not.empty; + for (let i = 0; i < interpretedResponse.length; i++) { + let dataItem = interpretedResponse[i]; + expect(dataItem).to.include.all.keys('requestId', 'cpm', 'width', 'height', 'ttl', 'creativeId', 'netRevenue', 'currency', 'meta', 'dealId'); + if (dataItem.meta.mediaType === VIDEO) { + const {context} = find(requestData.bids, (item) => item.bidId === dataItem.requestId); + if (context === INSTREAM) { + expect(dataItem).to.include.all.keys('videoCacheKey', 'vastUrl'); + expect(dataItem.vastUrl).to.be.a('string'); + expect(dataItem.videoCacheKey).to.be.a('string'); + } else if (context === OUTSTREAM) { + expect(dataItem).to.include.all.keys('renderer', 'vastXml', 'vastUrl'); + expect(dataItem.renderer).to.be.an('object'); + expect(dataItem.vastUrl).to.be.a('string'); + expect(dataItem.vastXml).to.be.a('string'); + } + } else if (dataItem.meta.mediaType === BANNER) { + expect(dataItem).to.include.all.keys('ad'); + expect(dataItem.ad).to.be.a('string'); } expect(dataItem.requestId).to.be.a('string'); expect(dataItem.cpm).to.be.a('number'); expect(dataItem.width).to.be.a('number'); expect(dataItem.height).to.be.a('number'); - if (dataItem.mediaType === VIDEO) { - expect(dataItem.vastXml).to.be.a('string'); - } else if (dataItem.mediaType === BANNER) { - expect(dataItem.ad).to.be.a('string'); - } expect(dataItem.ttl).to.be.a('number'); expect(dataItem.creativeId).to.be.a('string'); expect(dataItem.netRevenue).to.be.a('boolean'); expect(dataItem.currency).to.be.a('string'); } - it('Returns an empty array if invalid response is passed', function () { - const serverResponses = spec.interpretResponse('invalid_response'); - expect(serverResponses).to.be.an('array').that.is.empty; - }); + }); + it('Returns an empty array if response is not valid', function () { + const serverResponses = spec.interpretResponse('invalid_response', { data: '{}' }); + expect(serverResponses).to.be.an('array').that.is.empty; }); }); describe('getUserSyncs', function () { @@ -333,3 +306,105 @@ describe('onetag', function () { }); }); }); + +function getBannerVideoResponse() { + return { + body: { + nobid: false, + bids: [ + { + ad: '
Advertising
', + cpm: 13, + width: 300, + height: 250, + creativeId: '1820', + dealId: 'dishfo', + currency: 'USD', + requestId: 'banner', + mediaType: BANNER, + }, + { + cpm: 13, + width: 300, + height: 250, + creativeId: '1820', + dealId: 'dishfo', + currency: 'USD', + requestId: 'videoInstream', + vastUrl: 'https://videoinstream.org', + videoCacheKey: 'key', + mediaType: VIDEO + }, + { + cpm: 13, + width: 300, + height: 250, + creativeId: '1820', + dealId: 'dishfo', + currency: 'USD', + vastUrl: 'https://videooutstream.org', + requestId: 'videoOutstream', + ad: '', + rendererUrl: 'https://testRenderer', + mediaType: VIDEO + } + ] + } + }; +} + +function getBannerVideoRequest() { + return { + data: JSON.stringify({ + bids: [ + { + adUnitCode: 'target-div', + bidId: 'videoOutstream', + bidderRequestId: '12bb1e0f9fb669', + auctionId: '80784b4d-79ad-49ef-a006-75d8888b7609', + transactionId: '5f132731-3091-49b2-8fab-0e9c917733bc', + pubId: '386276e072', + context: 'outstream', + mimes: [], + playerSize: [], + type: 'video' + }, + { + adUnitCode: 'target-div', + bidId: 'videoInstream', + bidderRequestId: '12bb1e0f9fb669', + auctionId: '80784b4d-79ad-49ef-a006-75d8888b7609', + transactionId: '5f132731-3091-49b2-8fab-0e9c917733bc', + pubId: '386276e072', + context: 'instream', + mimes: [], + playerSize: [], + type: 'video' + } + ], + location: 'https%3A%2F%2Flocal.onetag.net%3A9000%2Fv2%2Fprebid-video%2Fvideo.html%3Fpbjs_debug%3Dtrue', + referrer: '0', + masked: 0, + wWidth: 860, + wHeight: 949, + oWidth: 1853, + oHeight: 1053, + sWidth: 1920, + sHeight: 1080, + aWidth: 1920, + aHeight: 1053, + sLeft: 1987, + sTop: 27, + xOffset: 0, + yOffset: 0, + docHidden: false, + hLength: 2, + timing: { + pageLoadTime: -1593433770022, + connectTime: 42, + renderTime: -1593433770092 + }, + onetagSid: 'user_id' + }) + } +}