/*!
 * jQuery TubePlayer Plugin
 * 
 * version: 1.0.4 (12-Nov-2011)
 * @requires v1.3.2 or later
 *
 * @imports SWFObject - http://code.google.com/p/swfobject/
 *	- Degrades to flash based player if not HTML5/iframe option 
 *
 * Author: Nirvana Tikku (@ntikku / ntikku@gmail.com)
 * Documentation:
 * 		http://www.tikku.com/jquery-youtube-tubeplayer-plugin
 * 
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html 
 */
;(function($){

	// tubeplayer namespace

	var TUBEPLAYER = ".tubeplayer",

		TUBE_PLAYER_CLASS = "jquery-youtube-tubeplayer",

		OPTS = "opts"+TUBEPLAYER;

	//	
	//
	// TubePlayer package 
	//
	//
	var TubePlayer = {};

	// all the instances that exist
	TubePlayer.ytplayers = {}; 

	// local init functions for multiple iframe players
	TubePlayer.inits = []; 

	// no need to import the iframe script multiple times
	TubePlayer.iframeScriptInited = false; 

	// tubeplayer inited flag - for destroy/re-init
	TubePlayer.inited = false;

	//
	//
	// public facing defaults
	//
	//

	$.tubeplayer = {};

	/**
	 * These are all the events that are bound to the YouTube Player
	 * the events can be overridden as they are public.
	 * 
	 * There are several functions that serve as wrappers to be utilized
	 * internally - stateChange, onError, qualityChange. Change them at your 
	 * own risk.
	 */
	$.tubeplayer.defaults = {

		afterReady: function($player){},

		stateChange: function(player){

			var _ret = this.onPlayer;

			return function(state){

				if(typeof(state)=="object")
					state = state.data;

				switch(state){

					case -1: return _ret.unstarted[player]();

					case 0: return _ret.ended[player]();

					case 1: return _ret.playing[player]();

					case 2: return _ret.paused[player]();

					case 3: return _ret.buffering[player]();

					case 5: return _ret.cued[player]();

					default: return null;

				}
			}

		},

		onError: function(player){

			var _ret = this.onErr;

			return function(errorCode){

				if(typeof(errorCode)=="object")
					errorCode = errorCode.data;

				switch(errorCode){

					case 2: return _ret.invalidParameter[player]();

					case 100: return _ret.notFound[player]();

					case 101:
					case 150: return _ret.notEmbeddable[player]();

					default: return null;

				}

			}

		},

		qualityChange: function(player){

			var _this = this;

			return function(suggested){

				if(typeof(suggested)=="object")
					suggested = suggested.data;

				return _this.onQualityChange[player](suggested);

			}

		},

		onQualityChange:{},

		onPlayer:{unstarted:{},ended:{},playing:{},paused:{},buffering:{},cued:{}},

		onErr:{notFound:{},notEmbeddable:{},invalidParameter:{}}

	};

	/**
	 * These are the internal defaults for the TubePlayer
	 * plugin to work without providing any parameters. They
	 * are merged with the users options.
	 */
	var defaults = {

		// public facing
		width: 425,
		height: 355,
		allowFullScreen: "true",
		initialVideo: "DkoeNLuMbcI",
		start: 0,
		preferredQuality: "default",
		showControls: true,
		showRelated: false,
		autoPlay: false,
		autoHide: true,
		theme: 'dark', // 'dark' or 'light'
		color: 'red', // 'red' or 'white'
		showinfo: false,
		modestbranding: true,

		// with respect to [wmode] - 'transparent' maintains z-index, but disables GPU acceleration
		wmode: 'transparent', // you probably want to use 'window' when optimizing for mobile devices
		swfobjectURL: "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js",
		loadSWFObject: true, // by default, we will attempt to load the swfobject script, if utilizing the flash player

		// privately used
		allowScriptAccess: "always",
		playerID: "tubeplayer-player-container",

		// html5 specific attrs
		iframed: true,

		// trigger fn's
		onPlay: function(id){},
		onPause: function(){},
		onStop: function(){},
		onSeek: function(time){},
		onMute: function(){},
		onUnMute: function(){},

		// player fn's
		onPlayerUnstarted: function(){},
		onPlayerEnded: function(){},
		onPlayerPlaying: function(){},
		onPlayerPaused: function(){},
		onPlayerBuffering: function(){},
		onPlayerCued: function(){},
		onQualityChange: function(){},

		// error fn's
		onErrorNotFound: function(){},
		onErrorNotEmbeddable: function(){},
		onErrorInvalidParameter: function(){}

	};

	/**
	 * The TubePlayer plugin bound to the jQuery object's prototype. 
	 * This method acts as an interface to instantiate a TubePlayer, 
	 * as well as invoke events that are attached - typically getters/setters
	 */
	$.fn.tubeplayer = function(input, xtra){

		var $this = $(this);

		var type = typeof input;

		if( arguments.length == 0 || type == "object" )

			return $this.each(function(){

				TubePlayer.init( $(this), input );

			});

		else if( type == "string" )

			return $this.triggerHandler( input+TUBEPLAYER, xtra || null );

	};


	/**
	 * This method is the base method for all the events
	 * that are bound to the TubePlayer. 
	 */
	var buildFN = function(fn, after){

		return function(evt,param){

			var p = TubePlayer.getPkg(evt);

			if(p.ytplayer) { 

				var ret = fn(evt, param, p);

				if(typeof(ret)=="undefined") 
					ret = p.$player;

				return ret;

			}

			return p.$player;

		};

	};

	/**
	 * Public method to get all the player instances
	 */
	$.tubeplayer.getPlayers = function(){

		return TubePlayer.ytplayers;

	};


	/**
	 * Initialize a YouTube player; 
	 *
	 *	First check to see if TubePlayer has been init'd
	 *	if it has then return, otherwise:
	 *		> add the tubeplayer class (used to denote a player)
	 *		> provide local data access to the options and store it
	 *		> initialize the default events on the jQuery instance
	 *		> create the container for the player
	 *		> initialize the player (iframe/HTML5 vs flash based)
	 * 
	 *	@param $player - the instance being created on
	 *	@param opts - the user's options
	 */
	TubePlayer.init = function($player, opts){

		if( $player.hasClass(TUBE_PLAYER_CLASS) )
			return $player;

		var o = $.extend({}, defaults, opts);

		o.playerID = o.playerID + ( new Date().valueOf() ) + "_" + Math.random();

		$player.addClass(TUBE_PLAYER_CLASS).data(OPTS, o);

		for(e in PLAYER) 
			$player.bind(e+TUBEPLAYER, $player, PLAYER[e]);

		// initialize the default event methods
		TubePlayer.initDefaults($.tubeplayer.defaults, o);

		// insert the player container
		jQuery("<div></div>").attr("id", o.playerID).appendTo($player);

		// append the player into the container
		TubePlayer.initPlayer($player, o);

		return $player; 

	};

	/**
	 * Every method needs these items
	 */
	TubePlayer.getPkg = function(evt){

		var $player = evt.data;

		var opts = $player.data(OPTS);

		var ytplayer = TubePlayer.ytplayers[opts.playerID];

		return {

			$player: $player,

			opts: opts,

			ytplayer: ytplayer

		}

	};

	/**
	 * This method handles the player init. Since 
	 * onYouTubePlayerReady is called when the script
	 * has been evaluated, we want all the instances
	 * to get init'd. For this we have a init queue.
	 * If the script has been init'd, we automatically
	 * pop the method off the queue and init the player.
	 */
	TubePlayer.iframeReady = function(o){

		TubePlayer.inits.push(function(){

			new YT.Player(o.playerID, {

				videoId: o.initialVideo,

				width: o.width,

				height: o.height,

				playerVars: { 

					'autoplay': (o.autoPlay?1:0), 

					'autohide': (o.autoHide?1:0),

					'controls': (o.showControls?1:0),

					'rel': (o.showRelated?1:0),

					'fs': (o.allowFullScreen?1:0),

					'wmode': o.wmode,

					'showinfo': (o.showinfo?1:0),

					'modestbranding': (o.modestbranding?1:0),

					'start': o.start,

					'theme': o.theme,

					'color': o.color

				},

				events: {

					'onReady': function(evt){

						TubePlayer.ytplayers[o.playerID] = evt.target;

						var $player = $(evt.target).parents("."+TUBE_PLAYER_CLASS);

						$.tubeplayer.defaults.afterReady($player);

					},

					'onPlaybackQualityChange': $.tubeplayer.defaults.qualityChange(o.playerID),

					'onStateChange': $.tubeplayer.defaults.stateChange(o.playerID),

					'onError': $.tubeplayer.defaults.onError(o.playerID)

				}

			});

		});

		// stacked init method
		if(TubePlayer.inits.length>=1 && !TubePlayer.inited){

			return function(){

				for(var i=0; i<TubePlayer.inits.length; i++){

					TubePlayer.inits[i]();

				}

				TubePlayer.inited = true;

			};

		}

		// if we've inited already, just call the init fn
		if(TubePlayer.inited){

			( TubePlayer.inits.pop() )();

		}

		return onYouTubePlayerAPIReady;

	};

	/**
	 * check to see if iframe option is plausible
	 */
	TubePlayer.supportsHTML5 = function(){

		return !!document.createElement('video').canPlayType;

	};

	/**
	 * @param d - the defaults
	 * @param o - the options w/ methods to attach
	 */
	TubePlayer.initDefaults = function(d, o){

		var ID = o.playerID; 

		// default onPlayer events
		var dp = d.onPlayer;
		dp.unstarted[ID] = o.onPlayerUnstarted;
		dp.ended[ID] = o.onPlayerEnded;
		dp.playing[ID] = o.onPlayerPlaying;
		dp.paused[ID] = o.onPlayerPaused;
		dp.buffering[ID] = o.onPlayerBuffering;
		dp.cued[ID] = o.onPlayerCued;

		// default onQualityChange
		d.onQualityChange[ID] = o.onQualityChange;

		// default onError events
		var de = d.onErr;
		de.notFound[ID] = o.onErrorNotFound;
		de.notEmbeddable[ID] = o.onErrorNotEmbeddable;
		de.invalidParameter[ID] = o.onErrorInvalidParameter;

	};

	/**
	 * init the iframed option if its requested and supported
	 * otherwise initialize the flash based player
	 * @param $player - the player that the tubeplayer binds to
	 * @param o - the init options
	 */
	TubePlayer.initPlayer = function($player, o){

		if(o.iframed && TubePlayer.supportsHTML5())

			TubePlayer.initIframePlayer($player, o);

		else

			TubePlayer.initFlashPlayer($player, o);

	};

	/**
	 * Initialize an iframe player
	 */
	TubePlayer.initIframePlayer = function($player, o){

		if(!TubePlayer.iframeScriptInited){

			// write the api script tag
			var tag = document.createElement('script');

			tag.src = "http://www.youtube.com/player_api";

			var firstScriptTag = document.getElementsByTagName('script')[0];

			firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

			TubePlayer.iframeScriptInited = true;

		};

		// init the iframe player
		onYouTubePlayerAPIReady = TubePlayer.iframeReady(o);

	};

	/**
	 * Flash player initialization
	 *  -> if 'loadSWFObject' is set to true, player will only be init'd
	 *      when the swfobject script request has completed successfully
	 *  -> if 'loadSWFObject' is set to false, assumes that you have 
	 *      imported your own SWFObject, prior to TubePlayer's initialization
	 * @imports swfobject automatically
	 */
	TubePlayer.initFlashPlayer = function($player, o){

		if(o.loadSWFObject){

		    $.getScript(o.swfobjectURL, TubePlayer.initFlashPlayerFN(o));

		} else {

		    TubePlayer.initFlashPlayerFN(o)();

		}

	};

	TubePlayer.initFlashPlayerFN = function(o){

	    return function(){

			var url =  ["//www.youtube.com/v/"]
			url.push( o.initialVideo );
			url.push( "?fs=" + (o.allowFullScreen?1:0) );
			url.push( "&enablejsapi=1&version=3" );
			url.push( "&playerapiid=" + o.playerID );
			url.push( "&rel= " + (o.showRelated?1:0) );
			url.push( "&autoplay=" + (o.autoPlay?1:0) );
			url.push( "&autohide=" + (o.autoHide?1:0) );
			url.push( "&controls=" + (o.showControls?1:0) );
			url.push( "&showinfo=" + (o.showinfo?1:0) );
			url.push( "&modestbranding=" + (o.modestbranding?1:0) );
			url.push( "&start=" + o.start );
			url.push( "&theme=" + o.theme );
			url.push( "&color=" + o.color );

			swfobject.embedSWF(url.join(""), o.playerID, 
				o.width, 
				o.height, 
				"8", null, null, 
				{ 
					allowScriptAccess: o.allowScriptAccess, 
					wmode: o.wmode, 
					allowFullScreen: o.allowFullScreen 
				}, 
				{ id: o.playerID }
			);

			// init the player ready fn
			onYouTubePlayerReady = function(playerId) { 

				var player = document.getElementById(playerId);

				TubePlayer.ytplayers[playerId] = player;

				player.addEventListener("onStateChange", "$.tubeplayer.defaults.stateChange('"+playerId+"')");

				player.addEventListener("onError", "$.tubeplayer.defaults.onError('"+playerId+"')");

				player.addEventListener("onPlaybackQualityChange", "$.tubeplayer.defaults.qualityChange('"+playerId+"')");

				var $player = $(player).parents("."+TUBE_PLAYER_CLASS);

				$.tubeplayer.defaults.afterReady($player);

			};

		}

	};

	// fmt: youtube.com/watch?x=[anything]&v=[desired-token]&
	TubePlayer.getVideoIDFromURL = function(sURL){

		var qryParamsStart = sURL.indexOf("?");

		var qryParams = sURL.substring(qryParamsStart, sURL.length);

		var videoStart = qryParams.indexOf("v=");
		if( videoStart > -1 ) { 
		    var videoEnd = qryParams.indexOf("&", videoStart);
		    if( videoEnd == -1 ) { 
		        videoEnd = qryParams.length;
		    }
		    return videoParam = qryParams.substring(videoStart+"v=".length, videoEnd);
		}

		return "";

	};

	/**
	 * All the events that are bound to a TubePlayer instance
	 */
	var PLAYER = {

		cue: buildFN(function(evt,param,p){ 

			p.ytplayer.cueVideoById(param, p.opts.preferredQuality);

		}),

		play: buildFN(function(evt,param,p){

			if(typeof(param)=='object') 
				p.ytplayer.loadVideoById(param.id,param.time, p.opts.preferredQuality); 

			else if(param) 
				p.ytplayer.loadVideoById(param, 0, p.opts.preferredQuality); 

			else
				p.ytplayer.playVideo(); 

			p.opts.onPlay(param);

		}),

		pause: buildFN(function(evt,param,p){

			p.ytplayer.pauseVideo();

			p.opts.onPause();

		}),

		stop: buildFN(function(evt,param,p){

			p.ytplayer.stopVideo();

			p.opts.onStop();

		}),

		seek: buildFN(function(evt,param,p){

			p.ytplayer.seekTo(param, true);

			p.opts.onSeek(param);

		}),

		mute: buildFN(function(evt,param,p){

			p.$player.attr("data-prev-mute-volume", p.ytplayer.getVolume());

			p.ytplayer.mute(); 

			p.opts.onMute();

		}),

		unmute: buildFN(function(evt,param,p){

			p.ytplayer.unMute(); 

			p.ytplayer.setVolume( ( p.$player.attr("data-prev-mute-volume") || 50 ) );

			p.opts.onUnMute();

		}),

		isMuted: buildFN(function(evt,param,p){

			return p.ytplayer.isMuted();

		}),

		volume: buildFN(function(evt,param,p){

			if(param) {

				p.ytplayer.setVolume(param);

				p.$player.attr("data-prev-mute-volume", p.ytplayer.getVolume());

			}	else  {

				return p.ytplayer.getVolume() || 0; // 0 because iframe's currently in labs

			}

		}),

		quality: buildFN(function(evt,param,p){

			if(param) 
				p.ytplayer.setPlaybackQuality(param); 

			else 
				return p.ytplayer.getPlaybackQuality();

		}),

		data: buildFN(function(evt,param,p){

			var ret = {}; 

			var P = p.ytplayer;

			ret.bytesLoaded = P.getVideoBytesLoaded(); 

			ret.bytesTotal = P.getVideoBytesTotal();

			ret.startBytes= P.getVideoStartBytes();

			ret.state = P.getPlayerState();

			ret.currentTime = P.getCurrentTime();

			ret.availableQualityLevels = P.getAvailableQualityLevels();

			ret.duration = P.getDuration();

			ret.videoURL = P.getVideoUrl();

			ret.getVideoEmbedCode = P.getVideoEmbedCode();

			ret.videoID = TubePlayer.getVideoIDFromURL(ret.videoURL);

			return ret;

		}),

		videoId: buildFN(function(evt,param,p){

			return TubePlayer.getVideoIDFromURL( p.ytplayer.getVideoUrl() );

		}),

		size: buildFN(function(evt, param, p){

			if(param.width && param.height) { 

				p.ytplayer.setSize(param.width, param.height);

				$(p.ytplayer).css(param);

			}

		}),

		destroy: buildFN(function(evt, param, p){

			p.$player.removeClass(TUBE_PLAYER_CLASS)
				.data(OPTS, null)
				.unbind(TUBEPLAYER)
				.html("");

			delete TubePlayer.ytplayers[p.opts.playerID];

			$(p.ytplayer).remove();

			return null;		

		}),

		player: buildFN(function(evt, param, p){

			return p.ytplayer;

		})

	};

})(jQuery);
