﻿/* 
 * Video class.  Handles all video player and video modal related tasks.
 * XML drives all content.  See "/Documents/xml/tv_ad.xml" for schema.
 *
 * @class video
 * @static
 */
juvedermUltra.tools.video = {
	 
	 // configuration properties
	_autoPlay: true,
	_width: 630,
	_height: 353,

	_xmlPath: "/Style%20Library/xml",
	_xmlFileName: "tv_ad.xml",
	_xmlData: null, // populated with the response xml object of the ajax request
	_videoPath: "/Style%20Library/video/", // path to video assets
	_flashPath: "/Style%20Library/video/fla/",
	_flashVideoPath: "../video/", // location of video file relative to the video_player.swf location.  used in flashvars for flash player.
	_isFlash: false, // is set to true if flash player is used.
	_playerContent: {},

	// panel properties/placeholders
	_panel_id: "video_modal",
	_panelContent: {},
	_panel: null,
	
	// player properties
	_player_id: "video5player",
	_flash_player_id: "videoplayer_callout",
	_player: null,

	// key listener placeholders
	escKeyListener: null,
	
	// global properties
	currentTitle: "",
	currentVideo: "",
	
	// init method. set defaults, creates the panel, defines panel/key listeners, and renders panel (starts the event chain)
	init: function(){
		var o = juvedermUltra.tools.video;
		o._isFlash = !$('html').hasClass('mobile');
		if(!o.escKeyListener) o.escKeyListener = new YAHOO.util.KeyListener(document, { keys:27 }, o.onEscKeyPress, "keyup" );
		if(!o._panel){
    		o._panel = new YAHOO.widget.Panel(o._panel_id, {width:o._width, height:o._height, visible:true, draggable:false, close:true, modal:true, zIndex:20001, underlay:"none", fixedcenter:true });
			o._panel.showEvent.subscribe(o.onPanelShow);
			o._panel.hideEvent.subscribe(o.onPanelHide);
			o._panel.renderEvent.subscribe(o.onPanelRender);
			o._panel.setBody(o.playerContent());
    		o._panel.render(document.body);
		} else {
			o.openVideo();
		}
	},
	
	// shows the modal with the supplied content.
	showVideo: function(panelID, xmlFileName){
		var o = juvedermUltra.tools.video;
		o._xmlFileName = xmlFileName || o._xmlFileName;
		o.readXMLData();
	},

	// closes the modal window.  the panel hide event listener handles cleanup.
	closeVideo: function(){
		var o = juvedermUltra.tools.video;
		o._panel.hide();
	},
	
	playVideo: function(){
		var o = juvedermUltra.tools.video;
	},
	
	stopVideo: function(){
		var o = juvedermUltra.tools.video;
	},
	
	// opens a new video in an existing player/panel
	openVideo: function(){
		var o = juvedermUltra.tools.video;
		if(!o._isFlash){
			//o._player.openVideo(o._videoPath + o.metaData.video.id, null, o._autoPlay);
		} else {
			var swfDrop = '<div id="' + o._player_id + '"></div>';
			$('.videoPlayer').append(swfDrop);
		}
		o._panel.show();
	},
	
	//  returns supported video player and modal content html
	playerContent: function(){
		var o = juvedermUltra.tools.video;  //shortcut var to containing class
		var v = o._videoPath + o.metaData.video.id; // video path and id
		var html = "";
		
		if(!o._isFlash){
			html += '<div class="videoPlayer">';
			html += '<video id="' + o._player_id + '" controls="controls">';
			html += '<source src="' + v + '.mp4" type="video/mp4"/>';
			html += '</video></div>';
		} else {
			html += '<div class="videoPlayer"><div id="' + o._player_id + '"></div></div>';
		}
		
		/*if(Rosetta.HTML5.support.h264()){
			o._isFlash = false;
			html += '<div class="videoPlayer">';
			html += '<video id="' + o._player_id + '" controls="controls">';
			html += '<source src="' + v + '.mp4" type="video/mp4"/>';
			html += '</video></div>';
		} else if(Rosetta.HTML5.support.ogg()) {
			o._isFlash = false;
			html += '<div class="videoPlayer">';
			html += '<video id="' + o._player_id + '" controls="controls">';
			html += '<source src="' + v + '.ogg" type="video/ogg"/>';
			html += '</video></div>';
		} else {
			o._isFlash = true;
			html += '<div class="videoPlayer"><div id="' + o._player_id + '"></div></div>';
		}*/
		return html;
	},
	
	// builds the flash video player using swfobject 1.5
	buildFlash: function(){
		var o = juvedermUltra.tools.video;
		var so2 = new SWFObject("/Style%20Library/fla/videoplayer_callout.swf", o._flash_player_id, o._width, o._height, "9.0.0.0", "#ffffff");
    	so2.addParam("allowScriptAccess", "sameDomain");
    	so2.addParam("allowFullScreen", "false");
    	so2.addParam("flashVars", "xmlPathName=" + o._xmlPath + "&xmlFileName="+o._xmlFileName);    	
    	so2.addParam("wmode", "transparent");
    	so2.write(o._player_id);
	},
	
	// returns the native video player dom object html5 video element
	getPlayer: function(){
		var o = juvedermUltra.tools.video;
		return o._player.getPlayer();
	},

	// event handlers
	onPanelRender: function(e){
		var o = juvedermUltra.tools.video;
		o._panelContent = document.getElementById(o._panel_id);
		o._panelContent.className = ""; // removes default yui styles
		if(!o._isFlash && !o._player) {
			o._player = new Rosetta.HTML5.videoPlayer(".videoPlayer");
			o._panel.show();
		} else if (o._isFlash) {
			o._panel.cfg.setProperty("close",false);
		}
	},
	onPanelShow: function(e){
		var o = juvedermUltra.tools.video;
		$('.mask').bind('click',o.onEscKeyPress);
		o.escKeyListener.enable();
		if(o._isFlash){
			o.buildFlash();
		} else {
			o._player.openVideo(o._videoPath + o.metaData.video.id, null, o._autoPlay);
		}
	},
	onPanelHide: function(e){
		var o = juvedermUltra.tools.video;
		$('.mask').unbind('click',o.onEscKeyPress);
		o.escKeyListener.disable();
		if(document.getElementById("literegDialog")) document.getElementById("literegDialog").style.display = "block";
		
		if(!o._isFlash){
			if(o._player != null) {
				o._player.getPlayer()[0].pause();
				o._player.getPlayer().trigger('abort');
				o._player.getPlayer().empty();
				$('.videoOverlay').remove();
				$('.replayControls').remove();
				$('.videoPlaylist').remove();
			}
		} else {
			$('.videoPlayer').empty();
		}
	},
	onEscKeyPress: function(e){
		var o = juvedermUltra.tools.video;
		if(o._isFlash){
			// executes an external interface call (doVideoClose) in the flash movie if it's the active player.
			var flashMovie = document.getElementById(o._flash_player_id);
			flashMovie.doVideoClose();
		} else {
			o.closeVideo();
		}
	},
	// ajax reads the xml meta data into a usuable object
	readXMLData: function(){
		var o = juvedermUltra.tools.video;
		var sUrl = o._xmlPath + "/" + o._xmlFileName;
		var callback = {
			success: function(oResponse){
				o._xmlData = oResponse.responseXML;
				o.metaData.video = new o.metaData.videoData(o._xmlData.getElementsByTagName('video')[0].getElementsByTagName('item')[0]);
				/*if(!o._isFlash){
					o._width = parseInt(o.metaData.video.width, 10) + 9;
					o._height = parseInt(o.metaData.video.height, 10) + 11;
				}*/
				o.init();
			},
			failure: function(oResponse){
				alert("AJAX ERROR LOADING VIDEO DATA: \n" + oResponse.statusText);
			}
		};
		var request = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback); 
	},

	// video meta data object
	metaData: {
		video: {}, // static value of instanced class below, set in ajax success handler.
		videoData: function(xml){
			var _xml = xml;
			this.id = xml.getAttribute('id');
			this.name = _xml.getAttribute('name');
			this.src = _xml.getAttribute('src');
			this.width = _xml.getAttribute('width');
			this.height = _xml.getAttribute('height');
		}
	}
};

