// -------------------------------------------------------------------------------------------
//
// AVIA VIDEO API - make sure that youtube, vimeo and html 5 use the same interface
//
// requires froogaloop vimeo library and youtube iframe api (yt can be loaded async)
//
// -------------------------------------------------------------------------------------------


(function($)
{
    "use strict";

	$.AviaVideoAPI  =  function(options, video, option_container)
	{
		this.videoElement = video;

		// actual video element. either iframe or video
		this.$video	= $( video );

		// container where the AviaVideoAPI object will be stored as data, and that can receive events like play, pause etc
		// also the container that allows to overwrite javacript options by adding html data- attributes
		this.$option_container = option_container ? $( option_container ) : this.$video;

		// button to click to actually start the loading process of the video
		this.load_btn = this.$option_container.find('.av-click-to-play-overlay');

		// contains video list
		this.video_wrapper = this.$video.parents('ul').eq(0);

		//check if we should load immediately or on click
		this.lazy_load = this.video_wrapper.hasClass('av-show-video-on-click') ? true : false;

		//mobile device?
		this.isMobile = $.avia_utilities.isMobile;

		//iamge fallback use
		this.fallback = this.isMobile ? this.$option_container.is('.av-mobile-fallback-image') : false;

		if( this.fallback )
		{
			return;
		}

		// set up the whole api object
		this._init( options );
	};

	$.AviaVideoAPI.defaults  = {

		loop: false,
		mute: false,
		controls: false,
		events: 'play pause mute unmute loop toggle reset unload'

	};

	$.AviaVideoAPI.apiFiles =
    {
    	youtube : {loaded: false, src: 'https://www.youtube.com/iframe_api' }
    };

    $.AviaVideoAPI.players =
    {

    };

  	$.AviaVideoAPI.prototype =
    {
    	_init: function( options )
    	{
			// set slider options
			this.options = this._setOptions(options);

			// info which video service we are using: html5, vimeo or youtube
			this.type = this._getPlayerType();

			this.player = false;

			// store the player object to the this.player variable created by one of the APIs (mediaelement, youtube, vimeo)
			this._bind_player();

			// set to true once the events are bound so it doesnt happen a second time by accident or racing condition
			this.eventsBound = false;

			// info if the video is playing
			this.playing = false;

			//set css class that video is currently not playing
			this.$option_container.addClass('av-video-paused');

			//play pause indicator
			this.pp = $.avia_utilities.playpause(this.$option_container);
    	},


    	//set the video options by first merging the default options and the passed options, then checking the video element if any data attributes overwrite the option set
    	_setOptions: function(options)
		{
			var newOptions 	= $.extend( true, {}, $.AviaVideoAPI.defaults, options ),
				htmlData 	= this.$option_container.data(),
				i 			= "";

			//overwritte passed option set with any data properties on the html element
			for( i in htmlData )
			{
				if( htmlData.hasOwnProperty(i) && (typeof htmlData[i] === "string" || typeof htmlData[i] === "number" || typeof htmlData[i] === "boolean") )
				{
					newOptions[i] = htmlData[i];
				}
			}

			return newOptions;
		},

		//get the player type
		_getPlayerType: function()
		{
			var vid_src = this.$video.get(0).src || this.$video.data('src');

			if( this.$video.is('video') )
			{
				return 'html5';
			}

			if( this.$video.is('.av_youtube_frame') )
			{
				return 'youtube';
			}

			if( vid_src.indexOf('vimeo.com') != -1 )
			{
				return 'vimeo';
			}
			if( vid_src.indexOf('youtube.com') != -1)
			{
				return 'youtube';
			}
		},

		_bind_player: function()
		{
			var _self = this;

			//	check if videos are disabled by user setting via cookie - or user must opt in.
			var cookie_check = $('html').hasClass('av-cookies-needs-opt-in') || $('html').hasClass('av-cookies-can-opt-out');
			var allow_continue = true;
			var silent_accept_cookie = $('html').hasClass('av-cookies-user-silent-accept');
			var self_hosted = 'html5' == this.type;

			if( cookie_check && ! silent_accept_cookie && ! self_hosted )
			{
				if( ! document.cookie.match(/aviaCookieConsent/) || $('html').hasClass('av-cookies-session-refused') )
				{
					allow_continue = false;
				}
				else
				{
					if( ! document.cookie.match(/aviaPrivacyRefuseCookiesHideBar/) )
					{
						allow_continue = false;
					}
					else if( ! document.cookie.match(/aviaPrivacyEssentialCookiesEnabled/) )
					{
						allow_continue = false;
					}
					else if( document.cookie.match(/aviaPrivacyVideoEmbedsDisabled/) )
					{
						allow_continue = false;
					}
				}
			}

			//	allow 3-rd party plugins to hook (see enfold\config-cookiebot\cookiebot.js)
			if( window['wp'] && wp.hooks )
			{
				allow_continue = wp.hooks.applyFilters( 'aviaCookieConsent_allow_continue', allow_continue );
			}

			let allow_class = allow_continue ? 'avia-video-cookie-allowed' : 'avia-video-cookie-not-allowed';
			$('html').addClass( allow_class );
			
			if( ! allow_continue )
			{
				this._use_external_link();
				return;
			}

			if(this.lazy_load && this.load_btn.length && this.type != "html5")
			{
				this.$option_container.addClass('av-video-lazyload');

				this.load_btn.on('click', function()
				{
					_self.load_btn.remove();
					_self._setPlayer();
				});
			}
			else
			{
				this.lazy_load = false;
				this._setPlayer();
			}
		},

		//if the user has disabled video slides via cookie a click event will open the video externally
		_use_external_link: function()
		{
			//display the play button no matter what
			this.$option_container.addClass('av-video-lazyload');

			this.load_btn.on('click', function(e)
			{
				if (e.originalEvent === undefined) return; //human click only

				var src_url = $(this).parents('.avia-slide-wrap').find('div[data-original_url]').data('original_url');
				if( src_url ) window.open(src_url , '_blank');
			});
		},

		//get the player object
		_setPlayer: function()
		{
			var _self = this;

			switch( this.type )
			{
				case "html5":

					this.player = this.$video.data('mediaelementplayer');

					//apply fallback. sometimes needed for safari
					if( ! this.player )
					{
						this.$video.data('mediaelementplayer', $.AviaVideoAPI.players[ this.$video.attr('id').replace(/_html5/,'') ] );
						this.player = this.$video.data('mediaelementplayer');
					}

					this._playerReady();
					break;

				case "vimeo":

					//we need to create an iframe and append it to the html first
					var ifrm 	= document.createElement("iframe");
					var $ifrm	= $( ifrm );

					//note: unmuted vimeo videos in chrome do often times not work due to chrome blocking them
					ifrm.onload = function()
					{
						_self.player = Froogaloop( ifrm );
						_self._playerReady();
						_self.$option_container.trigger('av-video-loaded');
					};

					ifrm.setAttribute("src", this.$video.data('src') );

					//we replace the old html structure with the iframe
					$ifrm.insertAfter( this.$video );
					this.$video.remove();
					this.$video = ifrm;
					break;

				case "youtube":

					this._getAPI(this.type);
					$('body').on('av-youtube-iframe-api-loaded', function(){ _self._playerReady(); });
					break;
			}
		},

		_getAPI: function( api )
		{
			//make sure the api file is loaded only once
			if($.AviaVideoAPI.apiFiles[api].loaded === false)
			{
				$.AviaVideoAPI.apiFiles[api].loaded = true;
				//load the file async
				var tag		= document.createElement('script'),
					first	= document.getElementsByTagName('script')[0];

				tag.src = $.AviaVideoAPI.apiFiles[api].src;
      			first.parentNode.insertBefore(tag, first);
			}
		},

		//wait for player to be ready, then bind events
		_playerReady: function()
		{
			var _self = this;

			this.$option_container.on('av-video-loaded', function(){ _self._bindEvents(); });

			switch(this.type)
			{
				case "html5":
					this.$video.on( 'av-mediajs-loaded', function(){ _self.$option_container.trigger('av-video-loaded'); } );
					this.$video.on( 'av-mediajs-ended' , function(){ _self.$option_container.trigger('av-video-ended');  } );
					break;
				case "vimeo":
					//finish event must be applied after ready event for firefox
					_self.player.addEvent('ready',  function()
					{
						_self.$option_container.trigger('av-video-loaded');
						_self.player.addEvent('finish', function(){ _self.$option_container.trigger('av-video-ended');  });
					});
					break;

				case "youtube":

					var params = _self.$video.data();

					if( _self._supports_video() )
					{
						params.html5 = 1;
					}

					_self.player = new YT.Player(_self.$video.attr('id'),
					{
						videoId: params.videoid,
						height: _self.$video.attr('height'),
						width: _self.$video.attr('width'),
						playerVars: params,
						events: {
						'onReady': function(){ _self.$option_container.trigger('av-video-loaded'); },
						'onError': function(player){ $.avia_utilities.log('YOUTUBE ERROR:', 'error', player); },
						'onStateChange': function(event){
							if (event.data === YT.PlayerState.ENDED)
							{
								var command = _self.options.loop != false ? 'loop' : 'av-video-ended';
								_self.$option_container.trigger(command);
							}
						  }
						}
					});
					break;
			}

			//fallback always trigger after 2 seconds
			setTimeout(function()
			{
				if(_self.eventsBound == true || typeof _self.eventsBound == 'undefined' || _self.type == 'youtube' )
				{
					return;
				}

				$.avia_utilities.log('Fallback Video Trigger "' + _self.type + '":', 'log', _self );

				_self.$option_container.trigger('av-video-loaded');

			}, 2000 );
		},

		//bind events we should listen to, to the player
		_bindEvents: function()
		{
			if( this.eventsBound == true || typeof this.eventsBound == 'undefined')
			{
				return;
			}

			var _self = this, volume = 'unmute';

			this.eventsBound = true;

			this.$option_container.on(this.options.events, function(e)
			{
				_self.api(e.type);
			});

			if( !_self.isMobile )
			{
				//set up initial options
				if( this.options.mute != false )
				{
					volume = "mute";
				}

				if( this.options.loop != false )
				{
					_self.api('loop');
				}

				_self.api(volume);
			}

			//set timeout to prevent racing conditions with other scripts
			setTimeout( function()
			{
				_self.$option_container.trigger('av-video-events-bound').addClass('av-video-events-bound');
			}, 50 );
		},


		_supports_video: function()
		{
		  return !!document.createElement('video').canPlayType;
		},


		/************************************************************************
		PUBLIC Methods
		*************************************************************************/


		api: function( action )
		{
			//commands on mobile can not be executed if the player was not started manually
			if(this.isMobile && !this.was_started()) return;

			// prevent calling of unbound function
			if(this.options.events.indexOf(action) === -1) return;

			// broadcast that the command was executed
			this.$option_container.trigger('av-video-'+action+'-executed');

			// console.log("video player api action: " + action);

			// calls the function based on action. eg: _html5_play()
			if(typeof this[ '_' + this.type + '_' + action] == 'function')
			{
				this[ '_' + this.type + '_' + action].call(this);
			}

			//call generic function eg: _toggle() or _play()
			if(typeof this[ '_' + action] == 'function')
			{
				this[ '_' + action].call(this);
			}
		},

		was_started: function()
		{
			if(!this.player) return false;

			switch(this.type)
			{
				case "html5":
					if( this.player.getCurrentTime() > 0 )
					{
						return true;
					}
					break;

				case "vimeo":
					if( this.player.api('getCurrentTime') > 0 )
					{
						return true;
					}
					break;

				case "youtube":
					if( this.player.getPlayerState() !== -1 )
					{
						return true;
					}
					break;
			}

			return false;
		},

		/************************************************************************
		Generic Methods, are always executed and usually set variables
		*************************************************************************/
		_play: function()
		{
			this.playing = true;
			this.$option_container.addClass('av-video-playing').removeClass('av-video-paused');
		},

		_pause: function()
		{
			this.playing = false;
			this.$option_container.removeClass('av-video-playing').addClass('av-video-paused');
		},

		_loop: function()
		{
			this.options.loop = true;
		},

		_toggle: function( )
		{
			var command = this.playing == true ? 'pause' : 'play';

			this.api(command);
			this.pp.set(command);
		},


		/************************************************************************
		VIMEO Methods
		*************************************************************************/

		_vimeo_play: function( )
		{
			this.player.api('play');
		},

		_vimeo_pause: function( )
		{
			this.player.api('pause');
		},

		_vimeo_mute: function( )
		{
			this.player.api('setVolume', 0);
		},

		_vimeo_unmute: function( )
		{
			this.player.api('setVolume', 0.7);
		},

		_vimeo_loop: function( )
		{
			// currently throws error, must be set in iframe
			// this.player.api('setLoop', true);
		},

		_vimeo_reset: function( )
		{
			this.player.api('seekTo',0);
		},

		_vimeo_unload: function()
		{
			this.player.api('unload');
		},

		/************************************************************************
		YOUTUBE Methods
		*************************************************************************/

		_youtube_play: function( )
		{
			this.player.playVideo();
		},

		_youtube_pause: function( )
		{
			this.player.pauseVideo();
		},

		_youtube_mute: function( )
		{
			this.player.mute();
		},

		_youtube_unmute: function( )
		{
			this.player.unMute();
		},

		_youtube_loop: function( )
		{
			// does not work properly with iframe api. needs to manual loop on "end" event
			// this.player.setLoop(true);
			if(this.playing == true) this.player.seekTo(0);
		},

		_youtube_reset: function( )
		{
			this.player.stopVideo();
		},

		_youtube_unload: function()
		{
			this.player.clearVideo();
		},

		/************************************************************************
		HTML5 Methods
		*************************************************************************/

		_html5_play: function( )
		{
			//disable stoping of other videos in case the user wants to run section bgs
			if(this.player)
			{
				this.player.options.pauseOtherPlayers = false;
				this.player.play();
			}

		},

		_html5_pause: function( )
		{
			if(this.player) this.player.pause();
		},

		_html5_mute: function( )
		{
			if(this.player) this.player.setMuted(true);
		},

		_html5_unmute: function( )
		{
			if(this.player) this.player.setVolume(0.7);
		},

		_html5_loop: function( )
		{
			if(this.player) this.player.options.loop = true;
		},

		_html5_reset: function( )
		{
			if(this.player) this.player.setCurrentTime(0);
		},

		_html5_unload: function()
		{
			this._html5_pause();
			this._html5_reset();
		}
    };

    //simple wrapper to call the api. makes sure that the api data is not applied twice
    $.fn.aviaVideoApi = function( options , apply_to_parent)
    {
    	return this.each( function()
    	{
    		// by default save the object as data to the initial video.
    		// in the case of slideshows its more benefitial to save it to a parent element (eg: the slide)
    		var applyTo = this;

    		if( apply_to_parent )
    		{
    			applyTo = $(this).parents(apply_to_parent).get(0);
    		}

    		var self = $.data( applyTo, 'aviaVideoApi' );

    		if( ! self )
    		{
    			self = $.data( applyTo, 'aviaVideoApi', new $.AviaVideoAPI( options, this, applyTo ) );
    		}
    	});
    };

})( jQuery );

