553 lines
28 KiB
JavaScript
553 lines
28 KiB
JavaScript
// global sekFrontLocalized, nimbleListenTo
|
|
// jQuery plugin
|
|
// Fired in ...front_fire.js
|
|
(function(w, d){
|
|
var callbackFunc = function() {
|
|
(function ( $, window ) {
|
|
//defaults
|
|
var pluginName = 'nimbleLoadVideoBg',
|
|
defaults = {
|
|
bgVideoContainerClass: 'sek-bg-video-wrapper',
|
|
bgYoutubeVideoContainerClass : 'sek-bg-youtube-video-wrapper',
|
|
bgLocalVideoContainerClass: 'sek-background-video-local',
|
|
bgLoadingClass: 'sek-bg-loading',
|
|
loop:true,
|
|
delayBeforeStart:null,
|
|
activeOnMobile:false,
|
|
startAt:null,
|
|
endAt:null,
|
|
lazyLoad:false
|
|
//enableCentering : true,
|
|
// onresize : true,
|
|
// onInit : true,//<= shall we smartload on init or wait for a custom event, typically smartload ?
|
|
// oncustom : [],//list of event here
|
|
// $containerToListen : null,//<= we might want to listen to custom event trigger to a parent container.Should be a jQuery obj
|
|
// imgSel : 'img',
|
|
// defaultCSSVal : { width : 'auto' , height : 'auto' },
|
|
// leftAdjust : 0,
|
|
// zeroLeftAdjust : 0,
|
|
// topAdjust : 0,
|
|
// zeroTopAdjust : -2,//<= top ajustement for sek-h-centrd
|
|
// useImgAttr:false,//uses the img height and width attributes if not visible (typically used for the customizr slider hidden images)
|
|
// setOpacityWhenCentered : false,//this can be used to hide the image during the time it is centered
|
|
// addCenteredClassWithDelay : 0,//<= a small delay can be required when we rely on the sek-v-centrd or sek-h-centrd css classes to set the opacity for example
|
|
// opacity : 1
|
|
};
|
|
|
|
function Plugin( element, options ) {
|
|
var self = this;
|
|
this.$element = $(element);
|
|
this.$window = nb_.cachedElements.$window;
|
|
this._defaults = defaults;
|
|
this._name = pluginName;
|
|
this.options = nb_.isObject( options ) ? options : {};
|
|
|
|
this.videoPlayer = false;//<= will hold the video player object
|
|
|
|
if ( this.options.lazyLoad ) {
|
|
//the scroll event gets throttled with the requestAnimationFrame
|
|
nb_.cachedElements.$window.on('scroll', nb_.throttle( function( _evt ) {
|
|
if ( nb_.elOrFirstVisibleParentIsInWindow( self.$element ) && !self.$element.data('sek-player-instantiated') ) {
|
|
// Are we already instantiated ?
|
|
if ( false === self.videoPlayer ) {
|
|
self.initSetup();
|
|
}
|
|
}
|
|
}, 50 ) );
|
|
|
|
//debounced resize event
|
|
nb_.cachedElements.$window.on('resize', nb_.debounce( function( _evt ) {
|
|
if ( nb_.elOrFirstVisibleParentIsInWindow( self.$element ) && !self.$element.data('sek-player-instantiated') ) {
|
|
// Are we already instantiated ?
|
|
if ( false === self.videoPlayer ) {
|
|
self.initSetup();
|
|
}
|
|
}
|
|
}, 100 ) );
|
|
|
|
if ( nb_.elOrFirstVisibleParentIsInWindow( self.$element ) && !self.$element.data('sek-player-instantiated') ) {
|
|
// Are we already instantiated ?
|
|
if ( false === self.videoPlayer ) {
|
|
self.initSetup();
|
|
}
|
|
}
|
|
} else {
|
|
// Are we already instantiated ?
|
|
if ( false === self.videoPlayer ) {
|
|
self.initSetup();
|
|
}
|
|
}
|
|
}
|
|
|
|
Plugin.prototype.initSetup = function() {
|
|
var self = this;
|
|
|
|
// set options from data attributes
|
|
if ( ! nb_.isUndefined( self.$element.data('sek-video-bg-loop') ) ) {
|
|
this.options.loop = self.$element.data('sek-video-bg-loop');
|
|
}
|
|
this.options.delayBeforeStart = self.$element.data('sek-video-delay-before');
|
|
this.options.delayBeforeStart = Math.abs( self.options.delayBeforeStart ? parseInt( self.options.delayBeforeStart, 10 ) : 0 );
|
|
|
|
if ( ! nb_.isUndefined( self.$element.data('sek-video-bg-on-mobile') ) ) {
|
|
this.options.activeOnMobile = self.$element.data('sek-video-bg-on-mobile');
|
|
}
|
|
this.options.startAt = self.$element.data('sek-video-start-at');
|
|
this.options.startAt = Math.abs( self.options.startAt ? parseInt( self.options.startAt, 10 ) : 0 );
|
|
|
|
this.options.endAt = self.$element.data('sek-video-end-at');
|
|
this.options.endAt = Math.abs( self.options.endAt ? parseInt( self.options.endAt, 10 ) : 0 );
|
|
|
|
// make sure the video fragment is consistent
|
|
// endAt > startAt, ...
|
|
this.isFragmentedVideo = false;
|
|
var startAt = this.options.startAt,
|
|
endAt = this.options.endAt;
|
|
|
|
if ( startAt || endAt ) {
|
|
if ( startAt && endAt < 1 ) {
|
|
this.isFragmentedVideo = true;
|
|
} else if ( startAt && endAt && endAt > startAt ) {
|
|
this.isFragmentedVideo = true;
|
|
} else if ( startAt < 1 && endAt ) {
|
|
this.isFragmentedVideo = true;
|
|
}
|
|
}
|
|
|
|
this.options = $.extend( {}, defaults, this.options );
|
|
|
|
var _doInit = function() {
|
|
// init now
|
|
self.init();
|
|
self.$element.on( 'refresh-video-dimensions', nb_.debounce( function() {
|
|
nb_.delay( function() {
|
|
self.updatePlayerDimensions();
|
|
}, 300 );
|
|
}, 200 ) );
|
|
|
|
// Flag
|
|
self.$element.data('sek-player-instantiated', true );
|
|
};
|
|
|
|
// maybe delay init if set by user
|
|
nb_.delay( _doInit, self.options.delayBeforeStart * 1000 );
|
|
};
|
|
|
|
/*
|
|
* @param : array of $img
|
|
* @param : current event
|
|
* @return : void
|
|
* scroll event performance enhancer => avoid browser stack if too much scrolls
|
|
*/
|
|
Plugin.prototype._better_scroll_event_handler = function( $_Elements , _evt ) {
|
|
var self = this;
|
|
if ( ! this.doingAnimation ) {
|
|
this.doingAnimation = true;
|
|
window.requestAnimationFrame(function() {
|
|
self._maybe_trigger_load( $_Elements , _evt );
|
|
self.doingAnimation = false;
|
|
});
|
|
}
|
|
};
|
|
|
|
//can access this.element and this.option
|
|
//@return void
|
|
Plugin.prototype.init = function () {
|
|
var self = this;
|
|
|
|
// Always fire when customzing
|
|
if ( !nb_.isCustomizing() && !this.options.activeOnMobile && nb_.isMobile() ) {
|
|
return;
|
|
}
|
|
|
|
// set video url prop
|
|
this.videoUrl = self.$element.data('sek-video-bg-src');
|
|
|
|
// set videoOrigin
|
|
if ( -1 !== this.videoUrl.indexOf('vimeo.com') ) {
|
|
this.videoOrigin = 'vimeo';
|
|
this.videoApiHelpers = this.getVideoApiHelpers( 'vimeo' );
|
|
} else if ( this.videoUrl.match(/^(?:https?:\/\/)?(?:www\.)?(?:m\.)?(?:youtu\.be\/|youtube\.com)/ ) ) {
|
|
this.videoOrigin = 'youtube';
|
|
this.videoApiHelpers = this.getVideoApiHelpers( 'youtube' );
|
|
}
|
|
|
|
|
|
// inject video container if not done yet
|
|
if ( nb_.isUndefined(self.$backgroundVideoContainer) || self.$backgroundVideoContainer.length < 1 ) {
|
|
self.$element.children().first().before( $('<div>', { class : self.options.bgVideoContainerClass} ) );
|
|
self.$backgroundVideoContainer = $( '.' + self.options.bgVideoContainerClass, self.$element );
|
|
}
|
|
|
|
if ( this.videoApiHelpers ) {
|
|
self.$backgroundVideoContainer.append( $('<div>', { class : self.options.bgYoutubeVideoContainerClass } ) );
|
|
this.videoId = this.videoApiHelpers.getVideoIDFromURL( this.videoUrl );
|
|
if ( this.videoId ) {
|
|
this.videoApiHelpers.onApiReady( function ( apiInstance ) {
|
|
self.apiInstance = apiInstance;
|
|
if ('youtube' === self.videoOrigin) {
|
|
self.setupYtubeVideo();
|
|
}
|
|
if ('vimeo' === self.videoOrigin) {
|
|
self.setupVimeoVideo();
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
this.videoOrigin = 'local';
|
|
self.setupLocalVideo();
|
|
}
|
|
|
|
// update video dimension for all players on resize
|
|
this.$window.on( 'resize', nb_.debounce( function() { self.updatePlayerDimensions(); }, 200 ) );
|
|
};
|
|
|
|
|
|
|
|
Plugin.prototype.setupLocalVideo = function() {
|
|
var self = this;
|
|
var _attributes = ['autoplay', 'muted', 'playsinline'];
|
|
if ( self.options.loop ) {
|
|
_attributes.push('loop');
|
|
}
|
|
_attributes = _attributes.join(' ');
|
|
|
|
if ( self.$backgroundVideoContainer.find('video').length < 1 ) {
|
|
self.$backgroundVideoContainer.append( '<video ' + _attributes + ' class="' + self.options.bgLocalVideoContainerClass +'"></video>');
|
|
}
|
|
self.$backgroundVideoLocal = $( '.' + self.options.bgLocalVideoContainerClass, self.$element );
|
|
self.videoPlayer = self.$backgroundVideoLocal;
|
|
|
|
var startAt = self.options.startAt,
|
|
endAt = self.options.endAt;
|
|
|
|
// Fragment video if conditions are met
|
|
if ( this.isFragmentedVideo ) {
|
|
if ( startAt && endAt < 1 ) {
|
|
this.videoUrl += '#t=' + startAt;
|
|
} else if ( startAt && endAt && endAt > startAt ) {
|
|
this.videoUrl += '#t=' + startAt + ',' + endAt;
|
|
} else if ( startAt < 1 && endAt ) {
|
|
this.videoUrl += '#t=0,' + endAt;
|
|
}
|
|
}
|
|
|
|
self.$backgroundVideoContainer
|
|
.find( '.' + self.options.bgLocalVideoContainerClass )
|
|
.attr('src', this.videoUrl )
|
|
.one( 'canplay', function() {
|
|
self.updatePlayerDimensions();
|
|
self.$backgroundVideoContainer.css('opacity', 1);
|
|
});
|
|
|
|
// if video is fragmented and should be looped, we need to hack because when fragmented, looping won't be done automatically
|
|
var isVideoUrlSetupForFragment = -1 !== this.videoUrl.indexOf('#t=');
|
|
if ( self.options.loop && isVideoUrlSetupForFragment ) {
|
|
// solution found here : https://stackoverflow.com/questions/23304021/loop-video-with-media-fragments
|
|
self.videoPlayer.on( 'timeupdate', nb_.throttle( function () {
|
|
if( this.currentTime > endAt ) {
|
|
this.currentTime = startAt;
|
|
this.play();
|
|
}
|
|
}, 100 ) );
|
|
} else {
|
|
// If not looped, remove video after play
|
|
self.videoPlayer.on( 'ended', function () {
|
|
self.videoPlayer.hide();
|
|
});
|
|
}
|
|
};
|
|
|
|
Plugin.prototype.setupYtubeVideo = function() {
|
|
var self = this;
|
|
|
|
// params : { initial : true, videoPlayer : self.videoPlayer }
|
|
var _setupYtubeLoopWhenFragmented = function( params ) {
|
|
var _self_ = this;
|
|
|
|
// container still exists?
|
|
if ( !params.videoPlayer.getIframe().contentWindow )
|
|
return;
|
|
|
|
var startAt = self.options.startAt,
|
|
endAt = self.options.endAt;
|
|
|
|
if ( !self.options.loop && !params.initial ) {
|
|
params.videoPlayer.stopVideo();
|
|
return;
|
|
}
|
|
|
|
params.videoPlayer.seekTo( startAt );
|
|
|
|
if ( endAt ) {
|
|
var fragmentLength = endAt - startAt + 1;
|
|
setTimeout(function () {
|
|
_setupYtubeLoopWhenFragmented( { initial : false , videoPlayer : params.videoPlayer } );
|
|
}, fragmentLength * 1000 );
|
|
}
|
|
};
|
|
|
|
// Chrome browser may not fire the `PLAYING` state on starttime
|
|
// if ( window.chrome ) {
|
|
// startStateCode = self.apiInstance.PlayerState.UNSTARTED;
|
|
// }
|
|
// Updated in January 2020
|
|
// fixes https://github.com/presscustomizr/nimble-builder/issues/574
|
|
var startStateCode = self.apiInstance.PlayerState.UNSTARTED;
|
|
|
|
self.$backgroundVideoContainer.addClass( self.options.bgLoadingClass );
|
|
this.videoPlayer = new self.apiInstance.Player( self.$backgroundVideoContainer.find( '.' + self.options.bgYoutubeVideoContainerClass )[0], {
|
|
videoId: self.videoId,
|
|
events: {
|
|
onReady: function onReady() {
|
|
self.videoPlayer.mute();
|
|
self.updatePlayerDimensions();
|
|
if ( self.isFragmentedVideo ) {
|
|
_setupYtubeLoopWhenFragmented( { initial : true, videoPlayer : self.videoPlayer } );
|
|
}
|
|
self.videoPlayer.playVideo();
|
|
},
|
|
onStateChange: function onStateChange(event) {
|
|
switch (event.data) {
|
|
case startStateCode:
|
|
self.$backgroundVideoContainer.removeClass( self.options.bgLoadingClass );
|
|
self.$backgroundVideoContainer.css('opacity', 1);
|
|
break;
|
|
|
|
case self.apiInstance.PlayerState.ENDED:
|
|
self.videoPlayer.seekTo( self.options.startAt );
|
|
|
|
if ( !self.options.loop ) {
|
|
self.videoPlayer.destroy();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
playerVars: {
|
|
controls: 0,
|
|
rel: 0
|
|
}
|
|
});
|
|
};//setupYtubeVideo
|
|
|
|
|
|
Plugin.prototype.setupVimeoVideo = function() {
|
|
var self = this;
|
|
|
|
var _setupVimeoFragment = function() {
|
|
var _self_ = this;
|
|
var startAt = self.options.startAt,
|
|
endAt = self.options.endAt;
|
|
// If a start time is defined, set the start time
|
|
if ( startAt ) {
|
|
self.videoPlayer.on( 'play', function ( data ) {
|
|
if ( 0 === data.seconds ) {
|
|
self.videoPlayer.setCurrentTime( startAt );
|
|
}
|
|
});
|
|
} // If an end time is defined, handle ending the video
|
|
|
|
|
|
self.videoPlayer.on( 'timeupdate', function (data) {
|
|
if ( endAt && endAt < data.seconds ) {
|
|
if ( ! self.options.loop ) {
|
|
// Stop at user-defined end time if not loop
|
|
self.videoPlayer.pause();
|
|
} else {
|
|
// Go to start time if loop
|
|
self.videoPlayer.setCurrentTime( startAt );
|
|
}
|
|
} // If start time is defined but an end time is not, go to user-defined start time at video end.
|
|
// Vimeo JS API has an 'ended' event, but it never fires when infinite loop is defined, so we
|
|
// get the video duration (returns a promise) then use duration-0.5s as end time
|
|
|
|
|
|
self.videoPlayer.getDuration().then( function ( duration ) {
|
|
if ( startAt && !endAt && data.seconds > duration - 0.5 ) {
|
|
self.videoPlayer.setCurrentTime( startAt );
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
self.$backgroundVideoContainer.addClass( self.options.bgLoadingClass );
|
|
// doc : https://github.com/vimeo/player.js
|
|
this.videoPlayer = new self.apiInstance.Player( self.$backgroundVideoContainer, {
|
|
id: self.videoId,
|
|
width: self.$backgroundVideoContainer.outerWidth().width,
|
|
autoplay: true,
|
|
loop: self.options.loop,
|
|
transparent: false,
|
|
playsinline: false,
|
|
background: true,
|
|
muted: true,
|
|
controls:false//<= hide all elements in the player (play bar, sharing buttons, etc)
|
|
} ); // Handle user-defined start/end times
|
|
|
|
if ( this.isFragmentedVideo ) {
|
|
_setupVimeoFragment();
|
|
}
|
|
|
|
this.videoPlayer.ready().then(function () {
|
|
$( self.videoPlayer.element ).addClass('sek-background-vimeo-element');
|
|
self.updatePlayerDimensions();
|
|
nb_.delay( function() {
|
|
self.$backgroundVideoContainer.removeClass( self.options.bgLoadingClass );
|
|
self.$backgroundVideoContainer.css('opacity', 1);
|
|
}, 200 );
|
|
});
|
|
};//setupVimeoVideo
|
|
|
|
|
|
|
|
Plugin.prototype.updatePlayerDimensions = function() {
|
|
var self = this;
|
|
if ( 'local' !== this.videoOrigin && !this.videoPlayer )
|
|
return;
|
|
|
|
var $playerElement;
|
|
if ('youtube' === this.videoOrigin) {
|
|
$playerElement = $( this.videoPlayer.getIframe() );
|
|
} else if ('vimeo' === this.videoOrigin ) {
|
|
$playerElement = $( this.videoPlayer.element );
|
|
} else if ('local' === this.videoOrigin ) {
|
|
$playerElement = self.videoPlayer;
|
|
}
|
|
|
|
if ( !$playerElement )
|
|
return;
|
|
|
|
var aspectRatioSetting = '16:9';
|
|
|
|
if ('vimeo' === this.videoOrigin) {
|
|
aspectRatioSetting = $playerElement[0].width + ':' + $playerElement[0].height;
|
|
}
|
|
|
|
var containerWidth = this.$backgroundVideoContainer.outerWidth(),
|
|
containerHeight = this.$backgroundVideoContainer.outerHeight(),
|
|
aspectRatioArray = aspectRatioSetting.split(':'),
|
|
aspectRatio = aspectRatioArray[0] / aspectRatioArray[1],
|
|
ratioWidth = containerWidth / aspectRatio,
|
|
ratioHeight = containerHeight * aspectRatio,
|
|
isWidthFixed = containerWidth / containerHeight > aspectRatio;
|
|
|
|
$playerElement
|
|
.width( isWidthFixed ? containerWidth : ratioHeight )
|
|
.height( isWidthFixed ? ratioWidth : containerHeight );
|
|
};
|
|
|
|
|
|
|
|
Plugin.prototype.getVideoApiHelpers = function( videoOrigin ) {
|
|
var self = this;
|
|
return {
|
|
insertAPI : function() {
|
|
$('script:first').before( $('<script>', {
|
|
src : 'youtube' === videoOrigin ? 'https://www.youtube.com/iframe_api' : 'https://player.vimeo.com/api/player.js',
|
|
id : 'sek-' + videoOrigin + '-api'
|
|
}));
|
|
},
|
|
//see /wp-includes/js/mediaelement/renderers/vimeo.js
|
|
getVideoIDFromURL : function(url) {
|
|
if (url === undefined || url === null) {
|
|
return null;
|
|
}
|
|
if ( 'youtube' === videoOrigin ) {
|
|
var videoIDParts = url.match( /^(?:https?:\/\/)?(?:www\.)?(?:m\.)?(?:youtu\.be\/|youtube\.com\/(?:(?:watch)?\?(?:.*&)?vi?=|(?:embed|v|vi|user)\/))([^?&"'>]+)/ );
|
|
return videoIDParts && videoIDParts[1];
|
|
} else {
|
|
var parts = url.split('?');
|
|
url = parts[0];
|
|
return parseInt(url.substring(url.lastIndexOf('/') + 1), 10);
|
|
}
|
|
},
|
|
onApiReady: function(callback) {
|
|
var self = this;
|
|
if ( $('#' + 'sek-' + videoOrigin + '-api').length < 1 ) {
|
|
this.insertAPI();
|
|
}
|
|
var _isPlayerRemoteApiLoaded = 'youtube' === videoOrigin ? ( window.YT && YT.loaded ) : window.Vimeo;
|
|
if ( _isPlayerRemoteApiLoaded ) {
|
|
callback( 'youtube' === videoOrigin ? YT : Vimeo );
|
|
} else {
|
|
// If not ready check again by timeout..
|
|
setTimeout(function () {
|
|
self.onApiReady(callback);
|
|
}, 350);
|
|
}
|
|
}
|
|
};//defaults
|
|
};//getVideoApiHelpers
|
|
|
|
|
|
// prevents against multiple instantiations
|
|
$.fn[pluginName] = function ( options ) {
|
|
return this.each(function () {
|
|
if (!$.data(this, 'plugin_' + pluginName)) {
|
|
$.data(this, 'plugin_' + pluginName,
|
|
new Plugin( this, options ));
|
|
}
|
|
});
|
|
};
|
|
|
|
})( jQuery, window );
|
|
|
|
|
|
/* ------------------------------------------------------------------------- *
|
|
* VIDEO BACKGROUND FOR SECTIONS AND COLUMNS
|
|
/* ------------------------------------------------------------------------- */
|
|
// - insert bg video container
|
|
// - inject player api script
|
|
// - print video iframe
|
|
// - on api ready, do stuff
|
|
jQuery( function($){
|
|
var _maybeInstantiatePlayers = function() {
|
|
$('[data-sek-video-bg-src]').each(function() {
|
|
if ( 'section' === $(this).data('sek-level') || 'column' === $(this).data('sek-level') ) {
|
|
if ( ! $(this).data('sek-player-instantiated') ) {
|
|
$(this).nimbleLoadVideoBg( { lazyLoad: sekFrontLocalized.video_bg_lazyload_enabled } );
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
// on page load
|
|
_maybeInstantiatePlayers();
|
|
|
|
// WHEN CUSTOMIZING
|
|
// on various nimble events when customizing
|
|
nb_.cachedElements.$body.on('sek-section-added sek-columns-refreshed', function( evt, params ){
|
|
_maybeInstantiatePlayers();
|
|
});
|
|
|
|
// On module refreshed inside a section, simply trigger a dimensions update
|
|
nb_.cachedElements.$body.on('sek-modules-refreshed', function( evt, params ){
|
|
$('[data-sek-video-bg-src]').each(function() {
|
|
$(this).trigger('refresh-video-dimensions');
|
|
});
|
|
});
|
|
|
|
// when customizing the level
|
|
nb_.cachedElements.$body.on('sek-level-refreshed', function( evt, params ){
|
|
// when removing a level => no params.id
|
|
if ( !params || !nb_.isObject( params ) || !params.id )
|
|
return;
|
|
|
|
var $levelRefreshed = $( '[data-sek-id="'+ params.id +'"][data-sek-video-bg-src]' );
|
|
if ( $levelRefreshed.length > 0 && !$levelRefreshed.data('sek-player-instantiated') ) {
|
|
$levelRefreshed.nimbleLoadVideoBg( { lazyLoad: sekFrontLocalized.video_bg_lazyload_enabled } );
|
|
}
|
|
});
|
|
});
|
|
|
|
};/////////////// callbackFunc
|
|
|
|
// on 'nb-app-ready', jQuery is loaded
|
|
nb_.listenTo('nb-app-ready', function(){
|
|
callbackFunc();
|
|
nb_.emit('nb-videobg-parsed');
|
|
});
|
|
}(window, document));
|
|
|