// HTML5 video player class
var Rosetta = Rosetta || {};
Rosetta.HTML5 = Rosetta.HTML5 || {};
Rosetta.HTML5.videoPlayer = function(selector, options) {
	
	//if no selector found on page, exit
	if(!$(selector).length) {
		return false;
	}
	
	var defaults = {
		autoplay:false	
	};
	var settings = $.extend({}, defaults, options);

	var $playerWrapper = $(selector);
	var $player = $playerWrapper.find('video');
	var player = $player[0]; //get the native video dom object.. not jquery'ed one
	
	var $poster = $('<div class="videoPoster"></div>').prependTo($playerWrapper); //create custom poster div. Dont define "poster" html attribute to video tag. Breaks iOS3
	var $playButton = $('<div class="playButton"></div>').appendTo($playerWrapper);
	//var $loading = $('<div class="videoLoading"></div>').appendTo($playerWrapper);
	
	createControls();
	$playButton.trigger("mouseover"); //prevent having to tap twice on iOS
	
	//attach events to the video element
	$player.bind({
		loadstart: function(e){
			//the user agent begins looking for media data, as part of the resource selection algorithm.	networkState equals NETWORK_LOADING
			logEvent(e);
			
		},
		progress: function(e){
			//The user agent is fetching media data.	networkState equals NETWORK_LOADING
			//log("progress: ", e, vPlayer.buffered.length, vPlayer.buffered.end(0)/vPlayer.duration);			
		},
		suspend: function(e){
			//The user agent is intentionally not currently fetching media data, but does not have the entire media resource downloaded.	networkState equals NETWORK_IDLE
			//player.play();
			logEvent(e);
		},
		abort: function(e){
			//The user agent stops fetching the media data before it is completely downloaded, but not due to an error.	error is an object with the code MEDIA_ERR_ABORTED. networkState equals either NETWORK_EMPTY or NETWORK_IDLE, depending on when the download was aborted.
			logEvent(e);
		},
		error: function(e){
			//An error occurs while fetching the media data.	error is an object with the code MEDIA_ERR_NETWORK or higher. networkState equals either NETWORK_EMPTY or NETWORK_IDLE, depending on when the download was aborted.
			logEvent(e);
			log(e);
		},
		emptied: function(e){
			//A media element whose networkState was previously not in the NETWORK_EMPTY state has just switched to that state (either because of a fatal error during load that's about to be reported, or because the load() method was invoked while the resource selection algorithm was already running, in which case it is fired synchronously during the load() method call).	networkState is NETWORK_EMPTY; all the IDL attributes are in their initial states.
			logEvent(e);
		},
		stalled: function(e){
			//The user agent is trying to fetch media data, but data is unexpectedly not forthcoming.	networkState is NETWORK_LOADING.
			//player.play();
			logEvent(e);
		},
		play: function(e){
			//Playback has begun. Fired after the play() method has returned.	paused is newly false.
			logEvent(e);
			$poster.hide();
			$playButton.hide();
		},
		pause: function(e){
			//Playback has been paused. Fired after the pause method has returned.	paused is newly true.
			logEvent(e);
			
		},
		loadedmetadata: function(e){
			//The user agent has just determined the duration and dimensions of the media resource.	readyState is newly equal to HAVE_METADATA or greater for the first time.
			logEvent(e);
		},
		loadeddata: function(e){
			//The user agent can render the media data at the current playback position for the first time.	readyState newly increased to HAVE_CURRENT_DATA or greater for the first time.
			logEvent(e);
		},
		waiting: function(e){
			//Playback has stopped because the next frame is not available, but the user agent expects that frame to become available in due course.	readyState is newly equal to or less than HAVE_CURRENT_DATA, and paused is false. Either seeking is true, or the current playback position is not contained in any of the ranges in buffered. It is possible for playback to stop for two other reasons without paused being false, but those two reasons do not fire this event: maybe playback ended, or playback stopped due to errors.
			//$loading.show();
			logEvent(e);
		},
		playing: function(e){
			//Playback has started.	readyState is newly equal to or greater than HAVE_FUTURE_DATA, paused is false, seeking is false, or the current playback position is contained in one of the ranges in buffered.
			//$loading.hide();
			logEvent(e);

		},
		canplay: function(e){
			//The user agent can resume playback of the media data, but estimates that if playback were to be started now, the media resource could not be rendered at the current playback rate up to its end without having to stop for further buffering of content.	readyState newly increased to HAVE_FUTURE_DATA or greater.
			logEvent(e);
			//$loading.hide();
		},
		canplaythrough: function(e){
			//The user agent estimates that if playback were to be started now, the media resource could be rendered at the current playback rate all the way to its end without having to stop for further buffering.	readyState is newly equal to HAVE_ENOUGH_DATA.
			logEvent(e);
		},
		seeking: function(e){
			//The seeking IDL attribute changed to true and the seek operation is taking long enough that the user agent has time to fire the event.	
			logEvent(e);
		},
		seeked: function(e){
			//The seeking IDL attribute changed to false.	
			logEvent(e);
		},
		timeupdate: function(e){
			//The current playback position changed as part of normal playback or in an especially interesting way, for example discontinuously.	
			//log("timeupdate: ");
			//log(e);
			//log('');
			
		},
		ended: function(e){
			//Playback has stopped because the end of the media resource was reached.	currentTime equals the end of the media resource; ended is true.
			logEvent(e);
		},
		ratechange: function(e){
			//Either the defaultPlaybackRate or the playbackRate attribute has just been updated.	
			logEvent(e);
		},
		durationchange: function(e){
			//The duration attribute has just been updated.	
			logEvent(e);
		},
		volumechange: function(e){
			//Either the volume attribute or the muted attribute has changed. Fired after the relevant attribute's setter has returned.
			logEvent(e);
		}
	});

	function logEvent(e) {
		log(e.type.toUpperCase() + " @ " + new Date(e.timeStamp));
		/*switch (e.target.networkState) {
			case 0:
				log('networkState: NETWORK_EMPTY');
				break;
			case 1:
				log('networkState: NETWORK_IDLE');
				break;
			case 2:
				log('networkState: NETWORK_LOADED');
				break;
			case 3:
				log('networkState: NETWORK_LOADING');
				break;
			case 4:
				log('networkState: NETWORK_NO_SOURCE');
				break;
		}
		switch (e.target.readyState) {
			case 0:
				log('readyState: HAVE_NOTHING');
				break;
			case 1:
				log('readyState: HAVE_METADATA');
				break;
			case 2:
				log('readyState: HAVE_CURRENT_DATA');
				break;
			case 3:
				log('readyState: HAVE_FUTURE_DATA');
				break;
			case 4:
				log('readyState: HAVE_ENOUGH_DATA');
				break;
		}
		log('playhead at: ' + e.target.currentTime + ' of ' + e.target.duration);
		log('currentSrc: ' + e.target.currentSrc);
		log(e);
		log(' ');*/
	};
	
	function createControls() {
		//the big overlayed play button
		$playButton.click(function(){
			$(this).fadeOut();
			player.play();
		});
	}

	function secToTime(totalSec) {
		hours = parseInt( totalSec / 3600 ) % 24;
		minutes = parseInt( totalSec / 60 ) % 60;
		seconds = totalSec % 60;
	
		return (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes) + ":" + (seconds  < 10 ? "0" + seconds : seconds);
	}

	
	//return publically exposed methods
	return {
		
		getPlayer: function() {
			return $player;	
		},
		
		playVid: function() {
			$poster.hide();
			$playButton.hide();
			player.play();	
		},
		
		pauseVid: function() {
			player.pause();	
		},
		
		loadVideo: function(){
			player.load();
		},
		
		resize:function(width, height) {
			$playerWrapper.width(width);
			$playerWrapper.height(height);
			//dynamic progress bar width... somewhat depends on the styling still
			$progressBar.width($playerWrapper.width() - 186);	
		},
		
		openVideo: function(videoPath,posterPath,playNOW) {
			if(Rosetta.HTML5.support.video()){
				//load poster image if defined
				$poster.empty();
				if(posterPath) $('<img src="' + posterPath + '"/>').appendTo($poster);
				$player.trigger('abort');
				//update the video tags source, reload and play
				$player.empty();
				if(Rosetta.HTML5.support.h264()){
					$player.append('<source src="' + videoPath + '.mp4" type="video/mp4"/>');
				} else if (Rosetta.HTML5.support.ogg()) {
					$player.append('<source src="' + videoPath + '.ogg" type="video/ogg"/>');
				}
				log('::: SOURCE CHANGED TO: ' + videoPath + ' :::');
				//begin playing if specified
				if(playNOW){
					$poster.hide();
					$playButton.hide();
					player.autobuffer = true;
					player.autoplay = true;
					log('::: ATTEMPTING TO PLAY NEW SOURCE: ' + videoPath + ' :::');
				}
				player.load();
				
			} 
		}
			
	}
	
};


//HTML5 support dectection
Rosetta.HTML5.support = {
	// returns true if browser has HTML5 support
	video: function(){
		return !!document.createElement('video').canPlayType;
	},
	// h264: mp4 / webkit support (safari, iOS, etc)
	h264: function(){
		if (!Rosetta.HTML5.support.video()){ return false; }
		var v = document.createElement("video"); 
		return v.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"') != "" ? true : false;
	},
	// ogg: firefox/opera support
	ogg: function(){
		if (!Rosetta.HTML5.support.video()){ return false; }
		var v = document.createElement("video"); 
		return v.canPlayType('video/ogg; codecs="theora, vorbis"') != "" ? true : false;
	}
};

//for console logging
window.log = function(arg){
  if(this.console){console.log(arg);}
};
