(function(){
'use strict';
/**
* Video Panorama
* @constructor
* @param {string} src - Equirectangular video url
* @param {object} [options] - Option for video settings
* @param {HTMLElement} [options.videoElement] - HTML5 video element contains the video
* @param {boolean} [options.loop=true] - Specify if the video should loop in the end
* @param {boolean} [options.muted=false] - Mute the video or not
* @param {boolean} [options.autoplay=false] - Specify if the video should auto play
* @param {boolean} [options.playsinline=true] - Specify if video should play inline for iOS. If you want it to auto play inline, set both autoplay and muted options to true
* @param {string} [options.crossOrigin="anonymous"] - Sets the cross-origin attribute for the video, which allows for cross-origin videos in some browsers (Firefox, Chrome). Set to either "anonymous" or "use-credentials".
* @param {number} [radius=5000] - The minimum radius for this panoram
*/
PANOLENS.VideoPanorama = function ( src, options, radius ) {
radius = radius || 5000;
var geometry = new THREE.SphereGeometry( radius, 60, 40 ),
material = new THREE.MeshBasicMaterial( { opacity: 0, transparent: true } );
PANOLENS.Panorama.call( this, geometry, material );
this.src = src;
this.options = options || {};
this.options.playsinline = this.options.playsinline !== false ? true : false;
this.videoElement = undefined;
this.videoRenderObject = undefined;
this.videoProgress = 0;
this.isIOS = /iPhone|iPad|iPod/i.test( navigator.userAgent );
this.isMobile = this.isIOS || /Android|BlackBerry|Opera Mini|IEMobile/i.test( navigator.userAgent );
this.addEventListener( 'leave', this.pauseVideo.bind( this ) );
this.addEventListener( 'enter-fade-start', this.resumeVideoProgress.bind( this ) );
this.addEventListener( 'video-toggle', this.toggleVideo.bind( this ) );
this.addEventListener( 'video-time', this.setVideoCurrentTime.bind( this ) );
};
PANOLENS.VideoPanorama.prototype = Object.create( PANOLENS.Panorama.prototype );
PANOLENS.VideoPanorama.constructor = PANOLENS.VideoPanorama;
/**
* Load video panorama
* @param {string} src - The video url
* @param {object} options - Option object containing videoElement
* @fires PANOLENS.Panorama#panolens-viewer-handler
*/
PANOLENS.VideoPanorama.prototype.load = function ( src, options ) {
var scope = this;
src = ( src || this.src ) || '';
options = ( options || this.options ) || {};
this.videoElement = options.videoElement || document.createElement( 'video' );
this.videoElement.muted = options.muted || false;
this.videoElement.loop = ( options.loop !== undefined ) ? options.loop : true;
this.videoElement.autoplay = ( options.autoplay !== undefined ) ? options.autoplay : false;
this.videoElement.crossOrigin = ( options.crossOrigin !== undefined ) ? options.crossOrigin : "anonymous";
// iphone inline player
if (options.playsinline) {
this.videoElement.setAttribute( "playsinline", "" );
this.videoElement.setAttribute( "webkit-playsinline", "" );
}
var onloadeddata = function(){
scope.onProgress( { loaded: 1, total: 1 } );
scope.setVideoTexture( scope.videoElement );
if ( scope.videoElement.autoplay ) {
/**
* Viewer handler event
* @type {object}
* @property {string} method - 'updateVideoPlayButton'
* @property {boolean} data - Pause video or not
*/
scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'updateVideoPlayButton', data: false } );
}
// For mobile silent autoplay
if ( scope.isMobile ) {
if ( scope.videoElement.autoplay && scope.videoElement.muted ) {
/**
* Viewer handler event
* @type {object}
* @property {string} method - 'updateVideoPlayButton'
* @property {boolean} data - Pause video or not
*/
scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'updateVideoPlayButton', data: false } );
} else {
/**
* Viewer handler event
* @type {object}
* @property {string} method - 'updateVideoPlayButton'
* @property {boolean} data - Pause video or not
*/
scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'updateVideoPlayButton', data: true } );
}
}
scope.onLoad();
};
/**
* Ready state of the audio/video element
* 0 = HAVE_NOTHING - no information whether or not the audio/video is ready
* 1 = HAVE_METADATA - metadata for the audio/video is ready
* 2 = HAVE_CURRENT_DATA - data for the current playback position is available, but not enough data to play next frame/millisecond
* 3 = HAVE_FUTURE_DATA - data for the current and at least the next frame is available
* 4 = HAVE_ENOUGH_DATA - enough data available to start playing
*/
if ( this.videoElement.readyState > 2 ) {
onloadeddata();
} else {
if ( !this.videoElement.querySelectorAll('source').length || !this.videoElement.src ) {
this.videoElement.src = src;
}
this.videoElement.load();
}
this.videoElement.onloadeddata = onloadeddata;
this.videoElement.ontimeupdate = function ( event ) {
scope.videoProgress = this.duration >= 0 ? this.currentTime / this.duration : 0;
/**
* Viewer handler event
* @type {object}
* @property {string} method - 'onVideoUpdate'
* @property {number} data - The percentage of video progress. Range from 0.0 to 1.0
*/
scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'onVideoUpdate', data: scope.videoProgress } );
};
this.videoElement.addEventListener( 'ended', function () {
if ( !scope.options.loop ) {
scope.resetVideo();
scope.dispatchEvent( { type: 'panolens-viewer-handler', method: 'updateVideoPlayButton', data: true } );
}
}, false );
};
/**
* Set video texture
* @param {HTMLVideoElement} video - The html5 video element
* @fires PANOLENS.Panorama#panolens-viewer-handler
*/
PANOLENS.VideoPanorama.prototype.setVideoTexture = function ( video ) {
var videoTexture, videoRenderObject, scene;
if ( !video ) return;
videoTexture = new THREE.VideoTexture( video );
videoTexture.minFilter = THREE.LinearFilter;
videoTexture.magFilter = THREE.LinearFilter;
videoTexture.format = THREE.RGBFormat;
videoRenderObject = {
video : video,
videoTexture: videoTexture
};
if ( this.isIOS ){
enableInlineVideo( video );
}
this.updateTexture( videoTexture );
this.videoRenderObject = videoRenderObject;
};
PANOLENS.VideoPanorama.prototype.reset = function () {
this.videoElement = undefined;
PANOLENS.Panorama.prototype.reset.call( this );
};
/**
* Check if video is paused
* @return {boolean} - is video paused or not
*/
PANOLENS.VideoPanorama.prototype.isVideoPaused = function () {
return this.videoRenderObject.video.paused;
};
/**
* Toggle video to play or pause
*/
PANOLENS.VideoPanorama.prototype.toggleVideo = function () {
if ( this.videoRenderObject && this.videoRenderObject.video ) {
if ( this.isVideoPaused() ) {
this.videoRenderObject.video.play();
} else {
this.videoRenderObject.video.pause();
}
}
};
/**
* Set video currentTime
* @param {object} event - Event contains percentage. Range from 0.0 to 1.0
*/
PANOLENS.VideoPanorama.prototype.setVideoCurrentTime = function ( event ) {
if ( this.videoRenderObject && this.videoRenderObject.video && !Number.isNaN(event.percentage) && event.percentage !== 1 ) {
this.videoRenderObject.video.currentTime = this.videoRenderObject.video.duration * event.percentage;
this.dispatchEvent( { type: 'panolens-viewer-handler', method: 'onVideoUpdate', data: event.percentage } );
}
};
/**
* Play video
*/
PANOLENS.VideoPanorama.prototype.playVideo = function () {
if ( this.videoRenderObject && this.videoRenderObject.video && this.isVideoPaused() ) {
this.videoRenderObject.video.play();
}
/**
* Play event
* @type {object}
* @event 'play'
* */
this.dispatchEvent( { type: 'play' } );
};
/**
* Pause video
*/
PANOLENS.VideoPanorama.prototype.pauseVideo = function () {
if ( this.videoRenderObject && this.videoRenderObject.video && !this.isVideoPaused() ) {
this.videoRenderObject.video.pause();
}
/**
* Pause event
* @type {object}
* @event 'pause'
* */
this.dispatchEvent( { type: 'pause' } );
};
/**
* Resume video
*/
PANOLENS.VideoPanorama.prototype.resumeVideoProgress = function () {
if ( this.videoElement.autoplay && !this.isMobile ) {
this.playVideo();
/**
* Viewer handler event
* @type {object}
* @property {string} method - 'updateVideoPlayButton'
* @property {boolean} data - Pause video or not
*/
this.dispatchEvent( { type: 'panolens-viewer-handler', method: 'updateVideoPlayButton', data: false } );
} else {
this.pauseVideo();
/**
* Viewer handler event
* @type {object}
* @property {string} method - 'updateVideoPlayButton'
* @property {boolean} data - Pause video or not
*/
this.dispatchEvent( { type: 'panolens-viewer-handler', method: 'updateVideoPlayButton', data: true } );
}
this.setVideoCurrentTime( { percentage: this.videoProgress } );
};
/**
* Reset video at stating point
*/
PANOLENS.VideoPanorama.prototype.resetVideo = function () {
if ( this.videoRenderObject && this.videoRenderObject.video ) {
this.setVideoCurrentTime( { percentage: 0 } );
}
};
/**
* Check if video is muted
* @return {boolean} - is video muted or not
*/
PANOLENS.VideoPanorama.prototype.isVideoMuted = function () {
return this.videoRenderObject.video.muted;
};
/**
* Mute video
*/
PANOLENS.VideoPanorama.prototype.muteVideo = function () {
if ( this.videoRenderObject && this.videoRenderObject.video && !this.isVideoMuted() ) {
this.videoRenderObject.video.muted = true;
}
this.dispatchEvent( { type: 'volumechange' } );
};
/**
* Unmute video
*/
PANOLENS.VideoPanorama.prototype.unmuteVideo = function () {
if ( this.videoRenderObject && this.videoRenderObject.video && this.isVideoMuted() ) {
this.videoRenderObject.video.muted = false;
}
this.dispatchEvent( { type: 'volumechange' } );
};
/**
* Returns the video element
*/
PANOLENS.VideoPanorama.prototype.getVideoElement = function () {
return this.videoRenderObject.video;
};
/**
* Dispose video panorama
*/
PANOLENS.VideoPanorama.prototype.dispose = function () {
this.resetVideo();
this.pauseVideo();
this.removeEventListener( 'leave', this.pauseVideo.bind( this ) );
this.removeEventListener( 'enter-fade-start', this.resumeVideoProgress.bind( this ) );
this.removeEventListener( 'video-toggle', this.toggleVideo.bind( this ) );
this.removeEventListener( 'video-time', this.setVideoCurrentTime.bind( this ) );
PANOLENS.Panorama.prototype.dispose.call( this );
};
})();