diff --git a/extensions/amp-apester-media/0.1/amp-apester-media.css b/extensions/amp-apester-media/0.1/amp-apester-media.css index d7d1121c8e22..2cee34654b4e 100644 --- a/extensions/amp-apester-media/0.1/amp-apester-media.css +++ b/extensions/amp-apester-media/0.1/amp-apester-media.css @@ -100,7 +100,8 @@ .i-amphtml-amp-apester-in-unit { display: block!important; position: relative!important; - top: 50%!important; - transform: translateY(-50%)!important; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); margin: 0!important; } diff --git a/extensions/amp-apester-media/0.1/amp-apester-media.js b/extensions/amp-apester-media/0.1/amp-apester-media.js index c308922e2a4d..ffa0fc7b5379 100644 --- a/extensions/amp-apester-media/0.1/amp-apester-media.js +++ b/extensions/amp-apester-media/0.1/amp-apester-media.js @@ -30,6 +30,7 @@ import {dev, user, userAssert} from '#utils/log'; import {handleAds} from './monetization'; import { extractTags, + getOperatingSystem, getPlatform, registerEvent, setFullscreenOff, @@ -188,6 +189,7 @@ class AmpApesterMedia extends AMP.BaseElement { const queryParams = {}; queryParams['renderer'] = false; queryParams['platform'] = getPlatform(); + queryParams['os'] = getOperatingSystem(); if (inative) { if (idOrToken) { suffix = `/inatives/${idOrToken}`; diff --git a/extensions/amp-apester-media/0.1/monetization/ads-constructor.js b/extensions/amp-apester-media/0.1/monetization/ads-constructor.js new file mode 100644 index 000000000000..7895f4f6b3d7 --- /dev/null +++ b/extensions/amp-apester-media/0.1/monetization/ads-constructor.js @@ -0,0 +1,246 @@ +import {createElementWithAttributes} from '#core/dom'; +import {setStyle} from '#core/dom/style'; +import {Services} from '#service'; + +import {defaultRefreshTime, AD_TYPES, defaultBannerSizes} from './consent-util'; + +/** + * @param {!AmpElement} apesterElement + * @return {{height: number, width: number}} + */ +export function getCompanionVideoAdSize(apesterElement) { + const adWidth = apesterElement./*REVIEW*/ clientWidth; + const adRatio = 0.6; + const adHeight = Math.ceil(adWidth * adRatio); + return {width: adWidth, height: adHeight}; +} + +/** + * @param {!AmpElement} apesterElement + * @param {{height: number, width: number}} size + * @param {string} aniviewPlayerId + * @param {!JsonObject} consentObj + * @param {string} publisherId + * @return {!Element} + */ +export function getAniviewAdElement(apesterElement, size, aniviewPlayerId, consentObj, publisherId) { + publisherId = publisherId || '5fabb425e5d4cb4bbc0ca7e4'; + const ampAvAd = createElementWithAttributes( + /** @type {!Document} */ (apesterElement.ownerDocument), + 'amp-iframe', + { + 'scrolling': 'no', + 'id': 'amp-iframe', + 'title': 'Ads', + 'layout': 'responsive', + 'sandbox': 'allow-scripts allow-same-origin allow-popups', + 'allowfullscreen': 'false', + 'frameborder': '0', + 'width': size.width, + 'height': size.height, + 'src': `https://player.avplayer.com/amp/ampiframe.html?AV_TAGID=${aniviewPlayerId}&AV_PUBLISHERID=${publisherId}`, + } + ); + + if (consentObj?.gdpr) { + ampAvAd['data-av_gdpr'] = consentObj['gdpr']; + ampAvAd['data-av_consent'] = consentObj['user_consent']; + } + return ampAvAd; +} + +/** + * @param {!AmpElement} apesterElement + * @param {string} aniviewPlayerId + * @param {!JsonObject} consentObj + * @param {boolean} isAbove + * @param {string} publisherId + * @return {!Element} + */ +export function constructStaticAniviewAd(apesterElement, aniviewPlayerId, consentObj, isAbove, publisherId) { + const size = getCompanionVideoAdSize(apesterElement); + const ampAvAd = getAniviewAdElement(apesterElement, size, aniviewPlayerId, consentObj, publisherId); + + ampAvAd.classList.add('i-amphtml-amp-apester-companion'); + + apesterElement.parentNode.insertBefore(ampAvAd, isAbove ? apesterElement : apesterElement.nextSibling); + + Services.mutatorForDoc(apesterElement).requestChangeSize( + ampAvAd, + size.height + ); +} + +/** + * @param {string} slot + * @param {Array} bannerSizes + * @param {!AmpElement} apesterElement + * @param {number} refreshInterval + * @param {!JsonObject} rtcConfig + * @param {!boolean} isAbove + * @return {!Element} + */ +export function constructStaticDisplayAd( + slot, + bannerSizes, + apesterElement, + refreshInterval, + rtcConfig, + isAbove, +) { + const maxWidth = Math.max.apply( + null, + bannerSizes.map((s) => s[0]) + ); + const maxHeight = Math.max.apply( + null, + bannerSizes.map((s) => s[1]) + ); + + const multiSizeData = bannerSizes.map((size) => size.join('x')).join(); + const ampAd = createElementWithAttributes( + /** @type {!Document} */ (apesterElement.ownerDocument), + 'amp-ad', + { + 'width': `${maxWidth}`, + 'height': `${maxHeight}`, + 'type': 'doubleclick', + 'layout': 'fixed', + 'data-slot': `${slot}`, + 'data-multi-size-validation': 'false', + 'data-multi-size': multiSizeData, + 'data-enable-refresh': `${refreshInterval}`, + } + ); + if (rtcConfig) { + ampAd.setAttribute('rtc-config', JSON.stringify(rtcConfig)); + } + ampAd.classList.add('i-amphtml-amp-apester-companion'); + apesterElement.parentNode.insertBefore(ampAd, isAbove ? apesterElement : apesterElement.nextSibling); + Services.mutatorForDoc(apesterElement).requestChangeSize( + ampAd, + maxHeight, + /* newWidth */ undefined + ); + return ampAd; +} + +/** + * @param {string} slot + * @param {Array} bannerSizes + * @param {!AmpElement} apesterElement + * @param {!JsonObject} rtcConfig + * @return {!Element} + */ +export function constructBottomAd( + slot, + apesterElement, + rtcConfig +) { + const height = 50; + const ampAd = createElementWithAttributes( + /** @type {!Document} */ (apesterElement.ownerDocument), + 'amp-ad', + { + 'width': '300', + 'height': `${height}`, + 'type': 'doubleclick', + 'layout': 'fixed', + 'data-slot': `${slot}`, + 'data-multi-size-validation': 'false', + 'data-enable-refresh': `${defaultRefreshTime}`, + } + ); + if (rtcConfig) { + ampAd.setAttribute('rtc-config', JSON.stringify(rtcConfig)); + } + ampAd.classList.add('i-amphtml-amp-apester-bottom-ad'); + apesterElement.appendChild(ampAd); + Services.mutatorForDoc(apesterElement).requestChangeSize(ampAd, height); + return ampAd; +} + + +/** + * @param {!AmpElement} adWrap + * @param {!AmpElement} progressBar + * @param {!JsonObject} refreshOptions + */ +function showInUnitAd(adWrap, progressBar, refreshOptions) { + const {skipTimer, timeInView} = refreshOptions; + const showTime = timeInView || skipTimer; + adWrap.classList.add('active'); + setStyle(progressBar, 'animation', `progress ${showTime}s linear 1`); + const timer = setTimeout(() => { + adWrap.classList.remove('active'); + clearTimeout(timer); + }, showTime * 1000); +} + +/** + * @param {string} mediaType + * @param {string} aniviewPlayerIdOrAdUnit + * @param {!AmpElement} apesterElement + * @param {!JsonObject} consentObj + * @param {!JsonObject} refreshOptions + * @param {string} publisherId + * @param {!JsonObject} rtcConfig + */ +export function constructInUnitAd(mediaType, aniviewPlayerIdOrAdUnit, apesterElement, consentObj, refreshOptions, publisherId, rtcConfig) { + let size; + let ampAvAd; + const {skipTimer, timeout, timeInView, timeBetweenAds} = refreshOptions; + const refreshTime = ((timeBetweenAds || timeout || 20) + (timeInView || skipTimer)) * 1000; + if (mediaType === AD_TYPES.video) { + size = getCompanionVideoAdSize(apesterElement); + ampAvAd = getAniviewAdElement(apesterElement, size, aniviewPlayerIdOrAdUnit, consentObj, publisherId); + } else { + size = {width: 300, height: 250}; + ampAvAd = createElementWithAttributes( + /** @type {!Document} */ (apesterElement.ownerDocument), + 'amp-ad', + { + 'width': `${size.width}`, + 'height': `${size.height}`, + 'type': 'doubleclick', + 'layout': 'fixed', + 'data-slot': `${aniviewPlayerIdOrAdUnit}`, + 'data-multi-size-validation': 'false', + 'data-enable-refresh': `${refreshTime}`, + } + ); + if (rtcConfig) { + ampAd.setAttribute('rtc-config', JSON.stringify(rtcConfig)); + } + } + + ampAvAd.classList.add('i-amphtml-amp-apester-in-unit'); + + const ampAvAdWrap = createElementWithAttributes( + /** @type {!Document} */ (apesterElement.ownerDocument), + 'div', + {'class': 'i-amphtml-amp-apester-in-unit-wrap'} + ); + + const progressBarWrap = createElementWithAttributes( + /** @type {!Document} */ (apesterElement.ownerDocument), + 'div', + {'class': 'i-amphtml-amp-apester-progress-bar'} + ); + ampAvAdWrap.appendChild(progressBarWrap); + ampAvAdWrap.appendChild(ampAvAd); + apesterElement.appendChild(ampAvAdWrap); + + showInUnitAd(ampAvAdWrap, progressBarWrap, refreshOptions); + setInterval( + () => { + showInUnitAd(ampAvAdWrap, progressBarWrap, refreshOptions); + }, + refreshTime + ); + + Services.mutatorForDoc(apesterElement).requestChangeSize( + ampAvAd, + size.height + ); +} diff --git a/extensions/amp-apester-media/0.1/monetization/adset.js b/extensions/amp-apester-media/0.1/monetization/adset.js new file mode 100644 index 000000000000..d3b065195cb4 --- /dev/null +++ b/extensions/amp-apester-media/0.1/monetization/adset.js @@ -0,0 +1,81 @@ +import {getValueForExpr} from '#core/types/object'; + +import { + constructStaticDisplayAd, + constructStaticAniviewAd, + constructBottomAd, + constructInUnitAd +} from './ads-constructor'; +import {PLACAMENT_POSITIONS, AD_TYPES, defaultBannerSizes, defaultRefreshTime} from './consent-util'; + + +/** + * @param {!JsonObject} media + * @param {!AmpElement} apesterElement + * @param {!JsonObject} consentObj + */ +export function handleAdsetAds(media, apesterElement, consentObj) { + // check if adset settings is set + const adsetData = getValueForExpr( + /**@type {!JsonObject}*/ (media), + 'adsetData' + ); + const adsetPlacements = adsetData?.placements; + if (!adsetPlacements?.length) { + return; + } + const staticSettings = adsetData.settings?.staticAds || {}; + const inUnitSettings = adsetData.settings?.inUnit || {}; + + adsetPlacements.forEach((adPlacement) => { + const adData = adPlacement.ads[0]; + const adProvider = adData?.provider; + const adUnit = adProvider?.adUnit; + const aniviewPlayerId = adProvider?.playerId; + const rtcConfig = adProvider?.rtcConfig; + + // static ads above or below + if ([PLACAMENT_POSITIONS.above, PLACAMENT_POSITIONS.below].includes(adPlacement.type)) { + const isAbove = adPlacement.type === PLACAMENT_POSITIONS.above; + + if (adUnit && adData.mediaType === AD_TYPES.display) { + const refreshInterval = staticSettings?.refresh?.refreshTime || defaultRefreshTime; + const bannerSizes = adProvider.sizes || defaultBannerSizes; + constructStaticDisplayAd( + adUnit, + bannerSizes, + apesterElement, + refreshInterval, + rtcConfig, + isAbove + ); + } else if (aniviewPlayerId && adData.mediaType === AD_TYPES.video) { + constructStaticAniviewAd( + apesterElement, + aniviewPlayerId, + consentObj, + isAbove, + adProvider?.publisherId + ); + } + } + + // bottom Ad + if (adPlacement.type === PLACAMENT_POSITIONS.bottom && adUnit) { + constructBottomAd(adUnit, apesterElement, rtcConfig); + } + + // inUnit Ad + if (adPlacement.type === PLACAMENT_POSITIONS.in_unit && (adUnit || aniviewPlayerId)) { + constructInUnitAd( + adData.mediaType, + adData.mediaType === AD_TYPES.display ? adUnit : aniviewPlayerId, + apesterElement, + consentObj, + inUnitSettings, + adProvider?.publisherId, + rtcConfig + ); + } + }); +} diff --git a/extensions/amp-apester-media/0.1/monetization/bottomAd/bottomAd.js b/extensions/amp-apester-media/0.1/monetization/bottomAd/bottomAd.js index 1845757eb7f3..78691668c016 100644 --- a/extensions/amp-apester-media/0.1/monetization/bottomAd/bottomAd.js +++ b/extensions/amp-apester-media/0.1/monetization/bottomAd/bottomAd.js @@ -1,7 +1,6 @@ -import {createElementWithAttributes} from '#core/dom'; import {getValueForExpr} from '#core/types/object'; -import {Services} from '#service'; +import {constructBottomAd} from '../ads-constructor'; const ALLOWED_AD_PROVIDER = 'gpt'; /** @@ -29,45 +28,6 @@ export function handleBottomAd(media, apesterElement) { bottomAdOptions['videoPlayer'] === ALLOWED_AD_PROVIDER ) { const slot = bottomAdOptions['tag']; - const bannerSizes = [[300, 50]]; - constructCompanionBottomAd(slot, bannerSizes, apesterElement, rtcConfig); + constructBottomAd(slot, apesterElement, rtcConfig); } } - -/** - * @param {string} slot - * @param {Array} bannerSizes - * @param {!AmpElement} apesterElement - * @param {!JsonObject} rtcConfig - * @return {!Element} - */ -function constructCompanionBottomAd( - slot, - bannerSizes, - apesterElement, - rtcConfig -) { - const width = bannerSizes[0][0]; - const height = bannerSizes[0][1]; - const refreshInterval = 30; - const ampAd = createElementWithAttributes( - /** @type {!Document} */ (apesterElement.ownerDocument), - 'amp-ad', - { - 'width': `${width}`, - 'height': `${height}`, - 'type': 'doubleclick', - 'layout': 'fixed', - 'data-slot': `${slot}`, - 'data-multi-size-validation': 'false', - 'data-enable-refresh': `${refreshInterval}`, - } - ); - if (rtcConfig) { - ampAd.setAttribute('rtc-config', JSON.stringify(rtcConfig)); - } - ampAd.classList.add('i-amphtml-amp-apester-bottom-ad'); - apesterElement.appendChild(ampAd); - Services.mutatorForDoc(apesterElement).requestChangeSize(ampAd, height); - return ampAd; -} diff --git a/extensions/amp-apester-media/0.1/monetization/companion/display.js b/extensions/amp-apester-media/0.1/monetization/companion/display.js index e73c4924ef1e..643a1b9048c7 100644 --- a/extensions/amp-apester-media/0.1/monetization/companion/display.js +++ b/extensions/amp-apester-media/0.1/monetization/companion/display.js @@ -1,7 +1,7 @@ -import {createElementWithAttributes} from '#core/dom'; import {getValueForExpr} from '#core/types/object'; -import {Services} from '#service'; +import {constructStaticDisplayAd} from '../ads-constructor'; +import {defaultBannerSizes} from '../consent-util'; const ALLOWED_AD_PROVIDER = 'gdt'; /** @@ -37,9 +37,8 @@ export function handleCompanionDisplay(media, apesterElement) { const slot = settings['slot']; const refreshInterval = settings['options']['autoRefreshTime'] === 60000 ? 60 : 30; - const defaultBannerSizes = [[300, 250]]; const bannerSizes = settings['bannerSizes'] || defaultBannerSizes; - constructCompanionDisplayAd( + constructStaticDisplayAd( slot, bannerSizes, apesterElement, @@ -48,55 +47,3 @@ export function handleCompanionDisplay(media, apesterElement) { ); } } - -/** - * @param {string} slot - * @param {Array} bannerSizes - * @param {!AmpElement} apesterElement - * @param {number} refreshInterval - * @param {!JsonObject} rtcConfig - * @return {!Element} - */ -function constructCompanionDisplayAd( - slot, - bannerSizes, - apesterElement, - refreshInterval, - rtcConfig -) { - const maxWidth = Math.max.apply( - null, - bannerSizes.map((s) => s[0]) - ); - const maxHeight = Math.max.apply( - null, - bannerSizes.map((s) => s[1]) - ); - - const multiSizeData = bannerSizes.map((size) => size.join('x')).join(); - const ampAd = createElementWithAttributes( - /** @type {!Document} */ (apesterElement.ownerDocument), - 'amp-ad', - { - 'width': `${maxWidth}`, - 'height': `${maxHeight}`, - 'type': 'doubleclick', - 'layout': 'fixed', - 'data-slot': `${slot}`, - 'data-multi-size-validation': 'false', - 'data-multi-size': multiSizeData, - 'data-enable-refresh': `${refreshInterval}`, - } - ); - if (rtcConfig) { - ampAd.setAttribute('rtc-config', JSON.stringify(rtcConfig)); - } - ampAd.classList.add('i-amphtml-amp-apester-companion'); - apesterElement.parentNode.insertBefore(ampAd, apesterElement.nextSibling); - Services.mutatorForDoc(apesterElement).requestChangeSize( - ampAd, - maxHeight, - /* newWidth */ undefined - ); - return ampAd; -} diff --git a/extensions/amp-apester-media/0.1/monetization/companion/video.js b/extensions/amp-apester-media/0.1/monetization/companion/video.js index b8d43821b1fb..18dc51f1c3f3 100644 --- a/extensions/amp-apester-media/0.1/monetization/companion/video.js +++ b/extensions/amp-apester-media/0.1/monetization/companion/video.js @@ -3,6 +3,11 @@ import {getValueForExpr} from '#core/types/object'; import {Services} from '#service'; +import { + constructStaticAniviewAd, + getCompanionVideoAdSize, +} from '../ads-constructor'; + /** * @param {!JsonObject} media * @param {!AmpElement} apesterElement @@ -53,14 +58,14 @@ export function handleCompanionVideo(media, apesterElement, consentObj) { } case 'aniview': { const {playerOptions = {}} = videoSettings; - if (!playerOptions.aniviewChannelId) { + if (!playerOptions.aniviewPlayerId) { return; } - addCompanionAvElement( - playerOptions, - position, + constructStaticAniviewAd( apesterElement, - consentObj + playerOptions.aniviewPlayerId, + consentObj, + position === 'above' ); break; } @@ -89,53 +94,6 @@ function getCompanionPosition(video) { return null; } -/** - * @param {!JsonObject} playerOptions - * @param {string} position - * @param {!AmpElement} apesterElement - * @param {!JsonObject} consentObj - */ -function addCompanionAvElement( - playerOptions, - position, - apesterElement, - consentObj -) { - const size = getCompanionVideoAdSize(apesterElement); - const ampAvAd = createElementWithAttributes( - /** @type {!Document} */ (apesterElement.ownerDocument), - 'amp-iframe', - { - 'scrolling': 'no', - 'id': 'amp-iframe', - 'title': 'Ads', - 'layout': 'responsive', - 'sandbox': 'allow-scripts allow-same-origin allow-popups', - 'allowfullscreen': 'false', - 'frameborder': '0', - 'width': size.width, - 'height': size.height, - 'src': `https://player.avplayer.com/amp/ampiframe.html?AV_TAGID=${playerOptions.aniviewPlayerId}&AV_PUBLISHERID=5fabb425e5d4cb4bbc0ca7e4`, - } - ); - - if (consentObj['gdpr']) { - ampAvAd['data-av_gdpr'] = consentObj['gdpr']; - ampAvAd['data-av_consent'] = consentObj['user_consent']; - } - - ampAvAd.classList.add('i-amphtml-amp-apester-companion'); - - const relativeElement = - position === 'below' ? apesterElement.nextSibling : apesterElement; - apesterElement.parentNode.insertBefore(ampAvAd, relativeElement); - - Services.mutatorForDoc(apesterElement).requestChangeSize( - ampAvAd, - size.height - ); -} - /** * @param {string} videoTag * @param {string} position @@ -174,17 +132,6 @@ function addCompanionSrElement(videoTag, position, macros, apesterElement) { ); } -/** - * @param {!AmpElement} apesterElement - * @return {{height: number, width: number}} - */ -export function getCompanionVideoAdSize(apesterElement) { - const adWidth = apesterElement./*REVIEW*/ clientWidth; - const adRatio = 0.6; - const adHeight = Math.ceil(adWidth * adRatio); - return {width: adWidth, height: adHeight}; -} - /** * @param {!JsonObject} interactionModel * @param {?string} campaignId diff --git a/extensions/amp-apester-media/0.1/monetization/consent-util.js b/extensions/amp-apester-media/0.1/monetization/consent-util.js index 6032f9e4164e..889dc6f99d2a 100644 --- a/extensions/amp-apester-media/0.1/monetization/consent-util.js +++ b/extensions/amp-apester-media/0.1/monetization/consent-util.js @@ -9,6 +9,22 @@ import { const TAG = 'amp-apester-media'; +export const PLACAMENT_POSITIONS = { + above: 'co_above', + below: 'co_below', + bottom: 'bottom', + 'in_unit': 'in_unit', +}; + +export const AD_TYPES = { + display: 'display', + video: 'video', +}; + +export const defaultBannerSizes = [[300, 250]]; + +export const defaultRefreshTime = 30; + const AWAIT_TIME_OUT_FOR_RESPONSE = 3000; const awaitPromiseTimeout = () => { diff --git a/extensions/amp-apester-media/0.1/monetization/inUnit/video.js b/extensions/amp-apester-media/0.1/monetization/inUnit/video.js index f2945dee6d64..60cb0c9a1121 100644 --- a/extensions/amp-apester-media/0.1/monetization/inUnit/video.js +++ b/extensions/amp-apester-media/0.1/monetization/inUnit/video.js @@ -1,8 +1,7 @@ -import {createElementWithAttributes} from '#core/dom'; -import {setStyle} from '#core/dom/style'; import {getValueForExpr} from '#core/types/object'; -import {Services} from '#service'; +import {constructInUnitAd} from '../ads-constructor'; +import {AD_TYPES} from '../consent-util'; /** * @param {!JsonObject} media @@ -30,11 +29,13 @@ export function handleInUnitVideo(media, apesterElement, consentObj) { switch (provider.type) { case 'aniview': { const playerOptions = provider.options || {}; - if (!playerOptions.aniviewChannelId) { + const {aniviewPlayerId} = playerOptions; + if (!aniviewPlayerId) { return; } - addAvElement( - playerOptions, + constructInUnitAd( + AD_TYPES.video, + aniviewPlayerId, apesterElement, consentObj, idleOptions.options @@ -45,90 +46,3 @@ export function handleInUnitVideo(media, apesterElement, consentObj) { break; } } - -/** - * @param {!AmpElement} adWrap - * @param {!AmpElement} progressBar - * @param {!JsonObject} idleOptions - */ -function showVideoAd(adWrap, progressBar, idleOptions) { - const {skipTimer} = idleOptions; - adWrap.classList.add('active'); - setStyle(progressBar, 'animation', `progress ${skipTimer}s linear 1`); - const timer = setTimeout(() => { - adWrap.classList.remove('active'); - clearTimeout(timer); - }, skipTimer * 1000); -} - -/** - * @param {!JsonObject} playerOptions - * @param {!AmpElement} apesterElement - * @param {!JsonObject} consentObj - * @param {!JsonObject} idleOptions - */ -function addAvElement(playerOptions, apesterElement, consentObj, idleOptions) { - const size = getCompanionVideoAdSize(apesterElement); - const ampAvAd = createElementWithAttributes( - /** @type {!Document} */ (apesterElement.ownerDocument), - 'amp-iframe', - { - 'scrolling': 'no', - 'id': 'amp-iframe', - 'title': 'Ads', - 'layout': 'responsive', - 'sandbox': 'allow-scripts allow-same-origin allow-popups', - 'allowfullscreen': 'false', - 'frameborder': '0', - 'width': size.width, - 'height': size.height, - 'class': 'i-amphtml-amp-apester-in-unit', - 'src': `https://player.avplayer.com/amp/ampiframe.html?AV_TAGID=${playerOptions.aniviewPlayerId}&AV_PUBLISHERID=5fabb425e5d4cb4bbc0ca7e4`, - } - ); - - if (consentObj['gdpr']) { - ampAvAd['data-av_gdpr'] = consentObj['gdpr']; - ampAvAd['data-av_consent'] = consentObj['user_consent']; - } - - const ampAvAdWrap = createElementWithAttributes( - /** @type {!Document} */ (apesterElement.ownerDocument), - 'div', - {'class': 'i-amphtml-amp-apester-in-unit-wrap'} - ); - - const progressBarWrap = createElementWithAttributes( - /** @type {!Document} */ (apesterElement.ownerDocument), - 'div', - {'class': 'i-amphtml-amp-apester-progress-bar'} - ); - ampAvAdWrap.appendChild(progressBarWrap); - ampAvAdWrap.appendChild(ampAvAd); - apesterElement.appendChild(ampAvAdWrap); - - showVideoAd(ampAvAdWrap, progressBarWrap, idleOptions); - const {skipTimer, timeout} = idleOptions; - setInterval( - () => { - showVideoAd(ampAvAdWrap, progressBarWrap, idleOptions); - }, - (timeout + skipTimer) * 1000 - ); - - Services.mutatorForDoc(apesterElement).requestChangeSize( - ampAvAd, - size.height - ); -} - -/** - * @param {!AmpElement} apesterElement - * @return {{height: number, width: number}} - */ -export function getCompanionVideoAdSize(apesterElement) { - const adWidth = apesterElement./*REVIEW*/ clientWidth; - const adRatio = 0.6; - const adHeight = Math.ceil(adWidth * adRatio); - return {width: adWidth, height: adHeight}; -} diff --git a/extensions/amp-apester-media/0.1/monetization/index.js b/extensions/amp-apester-media/0.1/monetization/index.js index df05f6e8125b..b75311d16790 100644 --- a/extensions/amp-apester-media/0.1/monetization/index.js +++ b/extensions/amp-apester-media/0.1/monetization/index.js @@ -1,3 +1,4 @@ +import {handleAdsetAds} from './adset'; import {handleBottomAd} from './bottomAd/bottomAd'; import {handleCompanionDisplay} from './companion/display'; import {handleCompanionVideo} from './companion/video'; @@ -10,11 +11,19 @@ import {handleInUnitVideo} from './inUnit/video'; * @return {!Promise} */ export function handleAds(media, apesterElement) { - const monetizationSettings = media['campaignData']; - if (monetizationSettings && !monetizationSettings.disabledAmpCompanionAds) { + const companionSettings = media['campaignData']; + const adsetSettings = media['adsetData']; + if ( + (companionSettings && !companionSettings.disabledAmpCompanionAds) || + (adsetSettings && adsetSettings._id) + ) { return getConsentData(apesterElement).then((consentData) => { - handleCompanionDisplay(media, apesterElement); + if (adsetSettings?.placements?.length) { + handleAdsetAds(media, apesterElement, consentData); + return; + } handleCompanionVideo(media, apesterElement, consentData); + handleCompanionDisplay(media, apesterElement); handleBottomAd(media, apesterElement); handleInUnitVideo(media, apesterElement, consentData); }); diff --git a/extensions/amp-apester-media/0.1/test/test-amp-apester-monetization.js b/extensions/amp-apester-media/0.1/test/test-amp-apester-monetization.js index c51d1393f006..26826da4cd07 100644 --- a/extensions/amp-apester-media/0.1/test/test-amp-apester-monetization.js +++ b/extensions/amp-apester-media/0.1/test/test-amp-apester-monetization.js @@ -6,6 +6,7 @@ import { resetServiceForTesting, } from '../../../../src/service-helpers'; import {handleAds} from '../monetization'; +import {AD_TYPES, PLACAMENT_POSITIONS} from '../monetization/consent-util'; describes.realWin( 'amp-apester-media-monetization', @@ -173,14 +174,79 @@ describes.realWin( it('Should show Aniview video for in-unit video', async () => { const media = createCampaignData({inUnitVideo: true}); await handleAds(media, baseElement); - const inUnitVidoe = queryAmpAdAniviewSelector(doc); - expect(inUnitVidoe).to.exist; + const inUnitVideo = queryAmpAdAniviewSelector(doc); + expect(inUnitVideo).to.exist; }); it('Should not show Aniview video for in-unit video', async () => { const media = createCampaignData({inUnitVideo: false}); await handleAds(media, baseElement); - const inUnitVidoe = queryAmpAdAniviewSelector(doc); - expect(inUnitVidoe).to.not.exist; + const inUnitVideo = queryAmpAdAniviewSelector(doc); + expect(inUnitVideo).to.not.exist; + }); + + // ADSET flow + describe('Adset flow', async () => { + it('Should show Aniview video for in-unit video', async () => { + const videoAd = getVideoAd(PLACAMENT_POSITIONS.in_unit); + const media = createAdsetData([videoAd]); + await handleAds(media, baseElement); + const inUnitVideo = queryAmpAdAniviewSelector(doc); + expect(inUnitVideo).to.exist; + }); + it('Should show Aniview video below', async () => { + const videoAd = getVideoAd(PLACAMENT_POSITIONS.below); + const media = createAdsetData([videoAd]); + await handleAds(media, baseElement); + const avAdBelow = queryAmpAdAniviewSelector(doc); + expect(avAdBelow).to.exist; + expect(baseElement.nextSibling).to.be.equal(avAdBelow); + }); + it('Should show display ad in unit', async () => { + const displayAd = getDisplayAd(PLACAMENT_POSITIONS.in_unit); + const media = createAdsetData([displayAd]); + await handleAds(media, baseElement); + const aboveAd = queryAmpAdDisplaySelector(doc); + expect(aboveAd).to.exist; + }); + it('Should show display ad above', async () => { + const displayAd = getDisplayAd(PLACAMENT_POSITIONS.above); + const media = createAdsetData([displayAd]); + await handleAds(media, baseElement); + const aboveAd = queryAmpAdDisplaySelector(doc); + expect(aboveAd).to.exist; + expect(baseElement.previousSibling).to.be.equal(aboveAd); + }); + it('Should show Aniview video below and display above', async () => { + const videoAd = getVideoAd(PLACAMENT_POSITIONS.below); + const displayAd = getDisplayAd(PLACAMENT_POSITIONS.above); + const media = createAdsetData([videoAd, displayAd]); + await handleAds(media, baseElement); + const avAdBelow = queryAmpAdAniviewSelector(doc); + expect(avAdBelow).to.exist; + expect(baseElement.nextSibling).to.be.equal(avAdBelow); + const aboveAd = queryAmpAdDisplaySelector(doc); + expect(aboveAd).to.exist; + expect(baseElement.previousSibling).to.be.equal(aboveAd); + }); + it('Should not have rtc-config attribute if not set in bottom ad', async () => { + const displayAd = getDisplayAd(PLACAMENT_POSITIONS.bottom); + const media = createAdsetData([displayAd]); + await handleAds(media, baseElement); + const bottomAd = queryAmpAdDisplaySelector(doc); + expect(bottomAd.getAttribute('rtc-config')).to.not.exist; + expect(bottomAd).to.exist; + }); + it('Should have rtc-config attribute if set in bottom ad', async () => { + const displayAd = getDisplayAd( + PLACAMENT_POSITIONS.bottom, + testRtcConfig + ); + const media = createAdsetData([displayAd]); + await handleAds(media, baseElement); + const bottomAd = queryAmpAdDisplaySelector(doc); + expect(bottomAd.getAttribute('rtc-config')).to.exist; + expect(bottomAd).to.exist; + }); }); } ); @@ -211,6 +277,7 @@ function createCampaignData({ 'video': { 'playerOptions': { 'aniviewChannelId': '5fad4ac42cd6d91dcb6e50e9', + 'aniviewPlayerId': '5d14c0ded1fb9900016a3118', }, 'videoTag': '5d14c0ded1fb9900016a3118', 'enabled': false, @@ -300,3 +367,63 @@ function createCampaignData({ media.campaignData = campaignData; return media; } + +function getVideoAd(placementPosition) { + return { + 'active': true, + 'type': placementPosition, + 'ads': [ + { + 'mediaType': AD_TYPES.video, + 'provider': { + 'providerName': 'aniview', + 'playerId': '5d14c0ded1fb9900016a3118', + }, + }, + ], + }; +} + +function getDisplayAd(placementPosition, rtcConfig) { + const provider = { + 'providerName': 'gpt', + 'adUnit': '/57806026/Dev_DT_300x250', + }; + if (rtcConfig) { + provider.rtcConfig = rtcConfig; + } + return { + 'active': true, + 'type': placementPosition, + 'ads': [ + { + 'mediaType': AD_TYPES.display, + 'provider': provider, + }, + ], + }; +} + +function createAdsetData(ads) { + const media = {}; + const adsetData = { + '_id': 'testAdsetId', + 'settings': { + 'inUnit': { + 'skipTimer': 5, + 'timeBetweenAds': 20, + 'timeInView': 10, + }, + 'staticAds': { + 'refresh': { + 'option': 'timer', + 'refreshTime': 30, + }, + }, + }, + 'placements': ads || [], + }; + + media.adsetData = adsetData; + return media; +} diff --git a/extensions/amp-apester-media/0.1/utils.js b/extensions/amp-apester-media/0.1/utils.js index 854121cfce16..9eb1930cb8c8 100644 --- a/extensions/amp-apester-media/0.1/utils.js +++ b/extensions/amp-apester-media/0.1/utils.js @@ -64,6 +64,23 @@ function isMobileDevice() { ); } +/** + * Gets the user os. + * @return {string} + */ +export function getOperatingSystem() { + const {userAgent} = navigator; + if (/android/i.test(userAgent)) { + return 'android'; + } + + if (/iPad|iPhone|iPod|iOS/.test(userAgent) && !window.MSStream) { + return 'ios'; + } + + return 'other'; +} + /** * Gets the user platform. * @return {string}