window.onYouTubeIframeAPIReady = function(){ jQuery('body').trigger('av-youtube-iframe-api-loaded'); };


// Init style shamelessly stolen from jQuery http://jquery.com
// min version: https://f.vimeocdn.com/js/froogaloop2.min.js
var Froogaloop = (function(){
    // Define a local copy of Froogaloop
    function Froogaloop(iframe) {
        // The Froogaloop object is actually just the init constructor
        return new Froogaloop.fn.init(iframe);
    }

    var eventCallbacks = {},
        hasWindowEvent = false,
        isReady = false,
        slice = Array.prototype.slice,
        playerOrigin = '*';

    Froogaloop.fn = Froogaloop.prototype = {
        element: null,

        init: function(iframe) {
            if (typeof iframe === "string") {
                iframe = document.getElementById(iframe);
            }

            this.element = iframe;

            return this;
        },

        /*
         * Calls a function to act upon the player.
         *
         * @param {string} method The name of the Javascript API method to call. Eg: 'play'.
         * @param {Array|Function} valueOrCallback params Array of parameters to pass when calling an API method
         *                                or callback function when the method returns a value.
         */
        api: function(method, valueOrCallback) {
            if (!this.element || !method) {
                return false;
            }

            var self = this,
                element = self.element,
                target_id = element.id !== '' ? element.id : null,
                params = !isFunction(valueOrCallback) ? valueOrCallback : null,
                callback = isFunction(valueOrCallback) ? valueOrCallback : null;

            // Store the callback for get functions
            if (callback) {
                storeCallback(method, callback, target_id);
            }

            postMessage(method, params, element);
            return self;
        },

        /*
         * Registers an event listener and a callback function that gets called when the event fires.
         *
         * @param eventName (String): Name of the event to listen for.
         * @param callback (Function): Function that should be called when the event fires.
         */
        addEvent: function(eventName, callback) {
            if (!this.element) {
                return false;
            }

            var self = this,
                element = self.element,
                target_id = element.id !== '' ? element.id : null;


            storeCallback(eventName, callback, target_id);

            // The ready event is not registered via postMessage. It fires regardless.
            if (eventName != 'ready') {
                postMessage('addEventListener', eventName, element);
            }
            else if (eventName == 'ready' && isReady) {
                callback.call(null, target_id);
            }

            return self;
        },

        /*
         * Unregisters an event listener that gets called when the event fires.
         *
         * @param eventName (String): Name of the event to stop listening for.
         */
        removeEvent: function(eventName) {
            if (!this.element) {
                return false;
            }

            var self = this,
                element = self.element,
                target_id = element.id !== '' ? element.id : null,
                removed = removeCallback(eventName, target_id);

            // The ready event is not registered
            if (eventName != 'ready' && removed) {
                postMessage('removeEventListener', eventName, element);
            }
        }
    };

    /**
     * Handles posting a message to the parent window.
     *
     * @param method (String): name of the method to call inside the player. For api calls
     * this is the name of the api method (api_play or api_pause) while for events this method
     * is api_addEventListener.
     * @param params (Object or Array): List of parameters to submit to the method. Can be either
     * a single param or an array list of parameters.
     * @param target (HTMLElement): Target iframe to post the message to.
     */
    function postMessage(method, params, target) {
        if (!target.contentWindow.postMessage) {
            return false;
        }

        var data = JSON.stringify({
            method: method,
            value: params
        });

        target.contentWindow.postMessage(data, playerOrigin);
    }

    /**
     * Event that fires whenever the window receives a message from its parent
     * via window.postMessage.
     */
    function onMessageReceived(event)
	{

        var data, method;

        try {
            data = JSON.parse(event.data);
            method = data.event || data.method;
        }
        catch(e)  {
            //fail silently... like a ninja!
        }

        if (method == 'ready' && !isReady) {
            isReady = true;
        }

        // Handles messages from the vimeo player only
        if (!(/^https?:\/\/player.vimeo.com/).test(event.origin)) {
            return false;
        }

        if (playerOrigin === '*') {
            playerOrigin = event.origin;
        }
        var value = data.value,
            eventData = data.data,
            target_id = target_id === '' ? null : data.player_id,

            callback = getCallback(method, target_id),
            params = [];

        if (!callback) {
            return false;
        }

        if (value !== undefined) {
            params.push(value);
        }

        if (eventData) {
            params.push(eventData);
        }

        if (target_id) {
            params.push(target_id);
        }

        return params.length > 0 ? callback.apply(null, params) : callback.call();
    }


    /**
     * Stores submitted callbacks for each iframe being tracked and each
     * event for that iframe.
     *
     * @param eventName (String): Name of the event. Eg. api_onPlay
     * @param callback (Function): Function that should get executed when the
     * event is fired.
     * @param target_id (String) [Optional]: If handling more than one iframe then
     * it stores the different callbacks for different iframes based on the iframe's
     * id.
     */
    function storeCallback(eventName, callback, target_id) {
        if (target_id) {
            if (!eventCallbacks[target_id]) {
                eventCallbacks[target_id] = {};
            }
            eventCallbacks[target_id][eventName] = callback;
        }
        else {
            eventCallbacks[eventName] = callback;
        }
    }

    /**
     * Retrieves stored callbacks.
     */
    function getCallback(eventName, target_id) {

	    /*modified by kriesi - removing this will result in a js error. */
        if (target_id && eventCallbacks[target_id] && eventCallbacks[target_id][eventName])
		{
            return eventCallbacks[target_id][eventName];
        }
        else
		{
            return eventCallbacks[eventName];
        }
    }

    function removeCallback(eventName, target_id) {
        if (target_id && eventCallbacks[target_id]) {
            if (!eventCallbacks[target_id][eventName]) {
                return false;
            }
            eventCallbacks[target_id][eventName] = null;
        }
        else {
            if (!eventCallbacks[eventName]) {
                return false;
            }
            eventCallbacks[eventName] = null;
        }

        return true;
    }

    function isFunction(obj) {
        return !!(obj && obj.constructor && obj.call && obj.apply);
    }

    function isArray(obj) {
        return toString.call(obj) === '[object Array]';
    }

    // Give the init function the Froogaloop prototype for later instantiation
    Froogaloop.fn.init.prototype = Froogaloop.fn;

    // Listens for the message event.
    // W3C
    if (window.addEventListener) {
        window.addEventListener('message', onMessageReceived, false);
    }
    // IE
    else {
        window.attachEvent('onmessage', onMessageReceived);
    }

    // Expose froogaloop to the global object
    return (window.Froogaloop = window.$f = Froogaloop);

})();
