(function( $ ) {
'use strict';
class AIOVGVideoElement extends HTMLElement {
/**
* Element created.
*/
constructor() {
super();
// Set references to the DOM elements used by the component
this.player = null;
this.playButtonEl = null;
this.cookieConsentEl = null;
this.emailCaptureEl = null;
// Set references to the public properties used by the component
this.settings = {};
// Set references to the private properties used by the component
this._playerId = '';
this._hasVideoStarted = false;
this._ajaxUrl = aiovg_player.ajax_url;
this._ajaxNonce = aiovg_player.ajax_nonce;
}
/**
* Browser calls this method when the element is added to the document.
* (can be called many times if an element is repeatedly added/removed)
*/
connectedCallback() {
this._playerId = this.dataset.id || '';
this.settings = this.dataset.params ? JSON.parse( this.dataset.params ) : {};
if ( ! this.settings.hasOwnProperty( 'player' ) ) {
this.settings.player = {};
}
this.settings.player.html5 = {
vhs: {
overrideNative: ! videojs.browser.IS_ANY_SAFARI,
}
};
if ( this.settings.hasOwnProperty( 'email_capture' ) ) {
this.emailCaptureEl = this.querySelector( '.aiovg-email-capture' );
}
if ( this.emailCaptureEl && parseInt( this.emailCaptureEl.dataset.pre_roll ) ) {
this._initPreRollEmailCapture();
} else if ( this.settings.cookie_consent ) {
this.cookieConsentEl = this.querySelector( '.aiovg-privacy-wrapper' );
const cookieConsentButtonEl = this.querySelector( '.aiovg-privacy-consent-button' );
if ( this.cookieConsentEl && cookieConsentButtonEl !== null ) {
const poster = this.cookieConsentEl.getAttribute( 'data-poster' ) || '';
if ( AIOVGVideoElement.isValidUrl( poster ) ) {
this.cookieConsentEl.style.backgroundImage = `url("${poster}")`;
}
cookieConsentButtonEl.addEventListener( 'click', () => this._onCookieConsent() );
} else {
this._initPlayer();
}
} else {
this._initPlayer();
}
}
/**
* Browser calls this method when the element is removed from the document.
* (can be called many times if an element is repeatedly added/removed)
*/
disconnectedCallback() {
// TODO
}
/**
* Define private methods.
*/
_onCookieConsent() {
this.settings.player.autoplay = true;
this._setCookie();
// Remove cookieconsent from other players
const videos = document.querySelectorAll( '.aiovg-player-element' );
for ( let i = 0; i < videos.length; i++ ) {
videos[ i ].removeCookieConsent();
}
window.postMessage({
message: 'aiovg-cookie-consent'
}, window.location.origin );
}
_initPreRollEmailCapture() {
this.classList.add( 'aiovg-show-email-capture' );
const poster = this.emailCaptureEl.getAttribute( 'data-poster' ) || '';
if ( AIOVGVideoElement.isValidUrl( poster ) ) {
this.emailCaptureEl.style.backgroundImage = `url("${poster}")`;
}
const firstInput = this.emailCaptureEl.querySelector( 'input' );
if ( firstInput ) {
firstInput.focus();
}
this._initEmailCaptureForm( ( data ) => {
if ( ! this.emailCaptureEl ) return false;
if ( this.emailCaptureEl.classList.contains( 'aiovg-is-loading' ) ) return false; // Prevent multiple submissions
this.emailCaptureEl.classList.add( 'aiovg-is-loading' );
const xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = () => {
if ( 4 == xmlhttp.readyState ) {
let response = null;
if ( 200 == xmlhttp.status && xmlhttp.responseText ) {
try {
response = JSON.parse( xmlhttp.responseText );
} catch ( e ) {}
}
if ( response && response.success ) {
// Hide the form and remove it from the DOM to prevent it from being submitted again.
this.classList.remove( 'aiovg-show-email-capture' );
this.emailCaptureEl.remove();
this.emailCaptureEl = null;
// Render player
this.settings.player.autoplay = true;
this._initPlayer();
// Email submission implies consent — remove both gates from other players
const videos = document.querySelectorAll( '.aiovg-player-element' );
for ( let i = 0; i < videos.length; i++ ) {
videos[ i ].removeCookieConsent();
videos[ i ].removeEmailCapture();
}
window.postMessage({
message: 'aiovg-email-captured'
}, window.location.origin );
} else {
// Show error message
const errorSpan = this.emailCaptureEl.querySelector( '.aiovg-email-capture-error' );
if ( errorSpan ) {
errorSpan.textContent = ( response && response.data && typeof response.data === 'string' ) ? response.data : errorSpan.getAttribute( 'data-error_generic' );
errorSpan.style.display = 'block';
}
// Restore the form so the user can retry.
this.emailCaptureEl.classList.remove( 'aiovg-is-loading' );
}
}
};
xmlhttp.open( 'POST', this._ajaxUrl + '?action=aiovg_save_email_lead&security=' + this._ajaxNonce, true );
xmlhttp.setRequestHeader( 'Content-type', 'application/x-www-form-urlencoded' );
xmlhttp.send( data );
} );
}
_initPlayer() {
if ( this.player ) {
return false;
}
this.player = videojs( this.querySelector( 'video-js' ), this.settings.player );
this.player.ready( () => this._onReady() );
this.player.one( 'loadedmetadata', () => this._onMetadataLoaded() );
this.player.on( 'play', () => this._onPlay() );
this.player.on( 'playing', () => this._onPlaying() );
this.player.on( 'ended', () => this._onEnded() );
this._initOffset();
this._initChapters();
this._initQualitySelector();
this._initOverlays();
this._initHotKeys();
this._initEmailCapture();
this._initContextMenu();
// Dispatch a player ready event
const options = {
id: this._playerId, // Backward compatibility to 3.3.0
player_id: this._playerId,
config: this.settings, // Backward compatibility to 3.3.0
settings: this.settings,
player: this.player
};
this._dispatchEvent( 'player.init', options );
}
_onReady() {
this.classList.remove( 'vjs-waiting' );
this.playButtonEl = this.querySelector( '.vjs-big-play-button' );
if ( this.playButtonEl !== null ) {
this.playButtonEl.addEventListener( 'click', () => this._onPlayClicked() );
}
}
_onMetadataLoaded() {
// Quality selector
const qualitySelectorEl = this.querySelector( '.vjs-quality-selector' );
if ( qualitySelectorEl !== null ) {
const items = qualitySelectorEl.querySelectorAll( '.vjs-menu-item' );
for ( let i = 0; i < items.length; i++ ) {
let item = items[ i ];
const textNode = item.querySelector( '.vjs-menu-item-text' );
const resolution = textNode.innerHTML.replace( /\D/g, '' );
if ( resolution >= 2160 ) {
item.innerHTML += '';
} else if ( resolution >= 720 ) {
item.innerHTML += '';
}
}
}
// Add support for SRT
if ( this.settings.hasOwnProperty( 'tracks' ) ) {
for ( let i = 0, max = this.settings.tracks.length; i < max; i++ ) {
const track = this.settings.tracks[ i ];
let mode = '';
if ( i == 0 && this.settings.cc_load_policy == 1 ) {
mode = 'showing';
}
if ( /srt/.test( track.src.toLowerCase() ) ) {
this._addSrtTextTrack( track, mode );
} else {
const obj = {
kind: 'captions',
src: track.src,
label: track.label,
srclang: track.srclang
};
if ( mode ) {
obj.mode = mode;
}
this.player.addRemoteTextTrack( obj, true );
}
}
}
// Chapters
if ( this.settings.hasOwnProperty( 'chapters' ) ) {
this._addMarkers();
}
}
_onPlayClicked() {
if ( ! this._hasVideoStarted ) {
this.classList.add( 'vjs-waiting' );
}
this.playButtonEl.removeEventListener( 'click', () => this._onPlayClicked() );
}
_onPlay() {
if ( ! this._hasVideoStarted ) {
this._hasVideoStarted = true;
this.classList.remove( 'vjs-waiting' );
this._updateViewsCount();
}
// Pause other players
const videos = document.querySelectorAll( '.aiovg-player-element' );
for ( let i = 0; i < videos.length; i++ ) {
if ( videos[ i ] != this ) {
videos[ i ].pause();
}
}
window.postMessage({
message: 'aiovg-video-playing'
}, window.location.origin );
}
_onPlaying() {
this.player.trigger( 'controlsshown' );
}
_onEnded() {
this.player.trigger( 'controlshidden' );
}
_initOffset() {
const offset = {};
if ( this.settings.hasOwnProperty( 'start' ) ) {
offset.start = this.settings.start;
}
if ( this.settings.hasOwnProperty( 'end' ) ) {
offset.end = this.settings.end;
}
if ( Object.keys( offset ).length > 1 ) {
offset.restart_beginning = false;
this.player.offset( offset );
}
}
_initChapters() {
if ( ! this.settings.hasOwnProperty( 'chapters' ) ) {
return false;
}
const root = this;
try {
this.player.getDescendant([
'ControlBar',
'ProgressControl',
'SeekBar',
'MouseTimeDisplay',
'TimeTooltip',
]).update = function( seekBarRect, seekBarPoint, time ) {
const markers = root.settings.chapters;
const markerIndex = markers.findIndex( ( { time: markerTime } ) => markerTime == root._formattedTimeToSeconds( time ) );
if ( markerIndex > -1 ) {
const label = markers[ markerIndex ].label;
videojs.dom.emptyEl( this.el() );
videojs.dom.appendContent( this.el(), [ root._labelEl( label ), root._timeEl( time ) ] );
return false;
}
this.write( time );
};
} catch ( error ) {
// console.log( error );
}
}
_initQualitySelector() {
// Standard quality selector
this.player.on( 'qualitySelected', ( event, source ) => {
const resolution = source.label.replace( /\D/g, '' );
this.player.removeClass( 'vjs-4k' );
this.player.removeClass( 'vjs-hd' );
if ( resolution >= 2160 ) {
this.player.addClass( 'vjs-4k' );
} else if ( resolution >= 720 ) {
this.player.addClass( 'vjs-hd' );
}
});
// HLS quality selector
const src = this.player.src();
if ( /.m3u8/.test( src ) || /.mpd/.test( src ) ) {
if ( this.settings.player.controlBar.children.indexOf( 'QualitySelector' ) != -1 ) {
this.player.qualityMenu();
}
}
}
_initOverlays() {
const overlays = [];
// Share / Embed
if ( this.settings.hasOwnProperty( 'share' ) || this.settings.hasOwnProperty( 'embed' ) ) {
overlays.push({
content: '',
class: 'vjs-share',
align: 'top-right',
start: 'controlsshown',
end: 'controlshidden',
showBackground: false
});
}
// Download
if ( this.settings.hasOwnProperty( 'download' ) ) {
let className = 'vjs-download';
if ( this.settings.hasOwnProperty( 'share' ) || this.settings.hasOwnProperty( 'embed' ) ) {
className += ' vjs-has-share';
}
overlays.push({
content: 'Download',
class: className,
align: 'top-right',
start: 'controlsshown',
end: 'controlshidden',
showBackground: false
});
}
// Logo / Trailer Badge
let hasLogo = this.settings.hasOwnProperty( 'logo' );
let hasTrailer = this.settings.hasOwnProperty( 'trailer' );
if ( hasLogo || hasTrailer ) {
let align = 'bottom-left';
let style = 'margin: 5px;';
if ( hasLogo ) {
if ( this.settings.logo.margin ) {
this.settings.logo.margin = this.settings.logo.margin - 5;
}
style = 'margin: ' + this.settings.logo.margin + 'px;';
switch ( this.settings.logo.position ) {
case 'topleft':
align = 'top-left';
break;
case 'topright':
align = 'top-right';
break;
case 'bottomright':
align = 'bottom-right';
break;
}
}
// Determine the href: trailer URL takes precedence when active.
let logoHref = hasLogo ? this.settings.logo.link : 'javascript:void(0)';
if ( hasTrailer && this.settings.trailer.url ) {
logoHref = this.settings.trailer.url;
}
// Icon: logo image > external-link SVG (only when URL set) > nothing.
let iconHtml = '';
if ( hasLogo ) {
iconHtml = '';
} else if ( hasTrailer && this.settings.trailer.url ) {
iconHtml = '';
}
// Label: visible trailer label or sr-only text for logo-only mode.
let labelHtml = hasTrailer ? '' + this.settings.trailer.label + '' : 'Logo';
let logoContent = '' + iconHtml + labelHtml + '';
overlays.push({
content: logoContent,
class: 'vjs-logo',
align: align,
start: 'controlsshown',
end: 'controlshidden',
showBackground: false
});
}
// Overlay
if ( overlays.length > 0 ) {
this.player.overlay({
content: '',
overlays: overlays
});
if ( this.settings.hasOwnProperty( 'share' ) || this.settings.hasOwnProperty( 'embed' ) ) {
const options = {};
options.content = this.querySelector( '.vjs-share-embed' );
options.temporary = false;
const ModalDialog = videojs.getComponent( 'ModalDialog' );
const modal = new ModalDialog( this.player, options );
modal.addClass( 'vjs-modal-dialog-share-embed' );
this.player.addChild( modal );
let wasPlaying = true;
this.querySelector( '.vjs-share-embed-button' ).addEventListener( 'click', () => {
wasPlaying = ! this.player.paused();
modal.open();
});
modal.on( 'modalclose', () => {
if ( wasPlaying ) {
this.player.play();
}
});
}
if ( this.settings.hasOwnProperty( 'embed' ) ) {
this.querySelector( '.vjs-input-embed-code' ).addEventListener( 'focus', function() {
this.select();
if ( navigator.clipboard && navigator.clipboard.writeText ) {
navigator.clipboard.writeText( this.value ).catch( () => {} );
} else {
document.execCommand( 'copy' );
}
});
}
}
}
_initHotKeys() {
if ( this.settings.hotkeys ) {
this.player.hotkeys();
}
}
_initEmailCapture() {
if ( ! this.emailCaptureEl ) return false;
this.emailCaptureEl.style.backgroundImage = 'none';
this.player.el().appendChild( this.emailCaptureEl );
let isModalOpen = false;
let wasPlaying = false;
const showEmailCapture = () => {
if ( ! this.emailCaptureEl || isModalOpen ) return false;
isModalOpen = true;
if ( this.player && ! this.player.ended() ) {
wasPlaying = ! this.player.paused();
this.player.pause();
}
this.classList.add( 'aiovg-show-email-capture' );
const firstInput = this.emailCaptureEl.querySelector( 'input' );
if ( firstInput ) {
firstInput.focus();
}
};
const hideEmailCapture = () => {
if ( ! this.emailCaptureEl || ! isModalOpen ) return false;
isModalOpen = false; // Clear before player.play() so the guard above does not block it.
this.classList.remove( 'aiovg-show-email-capture' );
this.emailCaptureEl.remove();
this.emailCaptureEl = null;
if ( wasPlaying ) {
this.player.play();
}
}
const ajaxEmailCapture = ( data ) => {
if ( ! this.emailCaptureEl ) return false;
if ( this.emailCaptureEl.classList.contains( 'aiovg-is-loading' ) ) return false; // Prevent multiple submissions
this.emailCaptureEl.classList.add( 'aiovg-is-loading' );
const xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = () => {
if ( 4 == xmlhttp.readyState ) {
let response = null;
if ( 200 == xmlhttp.status && xmlhttp.responseText ) {
try {
response = JSON.parse( xmlhttp.responseText );
} catch ( e ) {}
}
if ( response && response.success ) {
// Hide the form and remove it from the DOM to prevent it from being submitted again.
hideEmailCapture();
// Email submission implies consent — remove both gates from other players
const videos = document.querySelectorAll( '.aiovg-player-element' );
for ( let i = 0; i < videos.length; i++ ) {
videos[ i ].removeCookieConsent();
videos[ i ].removeEmailCapture();
}
window.postMessage({
message: 'aiovg-email-captured'
}, window.location.origin );
} else {
// Show error message
const errorSpan = this.emailCaptureEl.querySelector( '.aiovg-email-capture-error' );
if ( errorSpan ) {
errorSpan.textContent = ( response && response.data && typeof response.data === 'string' ) ? response.data : errorSpan.getAttribute( 'data-error_generic' );
errorSpan.style.display = 'block';
}
// Restore the form so the user can retry.
this.emailCaptureEl.classList.remove( 'aiovg-is-loading' );
}
}
};
xmlhttp.open( 'POST', this._ajaxUrl + '?action=aiovg_save_email_lead&security=' + this._ajaxNonce, true );
xmlhttp.setRequestHeader( 'Content-type', 'application/x-www-form-urlencoded' );
xmlhttp.send( data );
};
// Mid-roll — show when currentTime reaches the configured percentage.
const midRoll = parseInt( this.emailCaptureEl.dataset.mid_roll );
if ( midRoll ) {
let midRollFired = false;
const onMidRollTimeUpdate = () => {
if ( ! this.emailCaptureEl ) {
this.player.off( 'timeupdate', onMidRollTimeUpdate );
return false;
}
if ( midRollFired ) return false;
// Skip during ad playback — currentTime/duration reflect the ad, not the content.
if ( this.settings.hasOwnProperty( 'ads' ) && this.player.ads && this.player.ads.isInAdMode() ) {
return false;
}
const duration = this.player.duration();
const current = this.player.currentTime();
if ( duration && current >= ( midRoll / 100 ) * duration ) {
midRollFired = true;
this.player.off( 'timeupdate', onMidRollTimeUpdate );
showEmailCapture();
}
};
this.player.on( 'timeupdate', onMidRollTimeUpdate );
}
// Post-roll — show after the video ends.
const postRoll = parseInt( this.emailCaptureEl.dataset.post_roll );
if ( postRoll ) {
this.player.on( 'ended', () => {
showEmailCapture();
} );
}
// Re-pause on any play event while the overlay is open.
this.player.on( 'play', () => {
if ( ! this.emailCaptureEl || ! isModalOpen ) return false;
wasPlaying = true;
this.player.pause();
} );
this.player.on( 'playing', () => {
if ( ! this.emailCaptureEl || ! isModalOpen ) return false;
wasPlaying = true;
this.player.pause();
} );
this._initEmailCaptureForm( ( data ) => ajaxEmailCapture( data ) );
}
_initEmailCaptureForm( onSubmit ) {
const submitBtn = this.querySelector( '.aiovg-email-capture-submit' );
const skipLink = this.querySelector( '.aiovg-email-capture-skip' );
if ( submitBtn ) {
submitBtn.addEventListener( 'click', () => {
const emailField = this.emailCaptureEl.querySelector( '.aiovg-email-capture-email' );
const errorSpan = this.emailCaptureEl.querySelector( '.aiovg-email-capture-error' );
const email = emailField ? emailField.value.trim() : '';
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if ( ! email || ! regex.test( email ) ) {
if ( errorSpan ) {
const errorMessage = errorSpan.getAttribute( 'data-invalid_email' ) || 'Please enter a valid email address.';
errorSpan.textContent = errorMessage;
errorSpan.style.display = 'block';
}
if ( emailField ) {
emailField.classList.add( 'aiovg-is-invalid' );
emailField.setAttribute( 'aria-invalid', 'true' );
}
return false;
}
if ( errorSpan ) {
errorSpan.style.display = 'none';
}
if ( emailField ) {
emailField.classList.remove( 'aiovg-is-invalid' );
emailField.removeAttribute( 'aria-invalid' );
}
let params = 'email=' + encodeURIComponent( email );
const nameField = this.emailCaptureEl.querySelector( '.aiovg-email-capture-name' );
if ( nameField && nameField.value.trim() ) {
params += '&name=' + encodeURIComponent( nameField.value.trim() );
}
const phoneField = this.emailCaptureEl.querySelector( '.aiovg-email-capture-phone' );
if ( phoneField && phoneField.value.trim() ) {
params += '&phone=' + encodeURIComponent( phoneField.value.trim() );
}
if ( 'aiovg_videos' == this.settings.post_type && this.settings.post_id ) {
params += '&video_id=' + encodeURIComponent( this.settings.post_id );
} else {
params += '&video_id=0';
if ( this.emailCaptureEl.dataset.video_url ) {
params += '&video_url=' + encodeURIComponent( this.emailCaptureEl.dataset.video_url );
}
}
params += '&page_url=' + encodeURIComponent( window.location.href );
onSubmit( params );
} );
}
if ( skipLink ) {
const doSkip = () => onSubmit( 'skipped=1' );
skipLink.addEventListener( 'click', doSkip );
skipLink.addEventListener( 'keydown', ( event ) => {
if ( 13 === event.keyCode || 32 === event.keyCode ) {
event.preventDefault();
doSkip();
}
} );
}
}
_initContextMenu() {
if ( ! this.settings.hasOwnProperty( 'contextmenu' ) ) {
return false;
}
let contextmenuEl = document.querySelector( '#aiovg-contextmenu' );
if ( contextmenuEl === null ) {
contextmenuEl = document.createElement( 'div' );
contextmenuEl.id = 'aiovg-contextmenu';
contextmenuEl.style.display = 'none';
contextmenuEl.innerHTML = '