fluid-player
Version:
Fluid Player is a free HTML5 video player
1,230 lines (1,010 loc) • 75 kB
JavaScript
import { displayModes, trackingEventTypes } from '../constants/constants';
export default function (playerInstance, options) {
const VPAID_VERSION = '2.0';
const {
muteEvent,
unmuteEvent,
pauseEvent,
resumeEvent,
rewindEvent,
fullscreenEvent,
skipEvent,
playerExpandEvent,
playerCollapseEvent,
impressionEvent,
viewImpressionEvent,
startEvent,
firstQuartileEvent,
midpointEvent,
thirdQuartileEvent,
completeEvent,
progressEvent,
creativeViewEvent,
collapseEvent,
adCollapseEvent,
closeEvent,
acceptInvitationEvent
} = trackingEventTypes;
playerInstance.renderLinearAd = (ad, backupTheVideoTime) => {
playerInstance.toggleLoader(false);
// Reset live indicator
playerInstance.hideLiveIndicator();
playerInstance.isLiveStream = null;
//get the proper ad
playerInstance.vastOptions = ad;
if (backupTheVideoTime) {
playerInstance.backupMainVideoContentTime(ad.rollListId);
}
const playVideoPlayer = ad => {
playerInstance.switchPlayerToVpaidMode = ad => {
playerInstance.debugMessage('starting function switchPlayerToVpaidMode');
const vpaidIframe = "fp_" + ad.id + "_fluid_vpaid_iframe";
const creativeData = {};
creativeData.AdParameters = ad.adParameters;
const slotElement = document.createElement('div');
slotElement.className = 'fluid_vpaid_slot';
slotElement.setAttribute('adListId', ad.id);
playerInstance.domRef.player.parentNode.insertBefore(slotElement, vpaidIframe.nextSibling);
const environmentVars = {
slot: slotElement,
videoSlot: playerInstance.domRef.player,
videoSlotCanAutoPlay: true
};
// calls this functions after ad unit is loaded in iframe
const ver = playerInstance.vpaidAdUnit.handshakeVersion(VPAID_VERSION);
const compare = playerInstance.compareVersion(VPAID_VERSION, ver);
if (compare === 1) {
//VPAID version of ad is lower than we need
ad.error = true;
playerInstance.playMainVideoWhenVpaidFails(403);
return false;
}
if (playerInstance.vastOptions.skipoffset !== false) {
playerInstance.addSkipButton();
}
playerInstance.domRef.player.loop = false;
playerInstance.domRef.player.removeAttribute('controls'); //Remove the default Controls
playerInstance.vpaidCallbackListenersAttach();
const mode = (playerInstance.fullscreenMode ? displayModes.FULLSCREEN : displayModes.NORMAL);
const adWidth = playerInstance.domRef.player.offsetWidth;
const adHeight = playerInstance.domRef.player.offsetHeight;
playerInstance.vpaidAdUnit.initAd(adWidth, adHeight, mode, 3000, creativeData, environmentVars);
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);
ad.played = true;
playerInstance.adFinished = false;
};
playerInstance.switchPlayerToVastMode = () => {
// Get the actual duration from the video file if it is not present in the VAST XML
if (!playerInstance.vastOptions.duration) {
playerInstance.vastOptions.duration = selectedMediaFile.delivery === 'streaming' ?
Infinity : playerInstance.domRef.player.duration;
}
if (playerInstance.displayOptions.layoutControls.showCardBoardView) {
if (!ad.landingPage) {
playerInstance.addCTAButton(ad.clickthroughUrl);
} else {
playerInstance.addCTAButton(ad.landingPage);
}
} else {
let idAdClickable = [undefined, true]
.includes(playerInstance.displayOptions.vastOptions.adClickable);
if (playerInstance.rollsById[ad.rollListId].adClickable !== undefined) {
idAdClickable = playerInstance.rollsById[ad.rollListId].adClickable;
}
if (idAdClickable) {
playerInstance.addClickthroughLayer();
}
playerInstance.addCTAButton(ad.landingPage);
}
if (playerInstance.vastOptions.skipoffset !== false) {
playerInstance.addSkipButton();
}
playerInstance.domRef.player.loop = false;
playerInstance.addAdCountdown();
playerInstance.domRef.player.removeAttribute('controls'); //Remove the default Controls
playerInstance.vastLogoBehaviour(true);
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;
}
if (playerInstance.rollsById[ad.rollListId].adText || ad.adText) {
const adTextToShow = ad.adText ? ad.adText : playerInstance.rollsById[ad.rollListId].adText;
playerInstance.addAdPlayingText(adTextToShow);
}
playerInstance.positionTextElements(ad);
playerInstance.toggleLoader(false);
ad.played = true;
playerInstance.adFinished = false;
playerInstance.domRef.player.play();
//Announce the impressions
playerInstance.trackSingleEvent(impressionEvent);
playerInstance.domRef.player.removeEventListener('loadedmetadata', playerInstance.switchPlayerToVastMode);
// if in vr mode then do not show
if (playerInstance.vrMode) {
const adCountDownTimerText = playerInstance.domRef.wrapper.querySelector('.ad_countdown');
const ctaButton = playerInstance.domRef.wrapper.querySelector('.fluid_ad_cta');
const addAdPlayingTextOverlay = playerInstance.domRef.wrapper.querySelector('.fluid_ad_playing');
const skipBtn = playerInstance.domRef.wrapper.querySelector('.skip_button');
if (adCountDownTimerText) {
adCountDownTimerText.style.display = 'none';
}
if (ctaButton) {
ctaButton.style.display = 'none';
}
if (addAdPlayingTextOverlay) {
addAdPlayingTextOverlay.style.display = 'none';
}
if (skipBtn) {
skipBtn.style.display = 'none';
}
}
};
playerInstance.domRef.player.pause();
// Remove the streaming objects to prevent errors on the VAST content
playerInstance.detachStreamers();
// Try to load multiple
const selectedMediaFile = playerInstance.getSupportedMediaFileObject(playerInstance.vastOptions.mediaFileList);
// if player in cardboard mode then, linear ads media type should be a '360' video
if (playerInstance.displayOptions.layoutControls.showCardBoardView && ad.mediaType !== '360') {
ad.error = true;
playerInstance.playMainVideoWhenVastFails(403);
return false;
}
const isVpaid = playerInstance.vastOptions.vpaid;
if (!isVpaid && selectedMediaFile.isUnsuportedHls) {
import(/* webpackChunkName: "hlsjs" */ 'hls.js').then((it) => {
window.Hls = it.default;
const hls = new Hls({
debug: typeof FP_DEBUG !== 'undefined' && FP_DEBUG === true,
startPosition: 0,
p2pConfig: {
logLevel: false,
},
enableWebVTT: false,
enableCEA708Captions: false,
});
hls.attachMedia(playerInstance.domRef.player);
hls.loadSource(selectedMediaFile.src);
playerInstance.isCurrentlyPlayingAd = true;
hls.once(Hls.Events.LEVEL_LOADED, function (event, level) {
playerInstance.isLiveStream = !!level.details.live;
if (!!level.details.live) {
playerInstance.showLiveIndicator();
} else {
playerInstance.hideLiveIndicator();
}
});
playerInstance.hlsPlayer = hls;
playerInstance.domRef.player.addEventListener('loadedmetadata', playerInstance.switchPlayerToVastMode);
playerInstance.domRef.player.addEventListener('ended', () => {
hls.detachMedia();
hls.destroy();
playerInstance.hlsPlayer = false;
playerInstance.onVastAdEnded();
});
playerInstance.domRef.player.play();
});
} else if (!isVpaid) {
if (selectedMediaFile.src === false) {
// Couldn’t find MediaFile that is supported by this video player, based on the attributes of the MediaFile element.
ad.error = true;
playerInstance.playMainVideoWhenVastFails(403);
return false;
}
playerInstance.domRef.player.addEventListener('loadedmetadata', playerInstance.switchPlayerToVastMode);
playerInstance.domRef.player.src = selectedMediaFile.src;
playerInstance.isCurrentlyPlayingAd = true;
if (playerInstance.displayOptions.vastOptions.showProgressbarMarkers) {
playerInstance.hideAdMarkers();
}
playerInstance.domRef.player.load();
//Handle the ending of the Pre-Roll ad
playerInstance.domRef.player.addEventListener('ended', playerInstance.onVastAdEnded);
} else {
playerInstance.loadVpaid(ad, selectedMediaFile.src);
if (playerInstance.displayOptions.vastOptions.showProgressbarMarkers) {
playerInstance.hideAdMarkers();
}
}
};
/**
* Sends requests to the tracking URIs
*/
const videoPlayerTimeUpdate = () => {
if (playerInstance.adFinished) {
playerInstance.domRef.player.removeEventListener('timeupdate', videoPlayerTimeUpdate);
return;
}
const currentTime = Math.floor(playerInstance.domRef.player.currentTime);
if (playerInstance.vastOptions.duration !== 0) {
playerInstance.scheduleTrackingEvent(currentTime, playerInstance.vastOptions.duration);
}
if (currentTime >= (playerInstance.vastOptions.duration - 1) && playerInstance.vastOptions.duration !== 0) {
playerInstance.domRef.player.removeEventListener('timeupdate', videoPlayerTimeUpdate);
playerInstance.adFinished = true;
}
};
playVideoPlayer(ad);
playerInstance.domRef.player.addEventListener('timeupdate', videoPlayerTimeUpdate);
};
playerInstance.playRoll = (adList) => {
// register all the ad pods
const newPods = [];
for (let i = 0; i < adList.length; i++) {
newPods.push(adList[i]);
}
playerInstance.temporaryAdPods = newPods;
if (playerInstance.vastOptions !== null && playerInstance.vastOptions.adType.toLowerCase() === 'linear') {
return;
}
const adToPlay = playerInstance.getNextAdPod();
if (adToPlay !== null) {
playerInstance.renderLinearAd(adToPlay, true);
}
const roll = playerInstance.rollsById[adToPlay.rollListId].roll;
playerInstance.currentMediaSourceType = roll;
};
playerInstance.backupMainVideoContentTime = (rollListId) => {
const roll = playerInstance.rollsById[rollListId].roll;
//spec configs by roll
switch (roll) {
case 'midRoll':
playerInstance.domRef.player.mainVideoCurrentTime = playerInstance.domRef.player.currentTime - 1;
break;
case 'postRoll':
playerInstance.domRef.player.mainVideoCurrentTime = playerInstance.mainVideoDuration;
playerInstance.autoplayAfterAd = false;
playerInstance.domRef.player.currentTime = playerInstance.mainVideoDuration;
break;
case 'preRoll':
if (playerInstance.domRef.player.currentTime > 0 || playerInstance.domRef.player.safariPlayheadPosition) {
const currentTime = playerInstance.domRef.player.safariPlayheadPosition ?? playerInstance.domRef.player.currentTime;
playerInstance.domRef.player.mainVideoCurrentTime = currentTime - 1;
}
break;
}
};
playerInstance.getSupportedMediaFileObject = (mediaFiles) => {
let selectedMediaFile = null;
let adSupportedType = false;
if (mediaFiles.length) {
for (let i = 0; i < mediaFiles.length; i++) {
if (mediaFiles[i].apiFramework !== 'VPAID') {
const supportLevel = playerInstance.getMediaFileTypeSupportLevel(mediaFiles[i]['type']);
if (supportLevel === 'maybe' || supportLevel === 'probably') {
selectedMediaFile = mediaFiles[i];
adSupportedType = true;
}
//one of the best(s) option, no need to seek more
if (supportLevel === 'probably') {
break;
}
if (
supportLevel === 'no' && mediaFiles[i].delivery === 'streaming' &&
(mediaFiles[i].type === 'application/vnd.apple.mpegurl' || mediaFiles[i].type === 'application/x-mpegURL')
) {
selectedMediaFile = mediaFiles[i];
selectedMediaFile.isUnsuportedHls = true;
adSupportedType = true;
}
} else {
selectedMediaFile = mediaFiles[i];
adSupportedType = true;
break;
}
}
}
if (adSupportedType === false) {
return false;
}
return selectedMediaFile;
};
/**
* Reports how likely it is that the current browser will be able to play media of a given MIME type.
* @return string|null "probably", "maybe", "no" or null
*/
playerInstance.getMediaFileTypeSupportLevel = (mediaType) => {
if (null === mediaType) {
return null;
}
const tmpVideo = document.createElement('video');
let response = tmpVideo.canPlayType(mediaType);
return !response ? "no" : response;
};
playerInstance.scheduleTrackingEvent = (currentTime, duration) => {
if (currentTime === 0) {
playerInstance.trackSingleEvent(startEvent);
playerInstance.observe();
playerInstance.domRef.player.timeInView = 0;
}
// View Impression is defined by IAB as: Watching at least 2 seconds of the video where at least 50% of the ad’s pixels are visible on the screen
if (playerInstance.domRef.player.inView) {
if (playerInstance.domRef.player.timeInView > 2) {
playerInstance.trackSingleEvent(viewImpressionEvent);
} else {
playerInstance.domRef.player.timeInView += currentTime;
}
}
if (typeof playerInstance.vastOptions.tracking.progress !== 'undefined' &&
playerInstance.vastOptions.tracking.progress.length &&
typeof playerInstance.vastOptions.tracking.progress[currentTime] !== 'undefined') {
playerInstance.trackSingleEvent(progressEvent, currentTime);
}
if (typeof playerInstance.vastOptions.tracking.creativeView !== 'undefined' &&
playerInstance.vastOptions.tracking.creativeView.length &&
typeof playerInstance.vastOptions.tracking.creativeView[currentTime] !== 'undefined') {
playerInstance.trackSingleEvent(creativeViewEvent, currentTime);
}
if (currentTime === (Math.floor(duration / 4))) {
playerInstance.trackSingleEvent(firstQuartileEvent);
}
if (currentTime === (Math.floor(duration / 2))) {
playerInstance.trackSingleEvent(midpointEvent);
}
if (currentTime === (Math.floor(duration * 3 / 4))) {
playerInstance.trackSingleEvent(thirdQuartileEvent);
}
if (currentTime >= (duration - 1)) {
playerInstance.trackSingleEvent(completeEvent);
}
};
// ADS
playerInstance.trackSingleEvent = (eventType, eventSubType) => {
if (typeof playerInstance.vastOptions === 'undefined' || playerInstance.vastOptions === null) {
return;
}
let trackingUris = [];
trackingUris.length = 0;
switch (eventType) {
case startEvent:
case firstQuartileEvent:
case midpointEvent:
case thirdQuartileEvent:
case completeEvent:
case closeEvent:
case skipEvent:
case acceptInvitationEvent:
// currently 'acceptInvitation' can only be triggered by VPAID ads
if (playerInstance.vastOptions.stopTracking[eventType] === false) {
if (playerInstance.vastOptions.tracking[eventType] !== null) {
trackingUris = playerInstance.vastOptions.tracking[eventType];
}
playerInstance.vastOptions.stopTracking[eventType] = true;
}
break;
case progressEvent:
playerInstance.vastOptions.tracking.progress[eventSubType].elements.forEach(function (currentValue, index) {
if (
(playerInstance.vastOptions.tracking.progress[eventSubType].stopTracking === false) &&
(playerInstance.vastOptions.tracking.progress[eventSubType].elements.length)
) {
trackingUris = playerInstance.vastOptions.tracking.progress[eventSubType].elements;
}
playerInstance.vastOptions.tracking.progress[eventSubType].stopTracking = true;
});
break;
case creativeViewEvent:
if (!playerInstance.vastOptions.tracking.creativeView || !playerInstance.vastOptions.tracking.creativeView[eventSubType] || playerInstance.vastOptions.tracking.creativeView[eventSubType]?.stopTracking === true) {
break;
}
if (playerInstance.vastOptions.tracking.creativeView[eventSubType]?.elements.length) {
trackingUris = playerInstance.vastOptions.tracking.creativeView[eventSubType].elements;
}
playerInstance.vastOptions.tracking.creativeView[eventSubType].stopTracking = true;
break;
case impressionEvent:
if (
(typeof playerInstance.vastOptions.impression !== 'undefined') &&
(playerInstance.vastOptions.impression !== null) &&
(typeof playerInstance.vastOptions.impression.length !== 'undefined')
) {
trackingUris = playerInstance.vastOptions.impression;
}
break;
case viewImpressionEvent:
if (playerInstance.vastOptions.stopTracking.viewImpression === true) {
break;
}
if (
(typeof playerInstance.vastOptions.viewImpression !== 'undefined') &&
(playerInstance.vastOptions.viewImpression !== null) &&
(typeof playerInstance.vastOptions.viewImpression.length !== 'undefined')
) {
trackingUris = playerInstance.vastOptions.viewImpression;
playerInstance.vastOptions.stopTracking.viewImpression = true;
}
break;
case muteEvent:
case unmuteEvent:
case pauseEvent:
case resumeEvent:
case rewindEvent:
case fullscreenEvent:
case playerExpandEvent:
case playerCollapseEvent:
case collapseEvent:
case adCollapseEvent:
// currently 'collapse'/'adCollapse' can only be triggered by VPAID ads
if (!!playerInstance.vastOptions.tracking[eventType]) {
trackingUris = playerInstance.vastOptions.tracking[eventType];
}
break;
default:
break;
}
playerInstance.callUris(trackingUris);
};
// ADS
playerInstance.completeNonLinearStatic = (ad) => {
playerInstance.closeNonLinear(ad.id);
if (playerInstance.adFinished === false) {
playerInstance.adFinished = true;
playerInstance.isCurrentlyShowingNonLinearAd = false;
playerInstance.trackSingleEvent(completeEvent);
}
clearInterval(playerInstance.nonLinearTracking);
};
// ADS
/**
* Show up a nonLinear static creative
*/
playerInstance.createNonLinearStatic = (ad) => {
//get the proper ad
playerInstance.vastOptions = ad;
playerInstance.createBoard(ad);
if (playerInstance.rollsById[ad.rollListId].error === true || ad.error === true) {
playerInstance.announceLocalError(101);
return;
}
playerInstance.adFinished = false;
playerInstance.isCurrentlyShowingNonLinearAd = true;
let duration = (playerInstance.rollsById[ad.rollListId].nonLinearDuration) ? playerInstance.rollsById[ad.rollListId].nonLinearDuration : false;
if (!playerInstance.vastOptions.vpaid) {
playerInstance.trackSingleEvent(startEvent);
playerInstance.trackSingleEvent(creativeViewEvent, 0);
duration = duration || playerInstance.vastOptions.duration;
playerInstance.nonLinearTracking = setInterval(function () {
if (playerInstance.adFinished === true) {
return;
}
const currentTime = Math.floor(playerInstance.domRef.player.currentTime);
playerInstance.scheduleTrackingEvent(currentTime, duration);
if (currentTime >= (duration - 1)) {
playerInstance.adFinished = true;
playerInstance.isCurrentlyShowingNonLinearAd = false;
}
}, 400);
playerInstance.destructors.push(() => clearInterval(playerInstance.nonLinearTracking));
}
const time = parseInt(playerInstance.getCurrentTime()) + parseInt(duration);
playerInstance.scheduleTask({ time: time, closeStaticAd: ad, rollListId: ad.rollListId });
};
// ADS
playerInstance.createVpaidNonLinearBoard = (ad) => {
// create iframe
// pass the js
playerInstance.loadVpaidNonlinearAssets = function (ad) {
playerInstance.vastOptions = ad;
playerInstance.debugMessage('starting function switchPlayerToVpaidMode');
const vAlign = (ad.vAlign) ? ad.vAlign : playerInstance.nonLinearVerticalAlign;
const showCloseButton = (ad.vpaidNonLinearCloseButton) ? ad.vpaidNonLinearCloseButton : playerInstance.vpaidNonLinearCloseButton;
const vpaidIframe = "fp_" + ad.id + "_fluid_vpaid_iframe";
const creativeData = {};
creativeData.AdParameters = ad.adParameters;
const slotWrapper = document.createElement('div');
slotWrapper.id = 'fluid_vpaidNonLinear_' + ad.id;
slotWrapper.className = 'fluid_vpaidNonLinear_' + vAlign;
slotWrapper.className += ' fluid_vpaidNonLinear_ad';
slotWrapper.setAttribute('adListId', ad.id);
// Default values in case nothing defined in VAST data or ad settings
let adWidth = Math.min(468, playerInstance.domRef.player.offsetWidth);
let adHeight = Math.min(60, Math.floor(playerInstance.domRef.player.offsetHeight / 4));
if (typeof ad.size !== 'undefined') {
const dimensions = ad.size.split('x');
adWidth = dimensions[0];
adHeight = dimensions[1];
} else if (ad.dimension.width && ad.dimension.height) {
adWidth = ad.dimension.width;
adHeight = ad.dimension.height;
}
slotWrapper.style.width = '100%';
slotWrapper.style.height = adHeight + 'px';
let slotFrame;
if (showCloseButton) {
const slotFrame = document.createElement('div');
slotFrame.className = 'fluid_vpaidNonLinear_frame';
slotFrame.style.width = adWidth + 'px';
slotFrame.style.height = adHeight + 'px';
slotWrapper.appendChild(slotFrame);
const closeBtn = document.createElement('div');
closeBtn.className = 'close_button';
closeBtn.innerHTML = '';
closeBtn.title = playerInstance.displayOptions.layoutControls.closeButtonCaption;
const [tempadListId] = ad.id.split('_');
closeBtn.onclick = function (event) {
playerInstance.hardStopVpaidAd('');
playerInstance.trackCloseNonLinearAd();
if (typeof event.stopImmediatePropagation !== 'undefined') {
event.stopImmediatePropagation();
}
playerInstance.adFinished = true;
if (playerInstance.rollsById[tempadListId].roll === 'onPauseRoll') {
// keep new currentOnPauseRollAd or undefined if none available
playerInstance.currentOnPauseRollAd = playerInstance.onPauseRollAdPods[0];
//if any other onPauseRoll then render it
if (playerInstance.onPauseRollAdPods[0]) {
const getNextOnPauseRollAd = playerInstance.onPauseRollAdPods[0];
playerInstance.createBoard(getNextOnPauseRollAd);
delete playerInstance.onPauseRollAdPods[0];
}
}
return false;
};
slotFrame.appendChild(closeBtn);
}
const slotIframe = document.createElement('iframe');
slotIframe.id = playerInstance.videoPlayerId + 'non_linear_vapid_slot_iframe';
slotIframe.className = 'fluid_vpaid_nonlinear_slot_iframe';
slotIframe.setAttribute('width', adWidth + 'px');
slotIframe.setAttribute('height', adHeight + 'px');
slotIframe.setAttribute('sandbox', 'allow-forms allow-pointer-lock allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts');
slotIframe.setAttribute('frameborder', '0');
slotIframe.setAttribute('scrolling', 'no');
slotIframe.setAttribute('marginwidth', '0');
slotIframe.setAttribute('marginheight', '0');
slotWrapper.appendChild(slotIframe);
playerInstance.domRef.player.parentNode.insertBefore(slotWrapper, vpaidIframe.nextSibling);
const slotElement = slotIframe.contentWindow.document.createElement('div');
slotIframe.contentWindow.document.body.appendChild(slotElement);
playerInstance.vastOptions.slotIframe = slotIframe;
playerInstance.vastOptions.slotFrame = slotFrame;
const environmentVars = {
slot: slotElement,
videoSlot: playerInstance.domRef.player,
videoSlotCanAutoPlay: true
};
playerInstance.debugMessage(ad);
// calls this functions after ad unit is loaded in iframe
const ver = playerInstance.vpaidAdUnit.handshakeVersion(VPAID_VERSION);
const compare = playerInstance.compareVersion(VPAID_VERSION, ver);
if (compare === 1) {
//VPAID version of ad is lower than we need
ad.error = true;
playerInstance.playMainVideoWhenVpaidFails(403);
return false;
}
playerInstance.domRef.player.loop = false;
playerInstance.domRef.player.removeAttribute('controls'); //Remove the default Controls
playerInstance.vpaidCallbackListenersAttach();
const mode = (playerInstance.fullscreenMode ? displayModes.FULLSCREEN : displayModes.NORMAL);
playerInstance.vpaidAdUnit.initAd(adWidth, adHeight, mode, 3000, creativeData, environmentVars);
playerInstance.toggleLoader(false);
ad.played = true;
playerInstance.adFinished = false;
};
playerInstance.loadVpaid(ad, ad.staticResource);
playerInstance.debugMessage('create non linear vpaid');
};
// ADS
playerInstance.createNonLinearBoard = (ad) => {
ad.played = true;
const board = document.createElement('div');
const vAlign = (playerInstance.rollsById[ad.rollListId].vAlign) ? playerInstance.rollsById[ad.rollListId].vAlign : playerInstance.nonLinearVerticalAlign;
const creative = new Image();
creative.src = ad.staticResource;
creative.id = 'fluid_nonLinear_imgCreative_' + ad.id + '_' + playerInstance.videoPlayerId;
creative.onerror = function () {
playerInstance.rollsById[ad.rollListId].error = true;
playerInstance.announceError(500);
};
creative.onload = function () {
const playerWidth = playerInstance.domRef.player.clientWidth;
let origWidth;
let origHeight;
let newBannerWidth;
let newBannerHeight;
//Set banner size based on the below priority
// 1. adList -> roll -> size
// 2. VAST XML width/height attriubute (VAST 3.)
// 3. VAST XML static resource dimension
if (typeof playerInstance.rollsById[ad.rollListId].size !== 'undefined') {
origWidth = playerInstance.rollsById[ad.rollListId].size.split('x')[0];
origHeight = playerInstance.rollsById[ad.rollListId].size.split('x')[1];
} else if (ad.dimension.width && ad.dimension.height) {
origWidth = ad.dimension.width;
origHeight = ad.dimension.height;
} else {
origWidth = creative.width;
origHeight = creative.height;
}
if (origWidth > playerWidth) {
newBannerWidth = playerWidth - 5;
newBannerHeight = origHeight * newBannerWidth / origWidth;
} else {
newBannerWidth = origWidth;
newBannerHeight = origHeight;
}
if (playerInstance.rollsById[ad.rollListId].roll !== 'onPauseRoll') {
//Show the board only if media loaded
const nonLinear = playerInstance.domRef.wrapper.querySelector('#fluid_nonLinear_' + ad.id);
if (nonLinear) {
nonLinear.style.display = ''
}
}
const img = playerInstance.domRef.wrapper.querySelector('#' + creative.id);
img.width = newBannerWidth;
img.height = newBannerHeight;
playerInstance.trackSingleEvent(impressionEvent);
};
board.id = 'fluid_nonLinear_' + ad.id;
board.className = 'fluid_nonLinear_' + vAlign;
board.className += ' fluid_nonLinear_ad';
board.innerHTML = creative.outerHTML;
board.style.display = 'none';
//Bind the Onclick event
board.onclick = function () {
if (typeof ad.clickthroughUrl !== 'undefined') {
window.open(ad.clickthroughUrl);
}
//Tracking the NonLinearClickTracking events
if (typeof ad.clicktracking !== 'undefined') {
playerInstance.callUris([ad.clicktracking]);
}
};
if (typeof ad.clickthroughUrl !== 'undefined') {
board.style.cursor = 'pointer';
}
const closeBtn = document.createElement('div');
closeBtn.className = 'close_button';
closeBtn.innerHTML = '';
closeBtn.title = playerInstance.displayOptions.layoutControls.closeButtonCaption;
const tempRollListId = ad.rollListId;
closeBtn.onclick = function (event) {
playerInstance.trackCloseNonLinearAd();
this.parentElement.remove();
if (typeof event.stopImmediatePropagation !== 'undefined') {
event.stopImmediatePropagation();
}
playerInstance.adFinished = true;
playerInstance.isCurrentlyShowingNonLinearAd = false;
if (playerInstance.nonLinearTracking) {
clearInterval(playerInstance.nonLinearTracking);
}
if (playerInstance.rollsById[tempRollListId].roll === 'onPauseRoll') {
// keep new currentOnPauseRollAd or undefined if none available
playerInstance.currentOnPauseRollAd = playerInstance.onPauseRollAdPods[0];
//if any other onPauseRoll then render it
if (playerInstance.onPauseRollAdPods[0]) {
const getNextOnPauseRollAd = playerInstance.onPauseRollAdPods[0];
playerInstance.createBoard(getNextOnPauseRollAd);
delete playerInstance.onPauseRollAdPods[0];
}
}
return false;
};
board.appendChild(closeBtn);
playerInstance.domRef.player.parentNode.insertBefore(board, playerInstance.domRef.player.nextSibling);
};
// ADS
/**
* Adds a nonLinear static Image banner
*
* currently only image/gif, image/jpeg, image/png supported
*/
playerInstance.createBoard = (ad) => {
// create nonLinear Vpaid
// create nonLinear regular
if (ad.vpaid) {
playerInstance.hardStopVpaidAd('');
playerInstance.createVpaidNonLinearBoard(ad);
} else {
if (
typeof ad.staticResource === 'undefined' ||
playerInstance.supportedStaticTypes.indexOf(ad.creativeType) === -1
) {
// Couldn’t find NonLinear resource with supported type.
ad.error = true;
if (!playerInstance.vastOptions || typeof playerInstance.vastOptions.errorUrl === 'undefined') {
playerInstance.announceLocalError(503);
} else {
playerInstance.announceError(503);
}
return;
}
playerInstance.createNonLinearBoard(ad);
}
};
playerInstance.closeNonLinear = (adId) => {
const element = playerInstance.domRef.wrapper.querySelector('#fluid_nonLinear_' + adId + ', #fluid_vpaidNonLinear_' + adId);
if (element) {
element.remove();
playerInstance.isCurrentlyShowingNonLinearAd = false;
}
};
playerInstance.rollGroupContainsLinear = (groupedRolls) => {
let found = false;
for (let i = 0; i < groupedRolls.length; i++) {
if (playerInstance.rollsById[groupedRolls[i].id].adType && playerInstance.rollsById[groupedRolls[i].id].adType === 'linear') {
found = true;
break;
}
}
return found;
};
playerInstance.rollGroupContainsNonlinear = (groupedRolls) => {
let found = false;
for (let i = 0; i < groupedRolls.length; i++) {
if (playerInstance.rollsById[groupedRolls[i].id].adType.toLowerCase() === 'nonlinear') {
found = true;
break;
}
}
return found;
};
playerInstance.preRollFail = () => {
const preRollsLength = playerInstance.preRollAdPodsLength;
playerInstance.preRollVastResolved++;
if (playerInstance.preRollVastResolved === preRollsLength) {
playerInstance.preRollAdsPlay();
}
};
playerInstance.preRollSuccess = () => {
const preRollsLength = playerInstance.preRollAdPodsLength;
playerInstance.preRollVastResolved++;
if (playerInstance.preRollVastResolved === preRollsLength) {
playerInstance.preRollAdsPlay();
}
};
playerInstance.preRollAdsPlay = () => {
const time = 0;
const rollListIds = playerInstance.preRollAdPods;
const adsByType = {
linear: [],
nonLinear: []
};
playerInstance.firstPlayLaunched = true;
for (let index = 0; index < rollListIds.length; index++) {
playerInstance.rollsById[rollListIds[index]].ads.forEach(ad => {
if (ad.played === true) {
return;
}
if (ad.adType === 'linear') {
adsByType.linear.push(ad);
}
if (ad.adType === 'nonLinear') {
adsByType.nonLinear.push(ad);
playerInstance.scheduleTask({time: time, playRoll: 'midRoll', rollListId: ad.rollListId });
}
});
}
if (adsByType.linear.length > 0) {
playerInstance.toggleLoader(false);
playerInstance.playRoll(adsByType.linear);
} else {
playerInstance.playMainVideoWhenVastFails(900);
}
};
playerInstance.preRoll = (event) => {
const vastObj = event.vastObj;
playerInstance.domRef.player.removeEventListener(event.type, playerInstance.preRoll);
const rollListIds = [];
rollListIds[0] = event.type.replace('adId_', '');
const time = 0;
if (playerInstance.rollsById[rollListIds[0]].played === true) {
return;
}
playerInstance.preRollAdPods.push(rollListIds[0]);
playerInstance.preRollSuccess(vastObj);
};
playerInstance.createAdMarker = (adListId, time) => {
const markersHolder = playerInstance.domRef.wrapper.querySelector('.fluid_controls_ad_markers_holder');
const adMarker = document.createElement('div');
adMarker.className = 'fluid_controls_ad_marker fluid_controls_ad_marker_' + adListId;
adMarker.dataset.adListId = adListId;
adMarker.style.left = (time / playerInstance.mainVideoDuration * 100) + '%';
if (playerInstance.isCurrentlyPlayingAd) {
adMarker.style.display = 'none';
}
markersHolder.appendChild(adMarker);
};
playerInstance.hideAdMarker = (adListId) => {
const element = playerInstance.domRef.wrapper.querySelector('fluid_controls_ad_marker_' + adListId);
if (element) {
element.style.display = 'none';
}
};
playerInstance.showAdMarkers = () => {
const markersHolder = playerInstance.domRef.wrapper.querySelector('.fluid_controls_ad_markers_holder');
const adMarkers = markersHolder.getElementsByClassName('fluid_controls_ad_marker');
for (let i = 0; i < adMarkers.length; ++i) {
const item = adMarkers[i];
const rollListId = item.dataset.adListId;
if (playerInstance.rollsById[rollListId].played === false) {
item.style.display = '';
}
}
};
playerInstance.hideAdMarkers = () => {
const markersHolder = playerInstance.domRef.wrapper.querySelector('.fluid_controls_ad_markers_holder');
const adMarkers = markersHolder.getElementsByClassName('fluid_controls_ad_marker');
for (let i = 0; i < adMarkers.length; ++i) {
const item = adMarkers[i];
item.style.display = 'none';
}
};
playerInstance.midRoll = (event) => {
playerInstance.domRef.player.removeEventListener(event.type, playerInstance.midRoll);
const rollListId = event.type.replace('adId_', '');
if (playerInstance.rollsById[rollListId].played === true) {
return;
}
let time = playerInstance.rollsById[rollListId].timer;
if (typeof time == 'string' && time.indexOf("%") !== -1) {
time = time.replace('%', '');
time = Math.floor(playerInstance.mainVideoDuration / 100 * time);
}
if (playerInstance.displayOptions.vastOptions.showProgressbarMarkers &&
playerInstance.rollsById[rollListId].adType === "nonLinear") {
playerInstance.createAdMarker(rollListId, time);
}
playerInstance.scheduleTask({
time: time,
playRoll: 'midRoll',
rollListId
});
};
playerInstance.postRoll = (event) => {
playerInstance.domRef.player.removeEventListener(event.type, playerInstance.postRoll);
const rollListId = event.type.replace('adId_', '');
playerInstance.scheduleTask({
time: Math.floor(playerInstance.mainVideoDuration),
playRoll: 'postRoll',
rollListId
});
};
playerInstance.onPauseRoll = (event) => {
playerInstance.domRef.player.removeEventListener(event.type, playerInstance.onPauseRoll);
const rollListId = event.type.replace('adId_', '');
playerInstance.rollsById[rollListId].ads.forEach(ad => {
if (ad.adType === 'nonLinear') {
if (playerInstance.rollsById[ad.rollListId].error === true || ad.error === true) {
playerInstance.announceLocalError(101);
return;
}
const nonLinearAdExists = playerInstance.domRef.wrapper.getElementsByClassName('fluid_nonLinear_ad')[0];
if (!nonLinearAdExists) {
playerInstance.currentOnPauseRollAd = rollListId;
} else {
playerInstance.onPauseRollAdPods.push(rollListId);
}
}
});
};
/**
* Check if player has a valid nonLinear onPause Ad
*/
playerInstance.hasValidOnPauseAd = () => {
// TODO should be only one. Add validator to allow only one onPause roll
const onPauseAd = playerInstance.findRoll('onPauseRoll');
return (
onPauseAd.length !== 0 &&
playerInstance.rollsById[onPauseAd[0]] &&
playerInstance.rollsById[onPauseAd[0]].error === false &&
playerInstance.rollsById[onPauseAd[0]].ads.length &&
playerInstance.rollsById[onPauseAd[0]].ads[0].error !== true
);
};
/**
* Hide/show nonLinear onPause Ad
*/
playerInstance.toggleOnPauseAd = () => {
playerInstance.toggleLoader(false);
if (playerInstance.hasValidOnPauseAd() && !playerInstance.isCurrentlyPlayingAd && playerInstance.currentOnPauseRollAd) {
const ad = playerInstance.rollsById[playerInstance.currentOnPauseRollAd].ads[0];
playerInstance.vastOptions = ad;
let onPauseAd = '';
for (const child of playerInstance.domRef.wrapper.children) {
if (child.id === 'fluid_nonLinear_' + ad.id) {
onPauseAd = child;
}
}
if (playerInstance.domRef.player.paused && !onPauseAd) {
playerInstance.createBoard(ad);
for (const child of playerInstance.domRef.wrapper.children) {
if (child.id === 'fluid_nonLinear_' + ad.id) {
onPauseAd = child;
}
}
if (!onPauseAd) {
onPauseAd.style.display = 'none';
}
}
if (onPauseAd && playerInstance.domRef.player.paused) {
setTimeout(function () {
onPauseAd.style.display = 'flex';
ad.played = false;
playerInstance.trackingOnPauseNonLinearAd(ad, startEvent);
}, 500);
} else if (onPauseAd && !playerInstance.domRef.player.paused) {
onPauseAd.style.display = 'none';
playerInstance.adFinished = true;
playerInstance.trackingOnPauseNonLinearAd(ad, completeEvent);
}
}
};
/**
* Helper function for tracking onPause Ads
*/
playerInstance.trackingOnPauseNonLinearAd = (ad, status) => {
if (playerInstance.rollsById[ad.rollListId].error === true || ad.error === true) {
playerInstance.announceLocalError(101);
return;
}
playerInstance.vastOptions = ad;
playerInstance.trackSingleEvent(status);
};
playerInstance.getLinearAdsFromKeyTime = (keyTimeLinearObj) => {
const adListIds = [];
for (let i = 0; i < keyTimeLinearObj.length; i++) {
if (playerInstance.rollsById[keyTimeLinearObj[i].adListId].played === false) {
adListIds.push(keyTimeLinearObj[i].adListId);
}
}
return adListIds;
};
/**
* Handle scheduled tasks for a given key time
*
* @param keyTime key time in seconds
*/
playerInstance.adKeytimePlay = (keyTime) => {
if (!playerInstance.timerPool[keyTime] || playerInstance.isCurrentlyPlayingAd) {
return;
}
const timerPoolKeytimeCloseStaticAdsLength = playerInstance.timerPool[keyTime]['closeStaticAd'].length;
const timerPoolKeytimeLinearAdsLength = playerInstance.timerPool[keyTime]['linear'].length;
const timerPoolKeytimeNonlinearAdsLength = playerInstance.timerPool[keyTime]['nonLinear'].length;
const timerPoolKeytimeLoadVastLength = playerInstance.timerPool[keyTime]['loadVast'].length;
// remove the item from keytime if no ads to play
if ([
timerPoolKeytimeCloseStaticAdsLength,
timerPoolKeytimeLinearAdsLength,
timerPoolKeytimeNonlinearAdsLength,
timerPoolKeytimeLoadVastLength
].every(timerPoolLength => timerPoolLength === 0)) {
delete playerInstance.timerPool[keyTime];
return;
}
// Task: close nonLinear ads
if (timerPoolKeytimeCloseStaticAdsLength > 0) {
for (let index = 0; index < timerPoolKeytimeCloseStaticAdsLength; index++) {
const adToClose = playerInstance.timerPool[keyTime]['closeStaticAd'][index];
if (adToClose.played === true) {
playerInstance.completeNonLinearStatic(adToClose);
}
}
// empty closeStaticAd from the timerpool after closing
playerInstance.timerPool[keyTime]['closeStaticAd'] = [];
}
// Task: play linear ads
if (timerPoolKeytimeLinearAdsLength > 0) {
if (playerInstance.timerPool[keyTime]['linear'].length > 0) {
playerInstance.playRoll(playerInstance.timerPool[keyTime]['linear']);
// empty the linear ads from the timerpool after played
playerInstance.timerPool[keyTime]['linear'] = [];
// return after starting video ad, so non-linear will not overlap
return;
}
}
// Task: play nonLinear ads
if (timerPoolKeytimeNonlinearAdsLength > 0) {
for (let index = 0; index < timerPoolKeytimeNonlinearAdsLength; index++) {
const ad = playerInstance.timerPool[keyTime]['nonLinear'][index];
const rollListId = ad.rollListId;
const vastOptions = playerInstance.adPool[rollListId];
// we are not supporting nonLinear ads in cardBoard mode
if (ad.played ===