/*!
* jQuery TubePlayer Plugin
*
* version: 1.0.3 (14-Aug-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: "hd720",
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: false,

// 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() );

$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,

'fmt': 22,

'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( "?ap=%2526fmt%3D22");
//url.push( "&fmt=22");
url.push( "&usehd=1&fshd=1");
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?v=[desired-token]&
TubePlayer.getVideoIDFromURL = function(sURL){

var pattern = 'youtube.com/watch?v=';

var start = sURL.indexOf(pattern) + pattern.length;

var end = sURL.indexOf('&', start) || sURL.length;

if( start > end )
return "";

return sURL.substring(start, end);

};

/**
* 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);
