1
0
Fork 0
mirror of https://github.com/DanielnetoDotCom/YouPHPTube synced 2025-10-03 09:49:28 +02:00
Oinktube/node_modules/videojs-ima/src/ad-ui.js
Daniel 2a9630258f https://github.com/WWBN/AVideo/issues/6345#issuecomment-1067056556
Also check the lang in case insensitive
2022-03-14 14:28:38 -03:00

611 lines
16 KiB
JavaScript

/**
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* IMA SDK integration plugin for Video.js. For more information see
* https://www.github.com/googleads/videojs-ima
*/
/**
* Ad UI implementation.
*
* @param {Controller} controller Plugin controller.
* @constructor
* @struct
* @final
*/
const AdUi = function(controller) {
/**
* Plugin controller.
*/
this.controller = controller;
/**
* Div used as an ad container.
*/
this.adContainerDiv = document.createElement('div');
/**
* Div used to display ad controls.
*/
this.controlsDiv = document.createElement('div');
/**
* Div used to display ad countdown timer.
*/
this.countdownDiv = document.createElement('div');
/**
* Div used to display add seek bar.
*/
this.seekBarDiv = document.createElement('div');
/**
* Div used to display ad progress (in seek bar).
*/
this.progressDiv = document.createElement('div');
/**
* Div used to display ad play/pause button.
*/
this.playPauseDiv = document.createElement('div');
/**
* Div used to display ad mute button.
*/
this.muteDiv = document.createElement('div');
/**
* Div used by the volume slider.
*/
this.sliderDiv = document.createElement('div');
/**
* Volume slider level visuals
*/
this.sliderLevelDiv = document.createElement('div');
/**
* Div used to display ad fullscreen button.
*/
this.fullscreenDiv = document.createElement('div');
/**
* Bound event handler for onMouseUp.
*/
this.boundOnMouseUp = this.onMouseUp.bind(this);
/**
* Bound event handler for onMouseMove.
*/
this.boundOnMouseMove = this.onMouseMove.bind(this);
/**
* Stores data for the ad playhead tracker.
*/
this.adPlayheadTracker = {
'currentTime': 0,
'duration': 0,
'isPod': false,
'adPosition': 0,
'totalAds': 0,
};
/**
* Used to prefix videojs ima controls.
*/
this.controlPrefix = this.controller.getPlayerId() + '_';
/**
* Boolean flag to show or hide the ad countdown timer.
*/
this.showCountdown = true;
if (this.controller.getSettings().showCountdown === false) {
this.showCountdown = false;
}
/**
* Boolean flag if the current ad is nonlinear.
*/
this.isAdNonlinear = false;
this.createAdContainer();
};
/**
* Creates the ad container.
*/
AdUi.prototype.createAdContainer = function() {
this.assignControlAttributes(
this.adContainerDiv, 'ima-ad-container');
this.adContainerDiv.style.position = 'absolute';
this.adContainerDiv.style.zIndex = 1111;
this.adContainerDiv.addEventListener(
'mouseenter',
this.showAdControls.bind(this),
false);
this.adContainerDiv.addEventListener(
'mouseleave',
this.hideAdControls.bind(this),
false);
this.adContainerDiv.addEventListener(
'click',
this.onAdContainerClick.bind(this),
false);
this.createControls();
this.controller.injectAdContainerDiv(this.adContainerDiv);
};
/**
* Create the controls.
*/
AdUi.prototype.createControls = function() {
this.assignControlAttributes(this.controlsDiv, 'ima-controls-div');
this.controlsDiv.style.width = '100%';
if (!this.controller.getIsMobile()) {
this.assignControlAttributes(this.countdownDiv, 'ima-countdown-div');
this.countdownDiv.innerHTML = this.controller.getSettings().adLabel;
this.countdownDiv.style.display = this.showCountdown ? 'block' : 'none';
} else {
this.countdownDiv.style.display = 'none';
}
this.assignControlAttributes(this.seekBarDiv, 'ima-seek-bar-div');
this.seekBarDiv.style.width = '100%';
this.assignControlAttributes(this.progressDiv, 'ima-progress-div');
this.assignControlAttributes(this.playPauseDiv, 'ima-play-pause-div');
this.addClass(this.playPauseDiv, 'ima-playing');
this.playPauseDiv.addEventListener(
'click',
this.onAdPlayPauseClick.bind(this),
false);
this.assignControlAttributes(this.muteDiv, 'ima-mute-div');
this.addClass(this.muteDiv, 'ima-non-muted');
this.muteDiv.addEventListener(
'click',
this.onAdMuteClick.bind(this),
false);
this.assignControlAttributes(this.sliderDiv, 'ima-slider-div');
this.sliderDiv.addEventListener(
'mousedown',
this.onAdVolumeSliderMouseDown.bind(this),
false);
// Hide volume slider controls on iOS as they aren't supported.
if (this.controller.getIsIos()) {
this.sliderDiv.style.display = 'none';
}
this.assignControlAttributes(this.sliderLevelDiv, 'ima-slider-level-div');
this.assignControlAttributes(this.fullscreenDiv, 'ima-fullscreen-div');
this.addClass(this.fullscreenDiv, 'ima-non-fullscreen');
this.fullscreenDiv.addEventListener(
'click',
this.onAdFullscreenClick.bind(this),
false);
this.adContainerDiv.appendChild(this.controlsDiv);
this.controlsDiv.appendChild(this.countdownDiv);
this.controlsDiv.appendChild(this.seekBarDiv);
this.controlsDiv.appendChild(this.playPauseDiv);
this.controlsDiv.appendChild(this.muteDiv);
this.controlsDiv.appendChild(this.sliderDiv);
this.controlsDiv.appendChild(this.fullscreenDiv);
this.seekBarDiv.appendChild(this.progressDiv);
this.sliderDiv.appendChild(this.sliderLevelDiv);
};
/**
* Listener for clicks on the play/pause button during ad playback.
*/
AdUi.prototype.onAdPlayPauseClick = function() {
this.controller.onAdPlayPauseClick();
};
/**
* Listener for clicks on the play/pause button during ad playback.
*/
AdUi.prototype.onAdMuteClick = function() {
this.controller.onAdMuteClick();
};
/**
* Listener for clicks on the fullscreen button during ad playback.
*/
AdUi.prototype.onAdFullscreenClick = function() {
this.controller.toggleFullscreen();
};
/**
* Show pause and hide play button
*/
AdUi.prototype.onAdsPaused = function() {
this.controller.sdkImpl.adPlaying = false;
this.addClass(this.playPauseDiv, 'ima-paused');
this.removeClass(this.playPauseDiv, 'ima-playing');
this.showAdControls();
};
/**
* Show pause and hide play button
*/
AdUi.prototype.onAdsResumed = function() {
this.onAdsPlaying();
this.showAdControls();
};
/**
* Show play and hide pause button
*/
AdUi.prototype.onAdsPlaying = function() {
this.controller.sdkImpl.adPlaying = true;
this.addClass(this.playPauseDiv, 'ima-playing');
this.removeClass(this.playPauseDiv, 'ima-paused');
};
/**
* Takes data from the controller to update the UI.
*
* @param {number} currentTime Current time of the ad.
* @param {number} remainingTime Remaining time of the ad.
* @param {number} duration Duration of the ad.
* @param {number} adPosition Index of the ad in the pod.
* @param {number} totalAds Total number of ads in the pod.
*/
AdUi.prototype.updateAdUi =
function(currentTime, remainingTime, duration, adPosition, totalAds) {
// Update countdown timer data
const remainingMinutes = Math.floor(remainingTime / 60);
let remainingSeconds = Math.floor(remainingTime % 60);
if (remainingSeconds.toString().length < 2) {
remainingSeconds = '0' + remainingSeconds;
}
let podCount = ': ';
if (totalAds > 1) {
podCount = ' (' + adPosition + ' ' +
this.controller.getSettings().adLabelNofN + ' ' + totalAds + '): ';
}
this.countdownDiv.innerHTML =
this.controller.getSettings().adLabel + podCount +
remainingMinutes + ':' + remainingSeconds;
// Update UI
const playProgressRatio = currentTime / duration;
const playProgressPercent = playProgressRatio * 100;
this.progressDiv.style.width = playProgressPercent + '%';
};
/**
* Handles UI changes when the ad is unmuted.
*/
AdUi.prototype.unmute = function() {
this.addClass(this.muteDiv, 'ima-non-muted');
this.removeClass(this.muteDiv, 'ima-muted');
this.sliderLevelDiv.style.width =
this.controller.getPlayerVolume() * 100 + '%';
};
/**
* Handles UI changes when the ad is muted.
*/
AdUi.prototype.mute = function() {
this.addClass(this.muteDiv, 'ima-muted');
this.removeClass(this.muteDiv, 'ima-non-muted');
this.sliderLevelDiv.style.width = '0%';
};
/*
* Listener for mouse down events during ad playback. Used for volume.
*/
AdUi.prototype.onAdVolumeSliderMouseDown = function() {
document.addEventListener('mouseup', this.boundOnMouseUp, false);
document.addEventListener('mousemove', this.boundOnMouseMove, false);
};
/*
* Mouse movement listener used for volume slider.
*/
AdUi.prototype.onMouseMove = function(event) {
this.changeVolume(event);
};
/*
* Mouse release listener used for volume slider.
*/
AdUi.prototype.onMouseUp = function(event) {
this.changeVolume(event);
document.removeEventListener('mouseup', this.boundOnMouseUp);
document.removeEventListener('mousemove', this.boundOnMouseMove);
};
/*
* Utility function to set volume and associated UI
*/
AdUi.prototype.changeVolume = function(event) {
let percent =
(event.clientX - this.sliderDiv.getBoundingClientRect().left) /
this.sliderDiv.offsetWidth;
percent *= 100;
// Bounds value 0-100 if mouse is outside slider region.
percent = Math.min(Math.max(percent, 0), 100);
this.sliderLevelDiv.style.width = percent + '%';
if (this.percent == 0) {
this.addClass(this.muteDiv, 'ima-muted');
this.removeClass(this.muteDiv, 'ima-non-muted');
} else {
this.addClass(this.muteDiv, 'ima-non-muted');
this.removeClass(this.muteDiv, 'ima-muted');
}
this.controller.setVolume(percent / 100); // 0-1
};
/**
* Show the ad container.
*/
AdUi.prototype.showAdContainer = function() {
this.adContainerDiv.style.display = 'block';
};
/**
* Hide the ad container
*/
AdUi.prototype.hideAdContainer = function() {
this.adContainerDiv.style.display = 'none';
};
/**
* Handles clicks on the ad container
*/
AdUi.prototype.onAdContainerClick = function() {
if (this.isAdNonlinear) {
this.controller.togglePlayback();
}
};
/**
* Resets the state of the ad ui.
*/
AdUi.prototype.reset = function() {
this.hideAdContainer();
};
/**
* Handles ad errors.
*/
AdUi.prototype.onAdError = function() {
this.hideAdContainer();
};
/**
* Handles ad break starting.
*
* @param {Object} adEvent The event fired by the IMA SDK.
*/
AdUi.prototype.onAdBreakStart = function(adEvent) {
this.showAdContainer();
const contentType = adEvent.getAd().getContentType();
if ((contentType === 'application/javascript') &&
!this.controller.getSettings().showControlsForJSAds) {
this.controlsDiv.style.display = 'none';
} else {
this.controlsDiv.style.display = 'block';
}
this.onAdsPlaying();
// Start with the ad controls minimized.
this.hideAdControls();
};
/**
* Handles ad break ending.
*/
AdUi.prototype.onAdBreakEnd = function() {
const currentAd = this.controller.getCurrentAd();
if (currentAd == null || // hide for post-roll only playlist
currentAd.isLinear()) { // don't hide for non-linear ads
this.hideAdContainer();
}
this.controlsDiv.style.display = 'none';
this.countdownDiv.innerHTML = '';
};
/**
* Handles when all ads have finished playing.
*/
AdUi.prototype.onAllAdsCompleted = function() {
this.hideAdContainer();
};
/**
* Handles when a linear ad starts.
*/
AdUi.prototype.onLinearAdStart = function() {
// Don't bump container when controls are shown
this.removeClass(this.adContainerDiv, 'bumpable-ima-ad-container');
this.isAdNonlinear = false;
};
/**
* Handles when a non-linear ad starts.
*/
AdUi.prototype.onNonLinearAdLoad = function() {
// For non-linear ads that show after a linear ad. For linear ads, we show the
// ad container in onAdBreakStart to prevent blinking in pods.
this.adContainerDiv.style.display = 'block';
// Bump container when controls are shown
this.addClass(this.adContainerDiv, 'bumpable-ima-ad-container');
this.isAdNonlinear = true;
};
AdUi.prototype.onPlayerEnterFullscreen = function() {
this.addClass(this.fullscreenDiv, 'ima-fullscreen');
this.removeClass(this.fullscreenDiv, 'ima-non-fullscreen');
};
AdUi.prototype.onPlayerExitFullscreen = function() {
this.addClass(this.fullscreenDiv, 'ima-non-fullscreen');
this.removeClass(this.fullscreenDiv, 'ima-fullscreen');
};
/**
* Called when the player volume changes.
*
* @param {number} volume The new player volume.
*/
AdUi.prototype.onPlayerVolumeChanged = function(volume) {
if (volume == 0) {
this.addClass(this.muteDiv, 'ima-muted');
this.removeClass(this.muteDiv, 'ima-non-muted');
this.sliderLevelDiv.style.width = '0%';
} else {
this.addClass(this.muteDiv, 'ima-non-muted');
this.removeClass(this.muteDiv, 'ima-muted');
this.sliderLevelDiv.style.width = volume * 100 + '%';
}
};
/**
* Shows ad controls on mouseover.
*/
AdUi.prototype.showAdControls = function() {
const {disableAdControls} = this.controller.getSettings();
if (!disableAdControls) {
this.addClass(this.controlsDiv, 'ima-controls-div-showing');
}
};
/**
* Hide the ad controls.
*/
AdUi.prototype.hideAdControls = function() {
this.removeClass(this.controlsDiv, 'ima-controls-div-showing');
};
/**
* Assigns the unique id and class names to the given element as well as the
* style class.
* @param {HTMLElement} element Element that needs the controlName assigned.
* @param {string} controlName Control name to assign.
*/
AdUi.prototype.assignControlAttributes = function(element, controlName) {
element.id = this.controlPrefix + controlName;
element.className = this.controlPrefix + controlName + ' ' + controlName;
};
/**
* Returns a regular expression to test a string for the given className.
*
* @param {string} className The name of the class.
* @return {RegExp} The regular expression used to test for that class.
*/
AdUi.prototype.getClassRegexp = function(className) {
// Matches on
// (beginning of string OR NOT word char)
// classname
// (negative lookahead word char OR end of string)
return new RegExp('(^|[^A-Za-z-])' + className +
'((?![A-Za-z-])|$)', 'gi');
};
/**
* Returns whether or not the provided element has the provied class in its
* className.
* @param {HTMLElement} element Element to tes.t
* @param {string} className Class to look for.
* @return {boolean} True if element has className in class list. False
* otherwise.
*/
AdUi.prototype.elementHasClass = function(element, className) {
const classRegexp = this.getClassRegexp(className);
return classRegexp.test(element.className);
};
/**
* Adds a class to the given element if it doesn't already have the class
* @param {HTMLElement} element Element to which the class will be added.
* @param {string} classToAdd Class to add.
*/
AdUi.prototype.addClass = function(element, classToAdd) {
element.className = element.className.trim() + ' ' + classToAdd;
};
/**
* Removes a class from the given element if it has the given class
*
* @param {HTMLElement} element Element from which the class will be removed.
* @param {string} classToRemove Class to remove.
*/
AdUi.prototype.removeClass = function(element, classToRemove) {
const classRegexp = this.getClassRegexp(classToRemove);
element.className =
element.className.trim().replace(classRegexp, '');
};
/**
* @return {HTMLElement} The div for the ad container.
*/
AdUi.prototype.getAdContainerDiv = function() {
return this.adContainerDiv;
};
/**
* Changes the flag to show or hide the ad countdown timer.
*
* @param {boolean} showCountdownIn Show or hide the countdown timer.
*/
AdUi.prototype.setShowCountdown = function(showCountdownIn) {
this.showCountdown = showCountdownIn;
this.countdownDiv.style.display = this.showCountdown ? 'block' : 'none';
};
export default AdUi;