/**
 * bam.zPlayer : Lightbox-style, draggable (and now highly customizable) Video Player
 *
 * @author Aleksandar Kolundzija
 * @version 2.1
 *
 * *************************************************************************************************
 *  Z Player is now an instantiatable object. For ease of use and backwards compatibility, 
 *  old initiation syntax still works since instance of bam.zPlayer is created in this file (bottom)
 * *************************************************************************************************  
 *
 * To use default player, 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)
 *
 * To make a custom (branded, etc) version of the Z player, create an instance of bam.ZPlayer(customConfig)
 * (see _cfg below for supported customization options.)
 * 
 * Start of playback flow:
 * play() > {init()} > createFlvPlayer() > {onPlayerLoaded()} > playClip() > loadClipData() > startPlayback()
 *
 * @TODO Resolve potential collisions between companion ads for multiple instances of the player (same ad #ids)
 * @TODO Make isPlaying a public static property
 */

bam.ZPlayer = function(cfg){
	
	var _self          = this,
	
			_initialized   = false,
			_isPlaying     = false,
			_playInitiated = false, // used to prevent double clicks on play button			
			_isOpen        = false,
			
			_previousOverlayDisplayed,
			_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
			_relatedIsDisplayed = false,
			_topicId            = null,
			
			_defaultBgImg = "/shared/images/bam/zPlayer/bg.png",
						
			_$outerContainer = null, // reference to z player container div, assigned on init()
			_$flvContainer   = null, // reference to swf container div, assigned on init()
			
			// player configuration
			_cfg = {
				bgImageUrl    : _defaultBgImg, // for pre-loading
				cssUrl        : "/shared/css/bam/bam.zPlayer.css?v=2",
				cssRelatedUrl : "/shared/css/bam/bam.zPlayer.related.css?v=2", // can be used to override default css
				
				hOffset   : (window.club && club==="mlb" && window.section && section==="homepage") ? -60 : 0, // horizontal offset (from center) adjusted to be centered over mlb.com's mediawall.
				vOffset   : 95, // vertical offset (from top)
				draggable : true,
				
				playerFlavor : "None",
				
				companionAd: {
					show : false,
					w    : 728,
					h    : 90
				},
				
				showRelatedContentOverride : false, // used to force display of related content leave-behind
				relatedContentUrlOverride  : "", // custom source of related content leave-behind data
				relatedContentBgOverride   : "", // custom background image
				relatedKeywordCategories   : ["mlbtax_key", "team_id", "player_id", "game_pk"],
				redirectBadBrowsers        : true, // flag used to force incompatible browsers (currently only MacXFF2) to the VPP for playback		
								
				// swf params
				blankImgUrl    : "/images/000000.gif",
				replayImgUrl   : "/shared/images/bam/zPlayer/buttonReplay.png",
				skinUrl        : "/flash/video/y2009/skins/mlb_media_landing_full.swf",
				vppUrl         : "/media/video.jsp?", // for related content links
				playerObjName  : "bam.zPlayer.flashPlayer", // passed to swf for communication (deprecated - no longer needed)
				defaultVolume  : 60,
				containerId    : "zPlayerOuter",				
				flvElemId      : "", // id assigned to swf DOM element
				flvContainerId : "", // id of swf container element
				pageComponent  : "z_player", // used when assembling siteSection value (used in bam.FlvPlayer) for ads
				
				psKeys        : ["FLASH_1200K_640X360", "FLASH_1000K_640X360", "FLASH_800K_640X360", "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"},

				debugMode : false,

				MESSAGES: {
					errorLoadingRelatedContent: "Related content is temporarily unavailable."	
				}
			},
			
			_prevConfig = null; // for restoring previous configuration
		
		
	/**
	 * Logs messages to console when debugMode is true. Prefixes instance name.
	 */
	function _log(msg){
		if (typeof console!=="undefined" && _self.debugMode){ 
			console.log(_cfg.playerObjName.replace(/.flashPlayer/i, "") + ": " + msg);
		}
	}
	
	
	/**
	 * 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 = parseInt(RegExp.$1, 10);
			if (ffversion < 3 && userAgent.indexOf("mac") != -1){ return true; }
		}
		return false;
	}
	
	
	/**
	 * Private function bound-to/unbound-from overlay div
	 */
	function _trackOverlayClick(){
		_self.close({compActivity:"Z Player Outside Close Click"});
	}
	
	
	/**
	 * 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"});
	}
	
	
	/** 
	 * Private method for loading JS dependencies.
	 * @TODO Consider using public static areDependeiciesLoaded flag (note: can't assume $.draggable was loaded)
	 */
	function _loadDependencies(){
		if (typeof bam.media === "undefined"){ _getScriptSync("/shared/scripts/bam.media.js"); }
		if (typeof bam.media.relatedContent === "undefined"){ _getScriptSync("/shared/scripts/bam/bam.media.relatedContent.js"); }
		if (typeof bam.FlvPlayer === "undefined") { _getScriptSync("/shared/scripts/bam.FlvPlayer.js"); }		
		if (typeof $.easing === "undefined"){ _getScriptSync("/shared/scripts/external/jquery.easing.js"); }
		if (_cfg.draggable && typeof $.fn.draggable==="undefined"){ _getScriptSync("/shared/scripts/external/jquery-ui-draggable-1.8.min.js"); }		
		if (typeof bam.overlay === "undefined"){ bam.loadSync("/shared/scripts/bam/bam.overlay.js"); }
	}
		
	
	/**
	 * Returns HTML for display of companion ad if configured to show one.
	 * Otherwise returns an empty string.
	 * @return {string}
	 */
	function _getCompanionAdCode(){
		var adHtml = "";
		if (_cfg.companionAd.show){
			adHtml = '' + 
				'<div class="zPlayerCompAd">' + 
					'<div id="videoCompanionAd">' + 
					'</div>' +
				'</div>';
		}
		return adHtml;
	}
	
	
	/////////////////// public properties ////////////////////
	_self.curClipData = null;
	_self.flashPlayer = null;
	_self.content_id  = null;
	
	
	// public setters and getters
	_self.isPlaying    = function(){ return _isPlaying; }; // Public read access to private isPlaying variable.
	_self.isOpen       = function(){ return _isOpen; }; // Public read access to private isOpen variable.
	_self.setDebugMode = function(mode){ _cfg.debugMode = mode; }; // Public debug mode setter
	

	/**
	 * Private method which renders (zooms in) player and calls createFlvplayer 
	 * which triggers start of playback. Called by play().
	 */
	_self.showPlayer = function(){
		_log("starting playback");
		var fullWidth = parseInt($("body").innerWidth(),10),
				elemWidth = parseInt(_$outerContainer.css("width"),10),
				posLeft   = ((fullWidth/2) - (elemWidth/2)) + _cfg.hOffset, // offset so that its directly over media wall
				posTop    = $(document).scrollTop() + _cfg.vOffset;
		_$outerContainer.css({top:posTop+"px",left:posLeft+"px"}).show();
		_$outerContainer.find(".zPlayerInner")
			.animate({width:_cfg.zoomEndDims.w, height:_cfg.zoomEndDims.h, opacity:1}, "slow", function(){
				_$outerContainer.css({"background-image":"url("+_cfg.bgImageUrl+")"}).find(".zPlayerInner").css({"border":"none","background-color":"transparent"});
				_isOpen = true;
				_self.killFlvPlayer(); // will check if exists first
				_self.createFlvPlayer();
				_self.trigger("afterPlay");
				if (_cfg.draggable && typeof $.fn.draggable==="function"){
					_$outerContainer.draggable({containment:"document", cancel:"#"+_cfg.flvContainerId+", .zPlayer_leaveBehind, .zPlayerCloseBtn"});
					_$outerContainer.find(".zPlayerClipInfo").hover(function(){ $(this).css({cursor:"move"}); }, function(){ $(this).css({cursor:"default"}); });
				}			
			});
	};


	/**
	 * Public method for initiating playback.
	 * @param clipData Object, String (content_id) OR Number (content_id)
	 * @param topicId String (Optional)
	 */
	_self.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()");
		}
		_topicId = arguments[1]; // passed value or undefined
		if (_isMacXFF2() && _cfg.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;
		}
		_self.trigger("beforePlay");
		if (!_initialized){
			_self.init();
		}
		if (!_isMacXFF2()){
            _previousOverlayDisplayed = bam.overlay.isDisplayed();

			if ($("#overlay").length){
				_previousOverlayCSS = {"background-color":$("#overlay").css("background-color"), "opacity":$("#overlay").css("opacity")};
				$("#overlay").bind("click",_trackOverlayClick);
			}
			bam.overlay.setCSS({"background-color":"#ffffff"});			
			bam.overlay.show({fadeSpeed:"fast", opacity:"0.3", callback:bam.object.proxy(_self.showPlayer, _self)});
		}
		else{
			$("embed,iframe").css("visibility","hidden");
			_self.showPlayer();
		}
	};
				
		
	/**
	 * initializes zPlayer by loading required JavaScript files (if not already loaded)
	 * and appending required elements to the DOM.
	 * @TODO do something about that custom IE6 background (conflicts with reconfiguration)
	 */
	_self.init = function(){
		_log("initializing...");
		// assign flv container Ids
		if (!_cfg.flvElemId){      _cfg.flvElemId      = _cfg.containerId + "_flashPlayer"; } // id assigned to swf DOM element
		if (!_cfg.flvContainerId){ _cfg.flvContainerId = _cfg.containerId + "_flvContainer"; } // id of swf container element
		if ((/MSIE (6)/.test(navigator.userAgent) && navigator.platform==="Win32")){ 
			_cfg.bgImageUrl = _cfg.bgImageUrl.replace(/\.png$/i, ".gif"); // don't use png for IE6 (alpha+drag+flash+link problems)
		}
		var bgImage = new Image();
		bgImage.src = _cfg.bgImageUrl; // preload background image
		_loadDependencies();
		bam.loadCSS(_cfg.cssUrl);		
		var zPlayerHtml = '' +
			'<div id="'+_cfg.containerId+'">' +
				'<div class="zPlayerInner">' +
					'<div class="zPlayerClipInfo"><div class="zPlayerHeadline"></div><a class="zPlayerCloseBtn">Close</a></div>' +
					'<div id="'+_cfg.flvContainerId+'" class="zPlayer_flvContainer"></div>' + 
					'<div class="zPlayer_leaveBehind"></div>' + _getCompanionAdCode() + 
				'</div>' +
			'</div>';
		$(zPlayerHtml).appendTo("body");
		_$outerContainer = $("#"+_cfg.containerId);
		_$flvContainer   = $("#"+_cfg.flvContainerId);		
		_$outerContainer.find(".zPlayerInner").css({opacity:0, width:_cfg.zoomStartDims.w, height:_cfg.zoomStartDims.h});
		_$outerContainer.find(".zPlayerCloseBtn").click(_self.close);
		bam.overlay.init();
		_initialized = true;
		_self.trigger("afterInit");
	};

		
	/**
	 * Creates instance of FLV Player as a public property (bam.zPlayer.flashPlayer)
	 */
	_self.createFlvPlayer = function(){
		_log("createFlvPlayer()");
		_$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               : _cfg.skinUrl,
			endPosterPath      : _self.shouldShowRelatedContent() ? _cfg.blankImgUrl : _cfg.replayImgUrl,
			width              : _cfg.playerDims.w,
			height             : _cfg.playerDims.h,
			elemId             : _cfg.flvElemId,
			containerId        : _cfg.flvContainerId,
			wmode              : "transparent",
			defaultVolume      : _cfg.defaultVolume,
			debugMode          : _self.debugMode,
			onPlayerLoaded     : _self.onFlvPlayerLoaded,
			onPlaylistComplete : _self.onPlaylistComplete,
			onCollapse         : _self.onCollapse,
			pageComponent      : _cfg.pageComponent,

            showDefaultCompanionAd : _cfg.showDefaultCompanionAd || null,
            showCompanionAd : function() {
                var companionAds, ad,
                    adContainerID = "videoCompanionAd";
                
                //console.log("showCompanionAd", arguments);
                if (arguments.length > 0) {

                     companionAds = Array.prototype.slice.call(arguments[0]);

                    if (companionAds.length > 0) {
                        $.each(companionAds, function(i, companionAd) {
                            var tagName = (companionAd.type === "iframe") ? "iframe" : "div",
                                width = parseInt(companionAd.width, 10),
                                height = parseInt(companionAd.height, 10),
                                ad = $("<" + tagName + " />", {
                                    height : height,
                                    width : width
                                });

                            // only allow 728x90 for z-player companion
                            if ( ! (width === 728 && height === 90)) {
                                return;
                            }

                            if (companionAd.type === "iframe") {
                                ad.attr("src", companionAd.data)
                                  .attr("marginwidth", "0")
                                  .attr("marginheight", "0")
                                  .attr("frameborder", "0")
                                  .attr("scrolling", "no");
                            } else {
                                ad.html(companionAd.data);
                            }
                            
                            if (companionAds.length > 1) {
                                ad.appendTo("#" + adContainerID);
                            } else {
                                $("#" + adContainerID).empty().append(ad);
                            }
                            
                        });
                    }
                }
            }
		});

		_log("createFlvPlayer(): ad platform: " + _self.flashPlayer.adPlatform);
	};


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


	/**
	 * Handler triggered by flv player (SWF) after it has completed playback of the playlist
	 */
	_self.onPlaylistComplete = function(){
		_self.trigger("playlistComplete");
		_self.flashPlayer.execute("exitFullScreen");
		_isPlaying = false;
		if (_self.shouldShowRelatedContent()){
			_self.showRelatedContent();
		}
		_log("done with onPlaylistComplete");			
	};
		
		
	/**
	 * Checks if related content leave-behind should be displayed - currently only on MLB,
	 * and when section is homepage or the 2009 ws landing page
	 * @return boolean
	 * @TODO Revisit WS switches
	 */
	_self.shouldShowRelatedContent = function(){
		return (
			(_cfg.showRelatedContentOverride) ||
			(typeof(club)!=="undefined" && typeof(section)!=="undefined" && typeof(page_id)!=="undefined") &&
			(section==="homepage" || (section==="world_series" && page_id==="ps_2009_ws_landing"))
		);
	};
	
	
	/**
	 * Displays leave-behind screen with replay option and related videos which
	 * link to the Video Playback Page.
	 */
	_self.showRelatedContent = function(){
		_log("showRelatedContent");
		_self.trigger("showingRelatedContent");
		var bgImg = new Image();
		bgImg.src = _cfg.relatedContentBgOverride; // start loading the image
		_self.killFlvPlayer(function(){ 
			_$flvContainer.hide();
			_$outerContainer.find(".zPlayerHeadline").text("");
			var html = '' +
				'<div class="zPlayer_replayInfo">' +
					'<a class="zPlayer_replayButton" href="#">Click to Replay</a>' +
					'<div class="zPlayer_clipThumb"><img src="'+_self.curClipData.thumb+'"></div>' + 
					'<div class="zPlayer_clipTitle">'+_self.curClipData.headline+'</div>' + 
					'<div class="zPlayer_clipBlurb">'+_self.curClipData.blurb+'</div>' + 
				'</div>' + 
				'<div class="zPlayer_relatedVideos"></div>';
			if (_cfg.relatedContentBgOverride){
				_$outerContainer.find(".zPlayer_leaveBehind").css({"background-image":"url("+_cfg.relatedContentBgOverride+")"});
			}
			_$outerContainer.find(".zPlayer_leaveBehind").append(html).show();
			_$outerContainer.find(".zPlayer_replayButton").one("click", function(evt){
				evt.preventDefault();
				_$outerContainer.find(".zPlayer_leaveBehind").empty().hide();
				_$flvContainer.show();
				_relatedIsDisplayed = false;
				_self.createFlvPlayer();
			});
			_self.loadRelatedContent();
		});													
	};

	
	/**
	 * Loads and renders related content
	 */
	_self.loadRelatedContent = function(){
		_log("loadRelatedContent");		
		bam.media.relatedContent.load({
			indexFileOverride       : _topicId ? "" : "/gen/"+club+"/components/multimedia/topvideos.xml",
			topicId                 : _topicId,
			contentId               : _self.content_id,
			ignoreContentIds        : _playedVideos,
			useContentKeywords      : (typeof _topicId==="undefined"),
			searchKeywordCategories : _cfg.relatedKeywordCategories,
			success                 : function(index, topicConfig){
				var html      = "<ul>",
						vppParams = {};
				if (!index.length){
					_$outerContainer.find(".zPlayer_relatedVideos").html("<div class='zPlayer_relatedVideosError'>"+_cfg.MESSAGES.errorLoadingRelatedContent+"</div>");
					return;
				}
				$.each(index, function(i, curItem){
					if (typeof club!=="undefined" && club!=="mlb"){ vppParams.c_id = club; }
					if (_topicId){ vppParams.topic_id = _topicId; }
					vppParams.content_id = curItem.content_id;
					html += "<li><a href='" + _self.getVppUrl(vppParams) + "'><img src='"+curItem.thumb+"'/><div>"+curItem.headline+"</div></a></li>";
				});
				html += "</ul>";
				_$outerContainer.find(".zPlayer_relatedVideos").html(html);
				_relatedIsDisplayed = true;
				// _log("rendered related content");			
			},
			error : function(){
				_$outerContainer.find(".zPlayer_relatedVideos").html("<div class='zPlayer_relatedVideosError'>"+_cfg.MESSAGES.errorLoadingRelatedContent+"</div>");
			}
		});
	};
	

	/**
	 * Handles the onCollapse event from the FlvPlayer(swf). That event is generally 
	 * triggered by end-of-stream marker in video content itself.
	 */
	_self.onCollapse = function(){
		// debugging
		if(~document.location.search.indexOf("debug")) console.log("_self.onCollapse");
		setTimeout( function(){
			_self.collapsePlayer();
			_self.trigger("onCollapsePlayer");
		}, 500);
	};

	
	/**
	 * If the instance of flvPlayer exists, it empties the container and deletes the object.
	 */
	_self.killFlvPlayer = function(callback){
		if (_self.flashPlayer){
			_log("killFlvPlayer(): stopping, then killing flv player");
			_self.flashPlayer.execute("stopPlaylist");
			_self.flashPlayer.clearPlaylist();
			delete(_self.flashPlayer);
			setTimeout(function(){ 
				_$flvContainer.empty();
				if (typeof callback === "function"){ callback(); }
			}, 10);
		}
		else {
			_log("killFlvPlayer(): player did not exist. doing nothing.");
		}
	};
	
	
	/** 
	 * Returns URL for Video Playback Page. Override locally for different URL support
	 * @param paramHash Object
	 */
	_self.getVppUrl = function(paramHash){
		var url = _cfg.vppUrl;
		for (var paramName in paramHash){
			if (paramHash[paramName]){
				url += paramName + "=" + paramHash[paramName] + "&";
			}
		}
		return url;
	};
		
	
	/**
	 * Loads data for content_id (unless its already loaded), and plays clip
	 * @param content_id String (integer doesn't work here)
	 */
	_self.playClip = function(content_id){
		_isPlaying = true;
		_log("playClip("+content_id+")");
		if (_self.loadClipData(content_id)){
			_$flvContainer.show();
			_log("startingPlaylist: " + _self.curClipData.curVideoFlashUrl);
			var adUrl      = _self.getPrerollUrl(),
					playlist   = [],
					adPlatform = _self.flashPlayer.adPlatform;
			_$outerContainer.find(".zPlayerHeadline").html(_self.curClipData.headline);
			if (adPlatform==="dc"){ // FlvPlayer sets this value based on property, etc.
				_log("ad platform is DC. url: " + adUrl);
				playlist.push({type:'dartPreroll', path:adUrl});
			}
			playlist.push({
				type       : 'video',
				path       : _self.curClipData.curVideoFlashUrl,
				content_id : _self.curClipData.content_id,
				duration   : _self.curClipData.duration ? bam.media.getDurationInSeconds(_self.curClipData.duration) : 0
			});
			_self.flashPlayer.startPlaylist(playlist);
			_self.trigger("startPlayback");
			// add to played videos
			if ($.inArray(_self.curClipData.content_id, _playedVideos)===-1){
				_log("adding " + _self.curClipData.content_id + " to _playedVideos");
				_playedVideos.push(_self.curClipData.content_id);
			}
		}
		else {
			_log("error loading data");
			_$outerContainer.find(".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
	 */
	_self.isDataLoaded = function(content_id){
		var isClipDataLoaded = !!(_self.curClipData && _self.curClipData.curVideoFlashUrl);
		if (isClipDataLoaded && content_id!==_self.curClipData.content_id){
			isClipDataLoaded = false;
		}
		_log("isDataLoaded(): " + 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
	 * @TODO use jQuery selectors to retrieve XML data
	 */
	_self.loadClipData = function(content_id){
		_log("loadClipData("+content_id+")");
		if (!_self.isDataLoaded(content_id)){
			if (_self.loadClipData._cache && _self.loadClipData._cache[content_id]){
				_log("loadClipData: loading from cache");
				_self.curClipData = _self.loadClipData._cache[content_id];
				return true;
			}
			_log("loadClipData: loading new data");
			_self.curClipData = bam.media.getMetaData(content_id);
			if (_self.curClipData && typeof _self.curClipData.urls != "undefined"){
				var urlNode = {},
						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;
					}
				});
				// _log("looking for playback scenario match...");
				$.each(_cfg.psKeys, function(i, psKey){
					if (psMap[psKey]){
						// _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] : "";
					if (!_self.loadClipData._cache){
						_self.loadClipData._cache = {};
					}
					_self.loadClipData._cache[content_id] = _self.curClipData; // cache data
					_log("loadClipData: saving to cache");
				}
				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
	 */
	_self.close = function(/* {compActivity:""} */){
		// _log("close");
		var trackCompActivity = "Z Player Close Button Click";
		if (typeof arguments[0]==="object" && arguments[0].compActivity){
			trackCompActivity = arguments[0].compActivity;
		}
		_self.trigger("beforeClose", trackCompActivity);
		if (_self.flashPlayer && _self.flashPlayer.execute) {
			_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.
	 * @TODO remove _prevConfig stuff
	 */
	_self.collapsePlayer = function(){
		// _log("collapsing player");
		// debugging
		if(~document.location.search.indexOf("debug")) console.log("_self.collapsePlayer");
		_self.killFlvPlayer();
		delete(_self.curClipData);
		_$outerContainer.find(".zPlayer_leaveBehind").empty().hide();
		_relatedIsDisplayed = false;
		_$outerContainer.hide().css({"background":"none"});
		_$outerContainer.find(".zPlayerHeadline").text("");
		_$outerContainer.find(".zPlayerInner").css({width:_cfg.zoomStartDims.w, height:_cfg.zoomStartDims.h, background:"#222222", opacity:0});// });
		if (!!_previousOverlayDisplayed && !!_previousOverlayCSS){
			bam.overlay.setCSS(_previousOverlayCSS);
			_previousOverlayCSS = null;
		} else {
			bam.overlay.hide({callback:function(){
				if (_previousOverlayCSS){
					bam.overlay.setCSS(_previousOverlayCSS);
					_previousOverlayCSS = null;
				}
			}});
		}
		$("#overlay").unbind("click",_trackOverlayClick);
		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;
		if (_prevConfig){
			// _log("restoring old config...");
			$.extend(_cfg,_prevConfig);
			_initialized = false;
			_prevConfig  = null;				
			_$outerContainer.remove();
		}
		this.trigger("afterClose");
	};
	
	
	/**
	 * Sets clip data to the passed object.
	 */
	_self.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.
	 */
	_self.updateClip = function(newClipData){
		_self.setClipData(newClipData);
		_$outerContainer.find(".zPlayerHeadline").text(_self.curClipData.headline);
		// another tracking call here (to pass updated sequence id)?
	};
				

	/**
	 * DEPRECATED (use custom instance of player when needed)
	 * Reconfigures private _cfg object with new properties. Use to reconfigure zPlayer
	 * @param cfg Object
	 * @TODO Consider getting rid of _prevConfig since it shouldn't be used (in favor of new instances)
	 */
	_self.reconfigure = function(cfg){
		// _log("reconfiguring zPlayer");
		_prevConfig = {};
		$.extend(_prevConfig, _cfg);
		$.extend(_cfg, cfg);
	};


	/**
	 * DEPRECATED. Instead, call bam.zPlayer.reconfigure directly.
	 * @param newKeys Array
	 */
	_self.setPlaybackScenarioKeys = function(newKeys){
		if (newKeys instanceof Array){ _self.reconfigure({psKeys:newKeys}); }
	};



	/******************* constructor ********************/
	$.extend(_cfg, cfg);  // install config
	_self.debugMode = _cfg.debugMode;

	// Handler for loading related css on first render of related content
	_self.one("showingRelatedContent", function(){
		bam.loadCSS(_cfg.cssRelatedUrl);
	});
	
	
	// If bam.tracking is available, bind tracking calls to custom event triggers
	if (bam.tracking){
	
		_self.bind("beforePlay", function(){
			// _log("tracking on beforePlay");
			bam.tracking.track({
				async:{
					isDynamic    : false,
					compName     : "Z Player Interaction",
					compActivity : "Z Player Launch",
					actionGen    : true
				}
			});
		});

		_self.bind("startPlayback", function(){
			if (typeof club==="string" && typeof section==="string"){
				_playerContext = club.toUpperCase() + " " + section.substring(0,1).toUpperCase() + section.substring(1) + " Z Player";
			}
			// _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",
					playerFlavor   : "Z Player Sponsor: " + _cfg.playerFlavor,
					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
				}
			});
		});
		
		_self.bind("playlistComplete", function(){
			// _log("TRACKING on playlistComplete");
			bam.tracking.track({videoComplete:{playerContext:_playerContext}});
		});
		
		_self.bind("beforeClose", function(event, compActivity){
			// _log("TRACKING on beforeClose");
			bam.tracking.track({
				async:{
					isDynamic    : false,
					compName     : "Z Player Interaction",
					compActivity : compActivity,
					actionGen    : true
				}
			});
		});
			
		_self.bind("onCollapsePlayer", function(){
			// _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.
	_self.bind("beforePlay", function(){ _self.prePlay(); });
	_self.bind("afterClose", function(){ _self.postClose(); });
	
	$.easing.def = "easeInOutExpo";
	
};

/************** public non-priveledged methods ***************/
bam.ZPlayer.prototype = {
			
	/**
	 * Returns URL for DoubleClick preroll.
	 * @TODO Start phasing this out.
	 */
	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;
	},
	
	
	/** 
	 * DEPRECATED. Use bam.zPlayer.bind("beforePlay") and bam.zPlayer.bind("afterClose") instead 
	 */
	prePlay   : function(){},
	postClose : function(){}	
	
};

// make Z Player instances bindable
$.bindable(bam.ZPlayer, "beforePlay afterInit startPlayback afterPlay playlistComplete showingRelatedContent relatedContentLoaded onCollapsePlayer beforeClose afterClose");

// make default constructor (for simple use and backwards compatibility)
$(document).ready(function(){ 
	bam.zPlayer = new bam.ZPlayer({});
});

