// VPAID support module export default function (playerInstance, options) { const callbacks = { AdStarted: () => playerInstance.onStartVpaidAd, AdStopped: () => playerInstance.onStopVpaidAd, AdSkipped: () => playerInstance.onSkipVpaidAd, AdLoaded: () => playerInstance.onVpaidAdLoaded, AdLinearChange: () => playerInstance.onVpaidAdLinearChange, AdSizeChange: () => playerInstance.onVpaidAdSizeChange, AdExpandedChange: () => playerInstance.onVpaidAdExpandedChange, AdSkippableStateChange: () => playerInstance.onVpaidAdSkippableStateChange, AdDurationChange: () => playerInstance.onVpaidAdDurationChange, AdRemainingTimeChange: () => playerInstance.onVpaidAdRemainingTimeChange, AdVolumeChange: () => playerInstance.onVpaidAdVolumeChange, AdImpression: () => playerInstance.onVpaidAdImpression, AdClickThru: () => playerInstance.onVpaidAdClickThru, AdInteraction: () => playerInstance.onVpaidAdInteraction, AdVideoStart: () => playerInstance.onVpaidAdVideoStart, AdVideoFirstQuartile: () => playerInstance.onVpaidAdVideoFirstQuartile, AdVideoMidpoint: () => playerInstance.onVpaidAdVideoMidpoint, AdVideoThirdQuartile: () => playerInstance.onVpaidAdVideoThirdQuartile, AdVideoComplete: () => playerInstance.onVpaidAdVideoComplete, AdUserAcceptInvitation: () => playerInstance.onVpaidAdUserAcceptInvitation, AdUserMinimize: () => playerInstance.onVpaidAdUserMinimize, AdUserClose: () => playerInstance.onVpaidAdUserClose, AdPaused: () => playerInstance.onVpaidAdPaused, AdPlaying: () => playerInstance.onVpaidAdPlaying, AdError: () => playerInstance.onVpaidAdError, AdLog: () => playerInstance.onVpaidAdLog }; playerInstance.checkVPAIDInterface = (vpaidAdUnit) => { const VPAIDCreative = vpaidAdUnit; // checks if all the mandatory params present return !!(VPAIDCreative.handshakeVersion && typeof VPAIDCreative.handshakeVersion == "function" && VPAIDCreative.initAd && typeof VPAIDCreative.initAd == "function" && VPAIDCreative.startAd && typeof VPAIDCreative.startAd == "function" && VPAIDCreative.stopAd && typeof VPAIDCreative.stopAd == "function" && VPAIDCreative.skipAd && typeof VPAIDCreative.skipAd == "function" && VPAIDCreative.resizeAd && typeof VPAIDCreative.resizeAd == "function" && VPAIDCreative.pauseAd && typeof VPAIDCreative.pauseAd == "function" && VPAIDCreative.resumeAd && typeof VPAIDCreative.resumeAd == "function" && VPAIDCreative.expandAd && typeof VPAIDCreative.expandAd == "function" && VPAIDCreative.collapseAd && typeof VPAIDCreative.collapseAd == "function" && VPAIDCreative.subscribe && typeof VPAIDCreative.subscribe == "function" && VPAIDCreative.unsubscribe && typeof VPAIDCreative.unsubscribe == "function"); }; // Callback for AdPaused playerInstance.onVpaidAdPaused = () => { playerInstance.vpaidTimeoutTimerClear(); playerInstance.debugMessage("onAdPaused"); }; // Callback for AdPlaying playerInstance.onVpaidAdPlaying = () => { playerInstance.vpaidTimeoutTimerClear(); playerInstance.debugMessage("onAdPlaying"); }; // Callback for AdError playerInstance.onVpaidAdError = (message) => { playerInstance.debugMessage("onAdError: " + message); playerInstance.vpaidTimeoutTimerClear(); playerInstance.onVpaidEnded(); }; // Callback for AdLog playerInstance.onVpaidAdLog = (message) => { playerInstance.debugMessage("onAdLog: " + message); }; // Callback for AdUserAcceptInvitation playerInstance.onVpaidAdUserAcceptInvitation = () => { playerInstance.debugMessage("onAdUserAcceptInvitation"); }; // Callback for AdUserMinimize playerInstance.onVpaidAdUserMinimize = () => { playerInstance.debugMessage("onAdUserMinimize"); }; // Callback for AdUserClose playerInstance.onVpaidAdUserClose = () => { playerInstance.debugMessage("onAdUserClose"); }; // Callback for AdUserClose playerInstance.onVpaidAdSkippableStateChange = () => { if (!playerInstance.vpaidAdUnit) { return; } playerInstance.debugMessage("Ad Skippable State Changed to: " + playerInstance.vpaidAdUnit.getAdSkippableState()); }; // Callback for AdUserClose playerInstance.onVpaidAdExpandedChange = () => { if (!playerInstance.vpaidAdUnit) { return; } playerInstance.debugMessage("Ad Expanded Changed to: " + playerInstance.vpaidAdUnit.getAdExpanded()); }; // Pass through for getAdExpanded playerInstance.getVpaidAdExpanded = () => { playerInstance.debugMessage("getAdExpanded"); if (!playerInstance.vpaidAdUnit) { return; } return playerInstance.vpaidAdUnit.getAdExpanded(); }; // Pass through for getAdSkippableState playerInstance.getVpaidAdSkippableState = () => { playerInstance.debugMessage("getAdSkippableState"); if (!playerInstance.vpaidAdUnit) { return; } return playerInstance.vpaidAdUnit.getAdSkippableState(); }; // Callback for AdSizeChange playerInstance.onVpaidAdSizeChange = () => { if (!playerInstance.vpaidAdUnit) { return; } playerInstance.debugMessage("Ad size changed to: w=" + playerInstance.vpaidAdUnit.getAdWidth() + " h=" + playerInstance.vpaidAdUnit.getAdHeight()); }; // Callback for AdDurationChange playerInstance.onVpaidAdDurationChange = () => { if (!playerInstance.vpaidAdUnit) { return; } playerInstance.debugMessage("Ad Duration Changed to: " + playerInstance.vpaidAdUnit.getAdDuration()); }; // Callback for AdRemainingTimeChange playerInstance.onVpaidAdRemainingTimeChange = () => { if (!playerInstance.vpaidAdUnit) { return; } playerInstance.debugMessage("Ad Remaining Time Changed to: " + playerInstance.vpaidAdUnit.getAdRemainingTime()); }; // Pass through for getAdRemainingTime playerInstance.getVpaidAdRemainingTime = () => { playerInstance.debugMessage("getAdRemainingTime"); if (!playerInstance.vpaidAdUnit) { return; } return playerInstance.vpaidAdUnit.getAdRemainingTime(); }; // Callback for AdImpression playerInstance.onVpaidAdImpression = () => { playerInstance.debugMessage("Ad Impression"); //Announce the impressions playerInstance.trackSingleEvent('impression'); }; // Callback for AdClickThru playerInstance.onVpaidAdClickThru = (url, id, playerHandles) => { playerInstance.debugMessage("Clickthrough portion of the ad was clicked"); // if playerHandles flag is set to true // then player need to open click thorough url in new window if (playerHandles) { window.open(playerInstance.vastOptions.clickthroughUrl); } playerInstance.pauseVpaidAd(); // fire click tracking playerInstance.callUris(playerInstance.vastOptions.clicktracking); }; // Callback for AdInteraction playerInstance.onVpaidAdInteraction = (id) => { playerInstance.debugMessage("A non-clickthrough event has occured"); }; // Callback for AdVideoStart playerInstance.onVpaidAdVideoStart = () => { playerInstance.debugMessage("Video 0% completed"); playerInstance.trackSingleEvent('start'); }; // Callback for AdUserClose playerInstance.onVpaidAdVideoFirstQuartile = () => { playerInstance.debugMessage("Video 25% completed"); playerInstance.trackSingleEvent('firstQuartile'); }; // Callback for AdUserClose playerInstance.onVpaidAdVideoMidpoint = () => { playerInstance.debugMessage("Video 50% completed"); playerInstance.trackSingleEvent('midpoint'); }; // Callback for AdUserClose playerInstance.onVpaidAdVideoThirdQuartile = () => { playerInstance.debugMessage("Video 75% completed"); playerInstance.trackSingleEvent('thirdQuartile'); }; // Callback for AdVideoComplete playerInstance.onVpaidAdVideoComplete = () => { playerInstance.debugMessage("Video 100% completed"); playerInstance.trackSingleEvent('complete'); }; // Callback for AdLinearChange playerInstance.onVpaidAdLinearChange = () => { const vpaidNonLinearSlot = playerInstance.domRef.wrapper.getElementsByClassName("fluid_vpaidNonLinear_ad")[0]; const closeBtn = playerInstance.domRef.wrapper.querySelector('.close_button'); const adListId = vpaidNonLinearSlot.getAttribute('adlistid'); playerInstance.debugMessage("Ad linear has changed: " + playerInstance.vpaidAdUnit.getAdLinear()); if (!playerInstance.vpaidAdUnit.getAdLinear()) { return; } playerInstance.backupMainVideoContentTime(adListId.split('_')[0]); playerInstance.isCurrentlyPlayingAd = true; if (closeBtn) { closeBtn.remove(); } vpaidNonLinearSlot.className = 'fluid_vpaid_slot'; playerInstance.domRef.player.loop = false; playerInstance.domRef.player.removeAttribute('controls'); const progressbarContainer = playerInstance.domRef.player.parentNode.getElementsByClassName('fluid_controls_currentprogress'); for (let i = 0; i < progressbarContainer.length; i++) { progressbarContainer[i].style.backgroundColor = playerInstance.displayOptions.layoutControls.adProgressColor; } playerInstance.toggleLoader(false); }; // Pass through for getAdLinear playerInstance.getVpaidAdLinear = () => { playerInstance.debugMessage("getAdLinear"); return playerInstance.vpaidAdUnit.getAdLinear(); }; // Pass through for startAd() playerInstance.startVpaidAd = () => { playerInstance.debugMessage("startAd"); playerInstance.vpaidTimeoutTimerStart(); playerInstance.vpaidAdUnit.startAd(); }; // Callback for AdLoaded playerInstance.onVpaidAdLoaded = () => { playerInstance.debugMessage("ad has been loaded"); // start the video play as vpaid is loaded successfully playerInstance.vpaidTimeoutTimerClear(); playerInstance.startVpaidAd(); }; // Callback for StartAd() playerInstance.onStartVpaidAd = () => { playerInstance.debugMessage("Ad has started"); playerInstance.vpaidTimeoutTimerClear(); }; // Pass through for stopAd() playerInstance.stopVpaidAd = () => { playerInstance.vpaidTimeoutTimerStart(); playerInstance.vpaidAdUnit.stopAd(); }; // Hard Pass through for stopAd() excluding deleteOtherVpaidAdsApart playerInstance.hardStopVpaidAd = (ad) => { // this is hard stop of vpaid ads // we delete all the vpaid assets so the new one can be loaded // delete all assets apart from the ad from deleteOtherVpaidAdsApart if (playerInstance.vpaidAdUnit) { playerInstance.vpaidAdUnit.stopAd(); playerInstance.vpaidAdUnit = null; } const vpaidIframes = playerInstance.domRef.wrapper.getElementsByClassName("fluid_vpaid_iframe"); const vpaidSlots = playerInstance.domRef.wrapper.getElementsByClassName("fluid_vpaid_slot"); const vpaidNonLinearSlots = playerInstance.domRef.wrapper.getElementsByClassName("fluid_vpaidNonLinear_ad"); for (let i = 0; i < vpaidIframes.length; i++) { if (vpaidIframes[i].getAttribute('adListId') !== ad.id) { vpaidIframes[i].remove(); } } for (let j = 0; j < vpaidSlots.length; j++) { if (vpaidSlots[j].getAttribute('adListId') !== ad.id) { vpaidSlots[j].remove(); } } for (let k = 0; k < vpaidNonLinearSlots.length; k++) { if (vpaidNonLinearSlots[k].getAttribute('adListId') !== ad.id) { vpaidNonLinearSlots[k].remove(); } } }; // Callback for AdUserClose playerInstance.onStopVpaidAd = () => { playerInstance.debugMessage("Ad has stopped"); playerInstance.vpaidTimeoutTimerClear(); playerInstance.onVpaidEnded(); }; // Callback for AdUserClose playerInstance.onSkipVpaidAd = () => { playerInstance.debugMessage("Ad was skipped"); playerInstance.vpaidTimeoutTimerClear(); playerInstance.onVpaidEnded(); }; // Passthrough for skipAd playerInstance.skipVpaidAd = () => { playerInstance.vpaidTimeoutTimerStart(); if (!playerInstance.vpaidAdUnit) { return; } playerInstance.vpaidAdUnit.skipAd() playerInstance.vpaidTimeoutTimerClear(); playerInstance.onVpaidEnded(); }; // Passthrough for setAdVolume playerInstance.setVpaidAdVolume = (val) => { if (!playerInstance.vpaidAdUnit) { return; } playerInstance.vpaidAdUnit.setAdVolume(val); }; // Passthrough for getAdVolume playerInstance.getVpaidAdVolume = () => { if (!playerInstance.vpaidAdUnit) { return; } return playerInstance.vpaidAdUnit.getAdVolume(); }; // Callback for AdVolumeChange playerInstance.onVpaidAdVolumeChange = () => { if (!playerInstance.vpaidAdUnit) { return; } playerInstance.debugMessage("Ad Volume has changed to - " + playerInstance.vpaidAdUnit.getAdVolume()); }; playerInstance.resizeVpaidAuto = () => { if (playerInstance.vastOptions !== null && playerInstance.vastOptions.vpaid && playerInstance.vastOptions.linear) { const adWidth = playerInstance.domRef.player.offsetWidth; const adHeight = playerInstance.domRef.player.offsetHeight; const mode = (playerInstance.fullscreenMode ? 'fullscreen' : 'normal'); playerInstance.resizeVpaidAd(adWidth, adHeight, mode); } }; // Passthrough for resizeAd playerInstance.resizeVpaidAd = (width, height, viewMode) => { if (!playerInstance.vpaidAdUnit) { return; } playerInstance.vpaidAdUnit.resizeAd(width, height, viewMode); }; // Passthrough for pauseAd() playerInstance.pauseVpaidAd = () => { playerInstance.vpaidTimeoutTimerStart(); if (!playerInstance.vpaidAdUnit) { return; } playerInstance.vpaidAdUnit.pauseAd(); }; // Passthrough for resumeAd() playerInstance.resumeVpaidAd = () => { playerInstance.vpaidTimeoutTimerStart(); if (!playerInstance.vpaidAdUnit) { return; } playerInstance.vpaidAdUnit.resumeAd(); }; // Passthrough for expandAd() playerInstance.expandVpaidAd = () => { if (!playerInstance.vpaidAdUnit) { return; } playerInstance.vpaidAdUnit.expandAd(); }; // Passthrough for collapseAd() playerInstance.collapseVpaidAd = () => { if (!playerInstance.vpaidAdUnit) { return; } playerInstance.vpaidAdUnit.collapseAd(); }; playerInstance.vpaidTimeoutTimerClear = () => { if (playerInstance.vpaidTimer) { clearTimeout(playerInstance.vpaidTimer); } }; // placeholder for timer function playerInstance.vpaidTimeoutTimerStart = () => { // clear previous timer if any playerInstance.vpaidTimeoutTimerClear(); playerInstance.vpaidTimer = setTimeout(function () { playerInstance.announceLocalError('901'); playerInstance.onVpaidEnded(); }, playerInstance.displayOptions.vastOptions.vpaidTimeout); }; playerInstance.vpaidCallbackListenersAttach = () => { // The key of the object is the event name and the value is a reference to the callback function that is registered with the creative // Looping through the object and registering each of the callbacks with the creative for (let eventName in callbacks) { playerInstance.vpaidAdUnit.subscribe(callbacks[eventName](), eventName, playerInstance); } }; playerInstance.vpaidCallbackListenersDetach = () => { if (!playerInstance.vpaidAdUnit) { return; } for (let eventName in callbacks) { playerInstance.vpaidAdUnit.unsubscribe(callbacks[eventName](), eventName, playerInstance); } }; playerInstance.loadVpaid = (ad, vpaidJsUrl) => { const vpaidIframe = document.createElement('iframe'); vpaidIframe.id = "fp_" + ad.id + "_fluid_vpaid_iframe"; vpaidIframe.className = 'fluid_vpaid_iframe'; vpaidIframe.setAttribute('adListId', ad.id); vpaidIframe.setAttribute('frameborder', '0'); playerInstance.domRef.player.parentNode.insertBefore(vpaidIframe, playerInstance.domRef.player.nextSibling); const vpaidJsScriptElement = document.createElement('script'); vpaidJsScriptElement.src = vpaidJsUrl; vpaidIframe.contentWindow.document.head.append(vpaidJsScriptElement); // set interval with timeout playerInstance.tempVpaidCounter = 0; playerInstance.getVPAIDAdInterval = setInterval(function () { if (vpaidIframe && vpaidIframe.contentWindow) { const fn = vpaidIframe.contentWindow['getVPAIDAd']; // check if JS is loaded fully in iframe if (fn && typeof fn == 'function') { if (playerInstance.vpaidAdUnit) { playerInstance.hardStopVpaidAd(ad); } playerInstance.vpaidAdUnit = fn(); clearInterval(playerInstance.getVPAIDAdInterval); if (playerInstance.checkVPAIDInterface(playerInstance.vpaidAdUnit)) { if (playerInstance.getVpaidAdLinear()) { playerInstance.isCurrentlyPlayingAd = true; playerInstance.switchPlayerToVpaidMode(ad); } else { playerInstance.debugMessage('non linear vpaid ad is loaded'); playerInstance.loadVpaidNonlinearAssets(ad); } } } else { // video player will wait for 2seconds if vpaid is not loaded, then it will declare vast error and move ahead playerInstance.tempVpaidCounter++; if (playerInstance.tempVpaidCounter >= 20) { clearInterval(playerInstance.getVPAIDAdInterval); playerInstance.rollsById[ad.rollListId].error = true; playerInstance.playMainVideoWhenVpaidFails(403); return false; } else { playerInstance.debugMessage(playerInstance.tempVpaidCounter); } } } }, 100); playerInstance.destructors.push(() => clearInterval(playerInstance.getVPAIDAdInterval)); }; playerInstance.onVpaidEnded = (event) => { if (event) { event.stopImmediatePropagation(); } if (!playerInstance.vpaidAdUnit) { return; } const vpaidSlot = playerInstance.domRef.wrapper.querySelector('.fluid_vpaid_slot'); playerInstance.vpaidCallbackListenersDetach(); playerInstance.vpaidAdUnit = null; clearInterval(playerInstance.getVPAIDAdInterval); if (!!vpaidSlot) { vpaidSlot.remove(); } playerInstance.checkForNextAd(); }; playerInstance.playMainVideoWhenVpaidFails = (errorCode) => { const vpaidSlot = playerInstance.domRef.wrapper.querySelector('.fluid_vpaid_slot'); if (vpaidSlot) { vpaidSlot.remove(); } clearInterval(playerInstance.getVPAIDAdInterval); playerInstance.playMainVideoWhenVastFails(errorCode); }; }