/**
 * bam.zPlayer : A lightbox-style, draggable Video Player
 *
 * To use: call bam.zPlayer.play(), and pass one of:
 *  - String representing a content_id
 *  - Number representing a content_id
 *  - Object containing all required information for clip playback (headline, url, etc.)
 *  - Second parameter is optional - topic_id (For loading related content from topic's index)
 * 
 * Start of playback flow:
 * play() > {init()} > createFlvPlayer() > {onPlayerLoaded()} > playClip() > loadClipData() > startPlayback()
 *
 * To re-configure for other properties run something like:
 * 	bam.zPlayer.reconfigure({
 * 		psKeys: ["SNY_FLASH_1000K_PROGDNLD"],
 *		showRelatedContentOverride: true,
 *		relatedContentUrlOverride: "/gen/media_showcase.jsp",
 *		relatedContentBgOverride: "/images/multimedia/zPlayer_bgRelatedContent.gif"
 *	});
 */

bam.zPlayer = (function(){

	$.easing.def = "easeInOutExpo";
												
	var _initialized   = false,
			_isPlaying     = false,
			_playInitiated = false, // used to prevent double clicks on play button			
			_isOpen        = false,
			
			_previousOverlayCSS, // for restoring the prior state (if exists)
			_playerContext = "Z Player",

			// variables used for the related content leave-behind
			_playedVideos          = [], // local cache - loadClipData saves it here
			_relatedCssLoaded      = false,
			_relatedIsDisplayed    = false,
			_replayHandlerAssigned = false,
			_topicId               = null,
			
			/*
			* jQuery object for custom events.  Supported events are:
			* beforePlay, afterInit, startPlayback, afterPlay, playlistComplete, 
			* showingRelatedContent, relatedContentLoaded, collapsePlayer, 
			* beforeClose, afterClose
			*/
			_$event = $({}),
			
			// player configuration. can be re-configured using bam.zPlayer.reconfigure()
			_config = {
				bgImageUrl    : "/shared/images/bam/zPlayer/bg.png", // for pre-loading
				cssUrl        : "/shared/css/bam/bam.zPlayer.css",
				cssRelatedUrl : "/shared/css/bam/bam.zPlayer.related.css", // can be used to override default css
				
				showRelatedContentOverride : false, // used to force display of related content leave-behind
				relatedContentUrlOverride  : "", // custom source of related content leave-behind data
				relatedContentBgOverride   : "", // custom background image
								
				// swf params
				blankImgUrl    : "/images/000000.gif",
				replayImgUrl   : "/shared/images/bam/zPlayer/buttonReplay.png",
				skinUrl        : "/flash/video/y2009/skins/mlb_media_landing_full.swf",
				playerObjName  : "bam.zPlayer.flashPlayer", // passed to swf for communication
				defaultVolume  : 60,
				flvElemId      : "flashPlayer", // id assigned to swf DOM element
				flvContainerId : "zPlayer_flvContainer", // id of swf container element
				pageComponent  : "z_player", // used when assembling siteSection value (used in bam.FlvPlayer) for preroll
				
				psKeys        : ["MLB_FLASH_800K_STREAM_VPP", "MLB_FLASH_800K_STREAM", "MLB_FLASH_1000K_PROGDNLD", "MLB_FLASH_800K_PROGDNLD"], // ordered by priority
				playerDims    : {w:640, h:400},
				zoomStartDims : {w:"33px",  h:"22px"},
				zoomEndDims   : {w:"660px", h:"450px"},
				
				MESSAGES: {
					errorLoadingRelatedContent: "Related content is temporarily unavailable."	
				}
			};
	
	/**
	* Private method for detecting if browser is Firefox 2 on MAC.
	* (This browser doesn't like the opacity/flash/z-index combo, so we redirect to VPP)
	* @return boolean
	*/
	function _isMacXFF2(){
		var userAgent = navigator.userAgent.toLowerCase();
		if (/firefox[\/\s](\d+\.\d+)/.test(userAgent)) {
			var ffversion = new Number(RegExp.$1);
			if (ffversion < 3 && userAgent.indexOf("mac") != -1){ return true; }
		}
		return false;
	}
	
	
	var _self = {
		
		debugMode   : false, // enables console logging
		curClipData : null,
		flashPlayer : null,
		content_id  : null,
		
		// flag used to force incompatible browsers (currently only MacXFF2) to the VPP for playback
		redirectBadBrowsers : true, 
		

		/**
		* Logs messages to console when debugMode is set to true
		*/
		log: function(msg){
			if (typeof console!="undefined" && _self.debugMode){ console.log("bam.zPlayer: " + msg); }
		},
		
		
		/**
		* Public function for custom event binding.
		*/
		bind: function(eventName, dataOrFn, fnOrUndefined){
			_$event.bind(eventName, dataOrFn, fnOrUndefined);
		},
		
		
		/**
		* Public function for custom event un-binding.
		*/
		unbind: function(eventName, fn){
			_$event.unbind(eventName, fn);
		},
				

		/**
		* Public function for binding and unbinding custom event after it triggered once.
		*/
		one: function(eventName, dataOrFn, fnOrUndefined){
			_$event.one(eventName, dataOrFn, fnOrUndefined);
		},


		/**
		* Public method for initiating playback.
		* @param clipData Object, String (content_id) OR Number (content_id)
		* @param topicId String (Optional)
		*/
		play: function(clipData /*, topicId */){
			if (_playInitiated) return; // prevents weird behavior when user clicks play more than once
			_playInitiated = true;
			switch (typeof clipData){
				case "string" : _self.content_id = clipData; break;
				case "number" : _self.content_id = clipData+""; break; // convert to string
				case "object" : _self.content_id = clipData.content_id; _self.curClipData = clipData; break;
				default : throw new Error("Invalid data passed to bam.zPlayer.play()"); return false;
			}
			_topicId = arguments[1]; // passed value or undefined
			if (_isMacXFF2() && _self.redirectBadBrowsers){
				// redirect Mac users with Firefox 2.x browsers to VPP due to flash/overlay/flash issue
				location.href = _self.getVppUrl({content_id:_self.content_id, topic_id:_topicId});
				return;
			}
			_$event.trigger("beforePlay");
			if (!_initialized){
				_self.init();
			}
			/**
			 * Renders (zooms in) player and calls createFlvplayer which triggers start of playback.
			 */
			function showPlayer(){
				_self.log("starting playback");
				var fullWidth = parseInt($("body").innerWidth(),10),
						elemWidth = parseInt($("#zPlayerOuter").css("width"),10),
						posLeft   = ((fullWidth/2) - (elemWidth/2)) - 65, // offset so that its directly over media wall
						posTop    = $(document).scrollTop() + 95;
				$("#zPlayerOuter")
					.css({top:posTop+"px",left:posLeft+"px"})
					.show()
					.draggable({containment:"document", cancel:"#zPlayer_flvContainer, #zPlayer_leaveBehind"});
				$("#zPlayerInner")
					.animate({width:_config.zoomEndDims.w, height:_config.zoomEndDims.h, opacity:1}, "normal", function(){
						$("#zPlayerOuter").css({"background-image":"url("+_config.bgImageUrl+")"});
						setTimeout(function(){ $("#zPlayerInner").css({"background":"none"}) }, 100);
					});
				_isOpen = true;
				_self.killFlvPlayer(); // will check if exists first
				_self.createFlvPlayer();
				_$event.trigger("afterPlay");				
			}
			if (!_isMacXFF2()){
				if ($("#overlay").length){
					_previousOverlayCSS = {"background-color":$("#overlay").css("background-color"), "opacity":$("#overlay").css("opacity")};
					$("#overlay").click(function(){ _self.close({compActivity:"Z Player Outside Close Click"}); });
				}
				bam.overlay.setCSS({"background-color":"#ffffff"});			
				bam.overlay.show({fadeSpeed:"fast", opacity:"0.3", callback:showPlayer});
			}
			else{
				$("embed,iframe").css("visibility","hidden");
				showPlayer();
			}
		},
				
		
		/**
		* initializes zPlayer by loading required JavaScript files (if not already loaded)
		* and appending required elements to the DOM.
		* @TODO Make use of sequential ajax for JS imports
		*/
		init: function(){
			_self.log("initializing...");
			if ((/MSIE (6)/.test(navigator.userAgent) && navigator.platform == "Win32")){ 
				_config.bgImageUrl = "/shared/images/bam/zPlayer/bg.gif"; // don't use png for IE6 (alpha+drag+flash+link problems)
			}
			var bgImage = new Image();
			bgImage.src = _config.bgImageUrl; // preload background image
			/**
			* Private utility function for synchronously loading JavaScript includes 
			* without updating global ajax props via ajaxSetup
			* @param url String
			*/
			function getScriptSync(url){ 
				$.ajax({
					type:"GET",
					async:false,
					cache:true,
					url:url,
					dataType:"script"
				}); 
			}
			if (typeof bam.FlvPlayer === "undefined") getScriptSync("/shared/scripts/bam.FlvPlayer.js");
			if (typeof bam.media     === "undefined") getScriptSync("/shared/scripts/bam.media.js");
			getScriptSync("/shared/scripts/external/jquery.easing.js");
			getScriptSync("/shared/scripts/external/jquery-ui-draggable-1.7.1.min.js");
			bam.loadSync("/shared/scripts/bam/bam.overlay.js");
			bam.loadCSS(_config.cssUrl);
			var zPlayerHtml = '' +
				'<div id="zPlayerOuter">' +
					'<div id="zPlayerInner">' +
						'<div id="zPlayerClipInfo"><div id="zPlayerHeadline"></div><a id="zPlayerCloseBtn">Close</a></div>' +
						'<div id="zPlayer_flvContainer"></div>' +
						'<div id="zPlayer_leaveBehind"></div>' +
					'</div>' +
				'</div>';
			$(zPlayerHtml).appendTo("body");
			$("#zPlayerInner").css({opacity:0, width:_config.zoomStartDims.w, height:_config.zoomStartDims.h});
			$("#zPlayerClipInfo").hover(function(){ $(this).css({cursor:"move"}); }, function(){ $(this).css({cursor:"default"}); })
			$("#zPlayerCloseBtn").click(_self.close);
			bam.overlay.init();
			_initialized = true;
			_$event.trigger("afterInit");			
		},		

		
		/**
		* Creates instance of FLV Player as a public property (bam.zPlayer.flashPlayer)
		*/
		createFlvPlayer: function(){
			_self.log("createFlvPlayer()");
			$("#zPlayer_flvContainer").show(); // related content hides this div which prevents future loading of flvplayer. this ensures proper loading
			_self.flashPlayer = new bam.FlvPlayer({
				hideControls       : false,
				skin               : _config.skinUrl,
				self               : _config.playerObjName,
				endPosterPath      : _self.showRelatedContentLeaveBehind() ? _config.blankImgUrl : _config.replayImgUrl,
				width              : _config.playerDims.w,
				height             : _config.playerDims.h,
				elemId             : _config.flvElemId,
				containerId        : _config.flvContainerId,
				wmode              : $.browser.msie ? "window" : "transparent",
				defaultVolume      : _config.defaultVolume,
				debugMode          : _self.debugMode,
				onPlayerLoaded     : _self.onFlvPlayerLoaded,
				onPlaylistComplete : _self.onPlaylistComplete,
				onCollapse         : _self.onCollapse,
				pageComponent      : _config.pageComponent
			});
		},


		/**
		* Handler triggered by flv player (SWF) after it has loaded
		*/
		onFlvPlayerLoaded: function(){
			_self.log("flvPlayer.onFlvPlayerLoaded()");
			_self.playClip(_self.content_id);		
		},


		/**
		* Handler triggered by flv player (SWF) after it has completed playback of the playlist
		*/
		onPlaylistComplete: function(){
			_$event.trigger("playlistComplete");
			_self.flashPlayer.execute("exitFullScreen");
			_isPlaying = false;
			if (_self.showRelatedContentLeaveBehind()){
				_self.showRelatedContent();
			}
			_self.log("done with onPlaylistComplete");			
		},
		
		
		/**
		* Checks if related content leave-behind should be displayed.
		* @return boolean	
		*/
		showRelatedContentLeaveBehind: function(){
			return (
				(_config.showRelatedContentOverride) ||
				(typeof(club)!=="undefined" && typeof(section)!=="undefined" && typeof(page_id)!=="undefined") &&
				(section==="homepage" || (section==="world_series" && page_id==="ps_2009_ws_landing"))
			);
		},
		
		
		/**
		* Reconfigures private _config object with new properties. Use to reconfigure zPlayer
		* @param cfg Object
		*/
		reconfigure: function(cfg){
			_self.log("reconfiguring zPlayer");
			$.extend(_config, cfg);
		},		
			
		
		/**
		* Returns path to XML containing related content, based on page (club, section)
		* and value of _topicId.
		* @TODO Add support for SOE properties, OR just override locally
		*/
		getRelatedContentXmlPath: function(){
			var xmlPath = (_topicId) ? "/gen/multimedia/topic/"+_topicId+".xml" : "/gen/"+club+"/components/multimedia/topvideos.xml";  // correct these values
			if (typeof section !== "undefined" && section==="world_series"){ 
				xmlPath = "/gen/ps/2009/ws/series/ws/media_full.xml"; // override data url for worldseries.com
			}
			return xmlPath;
		},
		
		
		/**
		* Displays leave-behind screen with replay option and related videos which
		* link to the Video Playback Page.
		*/
		showRelatedContent: function(){
			_self.log("showRelatedContent");
			_$event.trigger("showingRelatedContent");
			var bgImg = new Image();
			bgImg.src = _config.relatedContentBgOverride; // start loading the image
			_self.killFlvPlayer(function(){ 
				$("#zPlayer_flvContainer").hide();
				$("#zPlayerHeadline").text("");
				var html = '\
					<div id="zPlayer_replayInfo">\
						<a id="zPlayer_replayButton" href="#">Click to Replay</a>\
						<div id="zPlayer_clipThumb"><img src="'+_self.curClipData.thumb+'"></div>\
						<div id="zPlayer_clipTitle">'+_self.curClipData.headline+'</div>\
						<div id="zPlayer_clipBlurb">'+_self.curClipData.blurb+'</div>\
					</div>\
					<div id="zPlayer_relatedVideos"></div>';
				if (_config.relatedContentBgOverride){
					$("#zPlayer_leaveBehind").css({"background-image":"url("+_config.relatedContentBgOverride+")"});
				}
				$("#zPlayer_leaveBehind").append(html).show();
				$("#zPlayer_replayButton").one("click", function(evt){
					evt.preventDefault();
					_self.log("replay button clicked");
					$("#zPlayer_leaveBehind").empty().hide();
					$("#zPlayer_flvContainer").show();
					_relatedIsDisplayed = false;
					_self.createFlvPlayer();
				});
				_self.loadRelatedContent();
			});													
		},
		
		
		/**
		* Loads related content from an XML file via ajax then calls renderRelatedContent.
		* @TODO This may need to query the search service if it doesn't find enough content
		* but in any case, it should normalize the data for display
		* @TODO Consider better way to call renderRelatedContent
		*/
		loadRelatedContent: function(){
			_self.log("loadRelatedContent");
			var xmlFilePath = _config.relatedContentUrlOverride || _self.getRelatedContentXmlPath();
			$.ajax({
				type     : "GET",
				async    : false,
				url      : xmlFilePath,
				dataType : "xml",
				error    : function(){ _self.log("loadRelatedContent(): Error loading " + xmlFilePath); },
				success  : function(xmlData){
					if (_topicId){ // get index from topic config, then render that
						_self.log("Looking for topic index in the topic config....");
						$.ajax({
							type     : "GET",
							async    : false,
							cache    : true,
							url      : $("topic video_index", xmlData).attr("src"),
							dataType : "xml",
							error    : function(){ 
								_self.log("loadRelatedContent(): Error loading " + xmlFilePath);
								$("#zPlayer_relatedVideos").html("<div id='zPlayer_relatedVideosError'>"+_config.MESSAGES.errorLoadingRelatedContent+"</div>");
							},
							success  : function(xmlData){
								_$event.trigger("relatedContentLoaded");
								_self.renderRelatedContent(xmlData); // @TODO bind to event above
							}	
						});
					}
					else { // just render index data (no topic to worry about)
						_$event.trigger("relatedContentLoaded");
						_self.renderRelatedContent(xmlData); // @TODO bind to event above
					}
				}
			});
		},
		

		/**
		* Parses XML DOM object and renders related videos from it by injecting new
		* HTML into the leave-behind related videos container.
		* @param xml XML DOM object returned by $.ajax()
		* @TODO Make sure blank XML data is handled properly (get data from search?)
		*/
		renderRelatedContent: function(xml){
			_self.log("renderRelatedContent");
			var count = 0,
					max   = 8,
					html  = "<ul>",
					items = [],
					vppParams = {},
					$curItem;
			$("data item", xml).each(function(i, curItem){
				$curItem = $(curItem);
				items.push({
					content_id : $curItem.attr("content_id"),
					headline   : $curItem.find(">title").text() || $curItem.find(">headline").text(),  // either Newsroom or Homebase format, also below
					thumb      : $curItem.find(">pictures picture[type='small-text-graphic'] url").text() || $curItem.find(">images image[type='7']").text() || $curItem.find(">pictures picture[type='medium-thumbnail'] url").text()
				});
				if (items.length > 9){ return; }
			});
			if (items.length < max){
				max = items.length;
			}
			while ( (count < max) && ($.inArray(items[count].content_id, _playedVideos)===-1) ){
				vppParams = {content_id:items[count].content_id};
				if (typeof club!=="undefined" && club!=="mlb"){ vppParams.c_id = club; }
				if (_topicId){ vppParams.topic_id = _topicId; }
				html += "<li><a href='" + _self.getVppUrl(vppParams) + "'><img src='"+items[count].thumb+"'/><div>"+items[count].headline+"</div></a></li>";
				count++;
			}
			html += "</ul>";
			$("#zPlayer_relatedVideos").html(html);
			_relatedIsDisplayed = true;
			_self.log("rendered related content");
		},

		
		/**
		* Handles the onCollapse event from the FlvPlayer(swf). That event is generally 
		* triggered by end-of-stream marker in video content itself.
		*/
		onCollapse: function(){
			setTimeout( function(){
				_self.collapsePlayer();
				_$event.trigger("collapsePlayer");
			}, 500);
		},

		
		/**
		* If the instance of flvPlayer exists, it empties the container and deletes the object.
		*/
		killFlvPlayer: function(callback){
			if (_self.flashPlayer){
				_self.log("killFlvPlayer(): stopping, then killing flv player");
				_self.flashPlayer.execute("stopPlaylist");
				_self.flashPlayer.clearPlaylist();
				delete(_self.flashPlayer);
				setTimeout(function(){ 
					$("#zPlayer_flvContainer").empty();
					if (typeof callback == "function"){
						callback();
					}
				}, 10);
			}
			else {
				_self.log("killFlvPlayer(): player did not exist. doing nothing.");
			}
		},
		
		
		/** 
		* Returns URL for Video Playback Page. Override locally for different URL support
		* @param paramHash Object
		*/
		getVppUrl: function(paramHash){
			var url = "/media/video.jsp?";
			for (var paramName in paramHash){
				if (paramHash[paramName]){
					url += paramName + "=" + paramHash[paramName] + "&";
				}
			}
			return url;
		},		
		
		
		/**
		 * Returns URL for DoubleClick preroll.
		 */
		getPrerollUrl: function(){
			var adDomain  = (typeof c_domain  != "undefined") ? c_domain[club] : "mlb",
					adSection = (typeof section   != "undefined") ? section        : "",
					adKeyVal  = (typeof dc_keyVal != "undefined") ? dc_keyVal      : "",
					adUrl     = "http://ad.doubleclick.net/pfadx/"+adDomain+".mlb/"+adSection+";"+adKeyVal+";pos=1;sz=640x360;tile=1;ord=" + (new Date).getTime();
			return adUrl;
		},
		
		
		/**
		* Loads data for content_id (unless its already loaded), and plays clip
		* @param content_id String (integer doesn't work here)
		*/
		playClip: function(content_id){
			_isPlaying = true;
			_self.log("playClip("+content_id+")");
			if (_self.loadClipData(content_id)){
				$("#zPlayer_flvContainer").show();
				_self.log("startingPlaylist: " + _self.curClipData.curVideoFlashUrl);
				var adUrl    = _self.getPrerollUrl(),
						playlist = [];
				$("#zPlayerHeadline").text(_self.curClipData.headline);
				if (bam.FlvPlayer.prerollPlatform==="fw"){ // FlvPlayer sets this value based on property, etc.
					_self.log("preroll platform is FW.");
					playlist.push({type:'freewheelAd', content_id:_self.curClipData.content_id, duration:bam.media.getDurationInSeconds(_self.curClipData.duration)});					
				}
				else {
					_self.log("preroll platform is DC. url: " + adUrl);
					playlist.push({type:'dartPreroll', path:adUrl});
				}
				playlist.push({type:'video', path:_self.curClipData.curVideoFlashUrl});
				_self.flashPlayer.startPlaylist(playlist);
				_$event.trigger("startPlayback");
				// add to played videos
				if ($.inArray(_self.curClipData.content_id, _playedVideos)!==-1){
					_self.log("adding " + _self.curClipData.content_id + " to _playedVideos");
					_playedVideos.push(_self.curClipData.content_id);
				}
			}
			else {
				_self.log("error loading data");
				$("#zPlayerHeadline").text("This video is temporarily unavailable");
			}
		},
		
		
		/**
		* Checks if data for the passed content_id is currently loaded (in .curClipData)
		* @param content_id string
		* @return boolean
		*/
		dataIsLoaded: function(content_id){
			var isClipDataLoaded = !!(_self.curClipData && _self.curClipData.curVideoFlashUrl);
			if (isClipDataLoaded && content_id!==_self.curClipData.content_id){
				isClipDataLoaded = false;
			}
			_self.log("dataIsLoaded(): " + isClipDataLoaded);
			return isClipDataLoaded;
		},
		
		
		/**
		* Loads data for clip with passed content_id by calling bam.media.getMetaData(), 
		* into .curClipData property, unless its already loaded.
		* @param content_id String
		* @return boolean
		*/
		loadClipData: function(content_id){
			if (!_self.dataIsLoaded(content_id)){
				_self.curClipData = bam.media.getMetaData(content_id);
				if (_self.curClipData && typeof _self.curClipData.urls != "undefined"){
					var urlNode = {};
					var psMap   = {};
					$.each(_self.curClipData.urls, function(i, url){ // load available playback scenarios into a hash for easy reference
						if (url.getAttribute("playback_scenario")){
							psMap[url.getAttribute("playback_scenario")] = url;
						}
						else if (url.getAttribute("speed")==="800" && url.getAttribute("type")==="flash-video"){
							psMap["v2_url"] = url;
						}
					});
					_self.log("looking for playback scenario match...");
					$.each(_config.psKeys, function(i, psKey){
						if (psMap[psKey]){
							_self.log("found it: " + psKey);
							urlNode = psMap[psKey]; // get the first match and exit
							return false;
						}
					});
					if (urlNode.firstChild && urlNode.firstChild.nodeValue){
						_self.curClipData.curVideoFlashUrl = urlNode.firstChild.nodeValue;
						_self.curClipData.curVideoFlashId  = urlNode.getAttribute("id");
						_self.curClipData.playbackScenario = urlNode.getAttribute("playback_scenario");
						_self.curClipData.sequenceId       = urlNode.getAttribute("sequence") || "Not Available";
						_self.curClipData.cdn              = urlNode.getAttribute("cdn")      || "Not Available";
						var bitRateArr = _self.curClipData.playbackScenario.match(/(\d+K)/);
						_self.curClipData.bitRate = (bitRateArr) ? bitRateArr[1] : "";				
					}
					else { 
						return false;
					}
				}
				else { 
					return false;
				}
			}
			return true;
		},
		
		
		/**
		* Handler for zPlayer's close button. Stops playback and calls collapsePlayer()
		* @param Object Optional parameter which contains compActivity property.
		* @TODO fix param for trigger
		*/
		close: function(/* {compActivity:""} */){
			_self.log("close");
			var trackCompActivity = "Z Player Close Button Click";
			if (typeof arguments[0]=="object" && arguments[0].compActivity){
				trackCompActivity = arguments[0].compActivity;
			}
			_$event.trigger("beforeClose", trackCompActivity);
			if (_self.flashPlayer) {
				_self.flashPlayer.execute("stopPlaylist");
			}
//			else if (!_relatedIsDisplayed){
//				// If _self.flashPlayer doesn't exist AND related content is NOT displayed, 
//				// then user already clicked the Close button before and we're ignoring this call
//				// to prevent calling collapsePlayer on a non-existent player
//				return;
//			}
			_self.collapsePlayer();
		},
		
		
		/**
		* Collapses the player by destroying flvPlayer instance and data, and hiding DOM elements.
		*/
		collapsePlayer: function(){
			_self.log("collapsing player");
			_self.killFlvPlayer();
			delete(_self.curClipData);
			$("#zPlayer_leaveBehind").empty().hide();
			_relatedIsDisplayed = false;
			$("#zPlayerOuter").hide().css({"background":"none"});
			$("#zPlayerHeadline").text("");
			$("#zPlayerInner").css({width:_config.zoomStartDims.w, height:_config.zoomStartDims.h, background:"#222222", opacity:0});// });
			bam.overlay.hide({callback:function(){
				if (_previousOverlayCSS){
					bam.overlay.setCSS(_previousOverlayCSS);
					_previousOverlayCSS = null;
				}
			}});
			if (_isMacXFF2()){
				$("embed,iframe").css("visibility","visible");
			}
			_isOpen = false;
			_isPlaying = false; // this is here since onPlaylistComplete is now always called (player often keeps playing with an end poster)			
			_playInitiated = false;
			_$event.trigger("afterClose");
		},
		
		
		/**
		* Public read access to private isPlaying variable.
		*/
		isPlaying: function(){
			return _isPlaying;
		},
		
		
		/**
		* Public read access to private isOpen variable.
		*/		
		isOpen: function(){
			return _isOpen;
		},
		
		
		/**
		* Sets clip data to the passed object.
		*/
		setClipData: function(newClipData){
			if (!_self.curClipData){
				_self.curClipData = {
					content_id       : "",
					headline         : "",
					curVideoFlashUrl : "",
					playbackScenario : "",
					bitRate          : "Not Available",
					sequenceId       : "Not Available",
					cdn              : "Not Available"
				};	
			}
			$.extend(_self.curClipData, newClipData);
		},
		
		
		/**
		* Handles case where the player is already playing but needs to be updated
		* with new content.
		*/
		updateClip: function(newClipData){
			_self.setClipData(newClipData);
			$("#zPlayerHeadline").text(_self.curClipData.headline);
			// another tracking call here (to pass updated sequence id)?
		},
		

		/** 
		* DEPRECATED. Use bam.zPlayer.bind("beforePlay") and bam.zPlayer.bind("afterClose") instead 
		*/
		prePlay: function(){},
		postClose: function(){},
				

		/**
		* DEPRECATED. Instead, call bam.zPlayer.reconfigure directly.
		* Updates the private psKeys (playback scenario keys) array. This allows 
		* instances of Z Player to set their own playback scenario(s) and not use
		* the default ones.
		* @param newKeys Array
		*/
		setPlaybackScenarioKeys: function(newKeys){
			if (newKeys instanceof Array){
				_self.reconfigure({psKeys:newKeys});
			}
		}		
		
	};
	

	// Handler for loading related css on first render of related content
	_$event.one("showingRelatedContent", function(){
		_self.log("loading related CSS since its hasn't been loaded already");
		bam.loadCSS(_config.cssRelatedUrl);
	});
	
	
	// If bam.tracking is available, bind tracking calls to custom event triggers
	if (bam.tracking){
	
		_$event.bind("beforePlay", function(){
			_self.log("tracking on beforePlay");
			bam.tracking.track({
				async:{
					isDynamic    : false,
					compName     : "Z Player Interaction",
					compActivity : "Z Player Launch",
					actionGen    : true
				}
			});
		});

		_$event.bind("startPlayback", function(){
			if (typeof club=="string" && typeof section=="string"){
				_playerContext = club.toUpperCase() + " " + section.substring(0,1).toUpperCase() + section.substring(1) + " Z Player";
			}
			_self.log("TRACKING on startPlayback", _playerContext);
			bam.tracking.track({
				async_media:{
					mediaID        : _self.content_id + "|" + _self.curClipData.playbackScenario,
					playerType     : "Flash",
					playerContext  : _playerContext,
					contextVersion : "3.0",
					streamType     : "Progressive Download",
					bitRate        : _self.curClipData.bitRate,
					sequenceID     : _self.curClipData.sequenceId,
					cdn            : _self.curClipData.cdn,
					actionGen      : (typeof(_self.actionGen)=="boolean" ? _self.actionGen : true) // actionGen is set externally, or defaulted to User Generated
				}
			});
		});
		
		_$event.bind("playlistComplete", function(){
			_self.log("TRACKING on playlistComplete");
			bam.tracking.track({videoComplete:{playerContext:_playerContext}});
		});
		
		_$event.bind("beforeClose", function(event, compActivity){
			_self.log("TRACKING on beforeClose");
			bam.tracking.track({
				async:{
					isDynamic    : false,
					compName     : "Z Player Interaction",
					compActivity : compActivity,
					actionGen    : true
				}
			});
		});
			
		_$event.bind("collapsePlayer", function(){
			_self.log("tracking on collapsePlayer");
			bam.tracking.track({
				async:{
					isDynamic    : false,
					compName     : "Z Player Interaction",
					compActivity : "Z Player Auto Close",
					actionGen    : true
				}
			});
		});
	
	} // end if bam.tracking
		
	
	// These calls add support for (now deprecated) prePlay() and postClose() methods. For backwards support only.
	_$event.bind("beforePlay", function(){ _self.prePlay(); });
	_$event.bind("afterClose", function(){ _self.postClose(); });
	
	return _self;
})();
