/** @namespace nfl.global.gc */
/** 
 * @name nfl.GameCenter
 * @namespace nfl.GameCenter provides namespace for gamecenter related classes and methods.
 */
nfl.namespace('nfl.global.gc');
nfl.namespace('nfl.GameCenter');

document.observe('dom:loaded',function(e){
	document.write = function(str){
		console.warn('foiled yet again! haha! aricake wuz here '+str);
	}
})
nfl.GameCenter.Utility = function(){
	return {
		data: function(){
			if(Prototype.Browser.IE && Prototype.Browser.Version == 6){
				this.data = function(){ 
					this.__data	= Object.clone(nfl.global.gc.engine.data[nfl.global.gc.engine.gameid]);
					return this.__data
				}
				return this.data();
			}
			this.__data	= nfl.global.gc.engine.data[nfl.global.gc.engine.gameid];
			return this.__data
		},
		replacePlayerNames:function(pnode){
			if(typeof pnode.players !== 'undefined'){
				if(Object.isArray(pnode.players)){
					//disabled to to performance concerns
					/*
					for(var pinc=0, plen=pnode.players.length; pinc < plen; pinc++){
						var pkey	= pnode.players[pinc];
						var plink	= this.getPlayerLink(pkey);
						//console.log('looking for player('+ pkey+') returned '+plink);
						
						if(plink){
							var prec	= this.getPlayerFromKey(pkey);
							var repl	= (prec.fn.substring(0,1)+'\\.'+prec.ln);
							var reg		= new RegExp(repl,'g');
							pnode.desc	= pnode.desc.replace(reg,plink); 
						}
					}
					*/
					pnode.linkified	= true;	
				}else{
					try{
						//disabled to to performance concerns
						/*
						for(pname in pnode.players){
							if(typeof pnode.players[pname].clubcode !== 'undefined'){
								var pkey	= pname;
								//console.log('looking for '+reg+' in '+pnode.desc+'\n'+(new RegExp(reg,'g')).test(pnode.desc));
								reg			= new RegExp((pnode.players[pname].playerName).replace('.','\\.'),'g');
								pnode.desc	= pnode.desc.replace(reg,this.getPlayerLink(pkey));
							}else{
								var pkey	= pnode.players[pname];
								var reg		= new RegExp(pname.replace('.','\\.'),'g');
								pnode.desc	= pnode.desc.replace(reg,this.getPlayerLink(pkey));
							}
						}
						*/
						pnode.linkified	= true;
					}catch(e){}
				}
			}
		},
		getPlayerFromKey: function(pkey,datasrc){
			var data = (typeof datasrc === 'undefined') ? nfl.GameCenter.Utility.data() : datasrc;
			var prec	= false;
			try{
				//console.info('data.home.players = '+Object.toJSON(data.home.players));
				if(typeof data.home.players[pkey] !== 'undefined'){
					prec	= data.home.players[pkey];
				}
			}catch(e){}
			try{
				//console.info('data.away.players = '+Object.toJSON(data.away.players));
				if(typeof data.away.players[pkey] !== 'undefined'){
					prec	= data.away.players[pkey];
				}
			}catch(e){}
			//console.log('getPlayerFromKey('+pkey+') returning '+ prec);
			if(prec !== false){ return prec; }
			return false;
		},
		getPlayerLink: function(pkey){
			var prec	= this.getPlayerFromKey(pkey);
			if(prec !== false){
				try{
					return '<a href="/players/profile?id='+ prec.esbid +'" class="nfl-player-link" data-id="'+ pkey +'">'+ (prec.fn.substring(0,1)+'.'+prec.ln) +'</a>';
				}catch(e){}
			}		
			return false
		},
		delinkNonRosterPlayers: function(container){
			if(nfl.global.gc.extdata.players !== 'undefined'){
				var playerlinks	= container.select('.nfl-player-link');
				playerlinks.each(function(ele){
					var pkey	= ele.getAttribute('data-id');
					if(!nfl.GameCenter.Utility.getPlayerFromKey(pkey,nfl.global.gc.extdata.players)){
						/* remove player links that are not part of a roster */
						//console.log('delinkNonRosterPlayers: removing '+pkey+'',nfl.GameCenter.Utility.getPlayerFromKey(pkey,nfl.global.gc.extdata.players));
						ele.replace(ele.innerHTML);
					}
				});
			}
		},
		getCacheBustInt: function(){
			var time, d;
			d = new Date();
			d.setSeconds(Math.floor(d.getUTCSeconds() / 10) * 10);
			d.setMilliseconds(0);
			time = d.getTime();
			return time
		},
		getTeamColors: function(team1abbr,team2abbr){
			var __teamColors = {
				'ARI': ['0xB1063A','FFFFFF'],
				'ATL': ['0x111111','111111'],
				'BAL': ['0x232176','111111'],
				'BUF': ['0x005496','DA2128'],
				'CAR': ['0x0099D9','DDDDDD'],
				'CHI': ['0x00143F','F26522'],
				'CIN': ['0xF04E23','111111'],
				'CLE': ['0xF26522','F26522'],
				'DAL': ['0x002A5C','D1D3D4'],
				'DEN': ['0x002A5C','F04E23'],
				'DET': ['0x006DB0','A6AEB0'],
				'GB': ['0x2A433A','FFC20E'],
				'HOU': ['0x00123F','00123F'],
				'IND': ['0x00427E','DDDDDD'],
				'JAC': ['0x006576','111111'],
				'KC': ['0xC9243F','FFC20E'],
				'MIA': ['0x006D6F','F26522'],
				'MIN': ['0x26175A','EEAD1E'],
				'NE': ['0x002A5C','D1D3D4'],
				'NO': ['0x111111','C6A876'],
				'NYG': ['0x012352','A30D2D'],
				'NYJ': ['0x2A433A','DDDDDD'],
				'OAK': ['0x111111','CCCCCC'],
				'PHI': ['0x003135','BFC0BF'],
				'PIT': ['0x111111','FFC20E'],
				'STL': ['0x002A5C','C6A876'],
				'SD': ['0x0080C5','FFC20E'],
				'SF': ['0xA30D2D','AF925D'],
				'SEA': ['0x2D5980','50B848'],
				'TB': ['0xA30D2D','64594B'],
				'TEN': ['0x4495D1','001532'],
				'WAS': ['0x96113C','FFC20E']
			}
			if(typeof __teamColors[team1abbr] !== 'undefined' && typeof __teamColors[team2abbr] !== 'undefined'){
				/* we have values for both abbrs */
				var __t1Color	= __teamColors[team1abbr][0];
				var __t2Color	= __teamColors[team2abbr][0];
				if(__t2Color.similarColor(__t1Color)){
					/* they are similar colors flip colors around */
					console.log('getTeamColors: team colors are too close.');
					__t2Color = __teamColors[team2abbr][1];
					return [
							__t1Color.replace('0x',''),
							__t2Color.replace('0x','')
					]
				}
				return [
						__t1Color.replace('0x',''),
						__t2Color.replace('0x','')
				]
			}
			return ['','']
		},
		overwriteTeamColorClasses: function(abbr1,abbr2){
			
			var __tColors 		= nfl.GameCenter.Utility.getTeamColors(abbr1,abbr2);
			var __classNames 	= [(abbr1+'colors'),(abbr2+'colors')];
			var __sTag			= '<style type="text/css">';
			if(__tColors[1] == 'FFFFFF' || __tColors[1] == 'DDDDDD'){
				__sTag			+= 'body #main-content .'+__classNames[1]+' {background-color: #'+__tColors[1]+' !important; color: #000000 !important; }';
				__sTag			+= 'body #main-content .'+__classNames[1]+' a:link, body #main-content .'+__classNames[1]+' a:visited, body #main-content .'+__classNames[1]+' a:hover {color: #000000 !important; }';
			}else{
				__sTag			+= 'body #main-content .'+__classNames[1]+' {background-color: #'+__tColors[1]+' !important}';
			}
			__sTag				+= '</style>';
			document.write(__sTag);
			
		},
		overrideTeamColorClasses: function(abbr1,abbr2){
			var __tColors 		= nfl.GameCenter.Utility.getTeamColors(abbr1,abbr2);
			var __classNames 	= [(abbr1+'colors'),(abbr2+'colors')];
			var __sTag			= document.createElement('style');
			if(__tColors[1] == 'FFFFFF' || __tColors[1] == 'DDDDDD'){
				var __sBlock	= document.createTextNode('body #main-content .'+__classNames[1]+' {background-color: #'+__tColors[1]+' !important; color: #000000; }');
			}else{
				var __sBlock	= document.createTextNode('body #main-content .'+__classNames[1]+' {background-color: #'+__tColors[1]+' !important}');
			}
			__sTag.appendChild(__sBlock);
			
			var __head			= document.getElementsByTagName("head")[0];
			__head.appendChild(__sTag);
		},
		updateTeamColors: function(div,abbr1,abbr2){
			var __tColors 		= nfl.GameCenter.Utility.getTeamColors(abbr1,abbr2);
			var __classNames 	= [(abbr1+'colors'),(abbr2+'colors')];
			console.info('updateTeamColors: '+abbr1+','+abbr2+' : ',__classNames,__tColors);
			if($(div)){
				//console.info('updateTeamColors:',$(div).select('.'+__classNames[1]));
				$(div).select('.'+__classNames[1]).each(function(ele){
					ele.removeClassName(__classNames[1]);
					if(__tColors[1] == 'FFFFFF' || __tColors[1] == 'DDDDDD'){
						ele.addClassName('lightBackground');
						ele.setStyle({'backgroundColor':'#'+__tColors[1],'color':'#000000'});
					}else{
						ele.setStyle({'backgroundColor':'#'+__tColors[1]});
					}
				}.bind(this));
			}
		}
	}
}();

/*
 * @author arianna.winters
 * @class nfl.carousel
 * @param {String} | {Object} parent container element of ul element
 */
nfl.carousel = Class.create({
	initialize: function(container,options){
		this.container	= $(container);
		console.log('initializing '+container);
		this.list		= this.container.select('.list-items').first();
		this.options	= (typeof options !== 'undefined')?options:{debug:false};
		this.rows		= (typeof this.options.rows !== 'undefined')? this.options.rows:1;
		this.evtprefix	= (typeof this.options.evtprefix !== 'undefined')?this.options.evtprefix:'nfl';
		this.totalPages	= -1;
		this.portwidth	= 0;
		this.totalWidth = 0;
		this.options.itempadding = (typeof this.options.itempadding !== 'undefined')? this.options.itempadding:-1;
		this.options.debug = (typeof this.options.debug !== 'undefined')?this.options.debug:false;
		
		this.console	= (this.options.debug == true)?console:{log:function(){},info:function(){},warn:function(){}}
		
		this.boundHandlers	= {};
		this.boundHandlers.onScroll = this.onScroll.bindAsEventListener(this);
		this.boundHandlers.onPageChange = this.onPageChange.bindAsEventListener(this);
		
		/* set list width to children total width */
		var tChildren	= this.list.select('li');
		tChildren.each(function(ele){
			this.totalWidth = this.totalWidth+(this.getItemTotalWidth(ele));
		}.bind(this));
		this.list.setStyle({width:(this.totalWidth)+'px'});
		this.console.log('set list style to '+this.list.getStyle('width'));
		this.scrollMultiplyer = 1;
		this.originalLeftMargin	= (parseInt(this.list.getStyle('margin-left').replace('px','')));
		//console.log('initializing '+container+' ending.');
		document.observe(this.evtprefix+':carousel:scroll',this.boundHandlers.onScroll);
		document.observe(this.evtprefix+':carousel:pagechange',this.boundHandlers.onPageChange);
		document.fire(this.evtprefix+':carousel:pagechange',{id:this.container.identify(),page:this.getCurrentPage()});
	},
	getItemTotalWidth: function(ele){
		var retWidth	= 0;
		if(ele){
			retWidth 	= ele.getWidth();
			var margins	= {left:(parseInt(ele.getStyle('margin-left'))),right:(parseInt(ele.getStyle('margin-right')))};
			
			if(margins.left > 0){retWidth = retWidth+margins.left;}
			if(margins.right > 0){retWidth = retWidth+margins.right;}
		}
		return retWidth
	},
	onPageChange: function(event){
		if(this.container.identify() == event.memo.id){
			this.console.info('nfl.carousel.onPageChange');
			currentPageEle	= this.container.select('.carousel-controls-page').first();
			totalPagesEle	= this.container.select('.carousel-controls-totalpages').first();
			this.portwidth 		= this.container.getWidth()+ Math.abs(this.originalLeftMargin);
			//this.console.info('nfl.photos.carousel.onPageChange: '+ this.totalWidth +' / '+ portwidth +' = '+ (Math.floor(this.totalWidth / portwidth)));
			//this.console.info('nfl.photos.carousel.onPageChange: '+ this.container.identify() +' total pages = '+ (Math.floor(this.totalWidth / portwidth)) +', current page = '+ this.getCurrentPage());
			if(totalPagesEle){totalPagesEle.update(this.getTotalPages());}
			if(currentPageEle){currentPageEle.update(this.getCurrentPage()+'');}
		}
	},
	getTotalPages: function(){
		if(this.totalPages === -1){
			if(!isNaN(Math.ceil(this.totalWidth / this.portwidth))){
				this.totalPages	= Math.ceil(this.totalWidth / this.portwidth);
				//this.console.info('nfl.photos.carousel.getTotalPages: is ie?'+ Prototype.Browser.IE +', version='+Prototype.Browser.Version);
			}
			if(this.totalPages < 1){
				this.totalPages	= 1;
			}
		}
		return this.totalPages
	},
	getCurrentPage: function(){
		itemcontainer	= this.list;
		portwidth 		= this.container.getWidth();
		return Math.ceil(Math.abs(parseInt(itemcontainer.getStyle('margin-left'))) / portwidth);
	},
	onScroll: function(event){
		this.console.info("nfl.carousel.onScroll");
		if(this.container.identify() == event.memo.id){
			var amountToScroll				= 0;
			var amountToScrollMultiplyer	= this.scrollMultiplyer;
			direction 		= event.memo.direction;
			itemcontainer	= this.list;
			//portwidth 		= this.container.getWidth()+ Math.abs(this.originalLeftMargin);
			portwidth 		= parseInt(this.container.getStyle('width'))+ Math.abs(this.originalLeftMargin);
			var newCoord	= 0;
			
			if(event.memo.multiplyer){ amountToScrollMultiplyer	= event.memo.multiplyer;}
			try{ 
				var itemLeft	= parseInt(itemcontainer.getStyle('margin-left'));
			}catch(e){
				itemcontainer.setStyle({'margin-left':'0px'});
				var itemLeft	= parseInt(itemcontainer.getStyle('margin-left'));
			}
			switch(direction){
				case 'left':
					//amountToScroll	= (itemLeft == (0+this.originalLeftMargin))?(0 - (portwidth * Math.floor((this.totalWidth - Math.abs(this.originalLeftMargin)) / portwidth))):(amountToScroll + portwidth);
					newCoord	= (itemLeft == (0+this.originalLeftMargin))?(0 - (portwidth * Math.floor((this.totalWidth - Math.abs(this.originalLeftMargin)) / portwidth))):(Math.abs(itemLeft) - portwidth);
					break;
				case 'right':
					newCoord	= ((Math.abs(itemLeft) + portwidth) <= this.totalWidth)?(0 - (Math.abs(itemLeft) + portwidth)):(0 + this.originalLeftMargin);
					break;
				default:
					break;
			}
			//now perform scroll operation based on value outputs from switch statement above
			if(Math.abs(newCoord) % portwidth == (0+this.originalLeftMargin)){
				//newLeft	= (itemLeft + amountToScroll);
				this.console.log("{nfl.carousel.onScroll: newCoord = "+ newCoord +"}");
				itemcontainer.morph('margin-left: ' + newCoord + 'px;',{afterFinish: function(){document.fire(this.evtprefix+':carousel:pagechange',{id:this.container.identify(),page:this.getCurrentPage()});}.bind(this), duration: .8, fps: 30});
			}else{
				this.console.warn('nfl.carousel.onScroll: item is in play.');
				//nfl.log("{nfl.carousel.onScroll: item is in play: "+ itemLeft +" % "+ portwidth +" == "+ (itemLeft % portwidth == (0+this.originalLeftMargin)) +"}");
			}
		}
	},
	destroy:function(){
		//document.stopObserving(this.evtprefix+':carousel:scroll', this.boundHandlers.onScroll);
		//document.stopObserving(this.evtprefix+':carousel:pagechange', this.boundHandlers.onPageChange);
		document.stopObserving(this.evtprefix+':carousel:scroll');
		console.info('nfl.carousel.destroy: destroyed');
	}
});

nfl.GameCenter.Slider	= Class.create({
	initialize:function(content, handle, track, options){
		this.content		= $(content);
		this.container		= $(content).up();
		this.handle			= $(handle);
		this.track			= $(track);
		this.onButtonDown	= this.onButtonPress.bind(this);
		this.onButtonUp		= this.onButtonUnPress.bind(this);
		this.onSlide		= this.onSliderChange.bind(this);
		
		this.options		= (typeof options !== 'undefined')?options:{};
		this.options 		= Object.extend({axis: 'vertical', increment: 1, interval: 100, range: $R(0, this.track.getHeight()), onChange: this.onSlide, onSlide: this.onSlide}, this.options);
		this.slider			= new Control.Slider(this.handle, this.track, this.options);
		this.buttons		= {};
		this.buttons.up		= (typeof this.options.up !== 'undefined')?($(this.options.up)):false;
		this.buttons.down	= (typeof this.options.down !== 'undefined')?($(this.options.down)):false;
		
		$H(this.buttons).values().each(function(button){
			button.observe('mousedown',this.onButtonDown);
			button.observe('mouseup',this.onButtonUp);
		}.bind(this));
		console.info('initialized nfl.GameCenter.Slider',this);
	},
	getContentPosFromInteger: function(value){
		var dims			= this.getMath();
		
		var heightNotShown	= dims.content.height - dims.container.height;
		var multiplyer		= (heightNotShown / dims.track.height);
		var retVal			= (0 - Math.abs(Math.round(value * multiplyer)));
		//console.info('nfl.GameCenter.Slider.getContentPosFromInteger: '+ retVal +','+ heightNotShown+', '+ value +','+multiplyer);
		return retVal;
	},
	getMath: function(){
		var retVal	= {
				container:{height: this.container.getHeight()},
				content:{height:this.content.getHeight()},
				track:{height:this.track.getHeight()}
		};
		retVal.multiplyer	= ((retVal.content.height - retVal.container.height) / retVal.track.height);
		
		return retVal
	},
	isScrollable: function(){
		var containerHeight	= this.container.getHeight();
		var contentHeight	= this.content.getHeight();
		if(contentHeight > containerHeight){ return true }
		return false
	},
	onSliderChange: function(value){
		/**/
		var newPos	= this.getContentPosFromInteger(value);
		//console.info('nfl.GameCenter.Slider.onSliderChange: '+ value);
		//console.info('nfl.GameCenter.Slider.onSliderChange: new content pos value = '+ newPos);
		this.content.setStyle({'marginTop':newPos+'px'});
	},
	onScroll: function(event){
		var dims			= this.getMath();
		var currentPos		= Math.abs(parseInt(this.content.getStyle('margin-top')));
		var sliderVal		= (currentPos / dims.multiplyer)

		var element	= event.element();
		if(element === this.buttons.up){
			console.info('nfl.GameCenter.Slider.Up');
			sliderVal = (sliderVal - (this.options.increment * 10));
		}
		if(element === this.buttons.down){
			console.info('nfl.GameCenter.Slider.Down');
			sliderVal = (sliderVal + (this.options.increment * 10));
		}
		
		this.slider.setValue(sliderVal, 0);
	},
	onButtonPress: function(event){
		console.info('nfl.GameCenter.Slider.onButtonPress');
		clearTimeout(this.timer);
		if(!this.isScrollable()){ return false; }
		this.timer = setInterval(function(event){this.onScroll(event)}.bind(this,event),this.options.interval);
	},
	onButtonUnPress: function(event){
		console.info('nfl.GameCenter.Slider.onButtonUnPress');
		clearTimeout(this.timer);
	},
	onDestroy: function(event){
		$H(this.buttons).values().each(function(button){
			button.stopObserving('mousedown',this.onButtonDown);
		}.bind(this))
	}
});


/*
 * Game Center Tabs
 */

nfl.GameCenter.Tabs	= function(){
	var tabsets			= {};
	var __CURTAB		= null;
	
	
	var getChannelURI	= function(target){
		if($(target).hasClassName('tab-channel-nav')){
			var sNav	= target;
		}else{
			var sNav = $(target).down('ol.tab-channel-nav');
			//if(typeof sNav == 'undefined'){sNav = null; console.warn('gobbledegook!')};
		}
		var tWidgetId	= null;
		//console.warn('getChannelURI: sNav == null? '+(sNav == null)+' typeof sNav = '+(typeof sNav)+'.');
		if(typeof sNav !== 'undefined'){ 
			sNav	= (typeof sNav.length === 'undefined')?sNav:$(sNav[0]);
			sNav.select('li').each(function(ele,inc){
				if(ele.hasClassName('active') || inc==0){
					var id	= ele.id;
					tWidgetId	= id.replace('tab-channel-','');
				}
			}.bind());
		}
		return tWidgetId;
	}
	var onVideLinkActivated=function(event){
		
	}
	var onTabClicked=function(event){
		var target	= event.memo;
		console.log('onTabClicked: '+target+'.');
		/* get subnav elements if present */
		var sNav		= $($(target).select('ul.tab-channel-nav'));
		var tWidgetId	= tabsets['main'].tabs[target].tab.readAttribute('data-tabid');
		document.fire('tab:content:destroy',tWidgetId);
		try{
			var channeluri	= getChannelURI(target); if(channeluri !== null){tWidgetId = channeluri;}
			var tabchannelpath	= '/widget/gc/tabs/'+ tWidgetId +'?gameId='+ nfl.global.gc.game.id;
			if(window.location.hashparams.get()){if(typeof window.location.hashparams.get().get('contentId') !== 'undefined' && window.location.hashparams.get().get('contentId') !== null){tabchannelpath += '&contentId='+window.location.hashparams.get().get('contentId');}}
			document.fire("tab:content:loading",tWidgetId);
			var tabContentRequest	= new Ajax.Updater((target+'-channel-content'), tabchannelpath, {
				method: 'get', 
				evalScripts: true,
				sanitizeJSON: false,
				onSuccess: function(){
					//$('dt-loading-panel-wrapper').hide();
					//document.fire("tab:content:loaded",{'path':tWidgetId,'id':(target+'-channel-content')});
					//document.fire("draft:tracker:data:updated",{empty:true});
				}.bind(this)
			});
		}catch(e){throw(e)}
	};
	var onChannelClicked=function(ele){
		console.log('secondary nav clicked. '+ele.identify()+'.');
		var tWidgetId			= getChannelURI($(ele.identify()));
		console.info("ele.identify() = " + ele.identify() + ", tWidgetId = " + tWidgetId);
		var tabchannelpath		= '/widget/gc/tabs/'+ tWidgetId +'?gameId='+ nfl.global.gc.game.id;
		if(window.location.hashparams.get()){if(typeof window.location.hashparams.get().get('contentId') !== 'undefined' && window.location.hashparams.get().get('contentId') !== null){tabchannelpath += '&contentId='+window.location.hashparams.get().get('contentId');}}
		var tabChannelContent	= $(ele.up('.gc-tabs-tab').select('.gc-tabs-tab-content')[0]).identify();
		console.info('load '+tabchannelpath+' into '+tabChannelContent);
		document.fire('tab:content:destroy',tWidgetId);
		document.fire("tab:content:loading",tWidgetId);
		var tabContentRequest	= new Ajax.Updater(tabChannelContent, tabchannelpath, {
			method: 'get', 
			evalScripts: true,
			sanitizeJSON: false,
			onSuccess: function(){
				//$('dt-loading-panel-wrapper').hide();
				//document.fire("tab:content:loaded",{'path':tWidgetId,'id':tabChannelContent});
				//document.fire("draft:tracker:data:updated",{empty:true});
			}.bind(this)
		});
	};
	var onContentLoading	= function(event){
		//console.info('nfl.GameCenter.Tabs.onContentLoaded '+ Object.toJSON(event.memo));
		if(typeof event !== 'undefined'){
			if(typeof event.memo !== 'undefined'){
				__CURTAB	= event.memo.toLowerCase();
			}
		}
	}
	var onCenterPieceClick = function(event){
		if(typeof event !== 'undefined'){
			if(typeof event.memo !== 'undefined'){
				var hash = event.memo;
				location.hash = hash;
				/* refocus */
				Effect.ScrollTo('tab');
			}
		}
	}
	var onContentLoaded = function(event){
		console.info('tab:content:loaded fired. performing post tab load global functions..')
		//nfl.GameCenter.Utility.updateTeamColors.delay(.5, event.memo.id, nfl.GameCenter.Utility.data().away.abbr, nfl.GameCenter.Utility.data().home.abbr);
		/*
		if(typeof nfl.GameCenter.Utility.data() !== 'undefined'){
			nfl.GameCenter.Utility.updateTeamColors(event.memo.id, nfl.GameCenter.Utility.data().home.abbr, nfl.GameCenter.Utility.data().away.abbr);
		}else{
			nfl.GameCenter.Utility.updateTeamColors(event.memo.id, nfl.global.gc.extdata.teams.home.abbr, nfl.global.gc.extdata.teams.away.abbr);
		}
		*/
	}
	document.observe("nfl:gamecenter:cp:click",onCenterPieceClick);
	document.observe("tab:content:loading",onContentLoading);
	//document.observe("tab:content:loaded",onContentLoaded);
	
	return{
		initialize: function(options){
			tabsets['main']	= new nfl.ui.tabset('tab',options);
			nfl.global.gc.subnavs	= {};
			
			//document.observe('tab:tabclicked',onTabClicked);
			document.observe('tab:tabchange',onTabClicked);
			
			/* initialize sub navigation */
			for(var key in tabsets['main'].tabs){
				//{tab:li,element:$(target)};
				console.log('initializing secondary navs. ['+key+']');
				tabsets['main'].tabs[key].element.select('.tab-channel-nav').each(function(ele){
					tabsets[ele.identify()] = new nfl.ui.tabset(ele.identify(),{'keepstate':true, 'tabonly': true, 'defaultindex': -1, 'removetabcontent':true, 'replaceTabContent':'<center><div>&nbsp;</div></center>'});
					/* now that these are valid tab sets, tie click events to them */
					document.observe(ele.identify()+':tabclicked',function(){onChannelClicked(ele)}.bind(this));
				}.bind(this));
			};
			/* bindings in place, fire off initial tab change to load everything up
			 * meant more for cases where a deep link param is non-hash and a hash param is also preset and must override */
			var loadedNewTab	= false
			if($(tabsets['main'].options.tabSet)){
				var activeTab	= $(tabsets['main'].tabList).down('.active');
				var hparams	= window.location.hashparams.get();
				if(activeTab && hparams){
					if(hparams.get('tab')){
						if(hparams.get('tab') !== nfl.global.gc.tabs.defaultName){
							console.warn('hash tab param does not match up with default tab that was loaded. load new one');
							loadedNewTab = true;
							var target	= activeTab.readAttribute('target');
							document.fire('tab:tabchange',target);
						}
					}
				}
				if(!loadedNewTab){
					/* we call out to omniture for an initial ping */
					try{
						nfl.GameCenter.Analytics.ping(activeTab.readAttribute('data-tabid'));
					}catch(e){ console.warn('could not make initial omniture ping. '+e.message); }
				}
			}
		},
		getTabId: function(){
			if(__CURTAB === null){
				/* try and set it */
				var activeTab	= $(tabsets['main'].tabList).down('.active');
				var target		= activeTab.readAttribute('target');
				var tWidgetId	= tabsets['main'].tabs[target].tab.readAttribute('data-tabid');
				var channeluri	= getChannelURI(target); if(channeluri !== null){tWidgetId = channeluri;}
				__CURTAB		= tWidgetId;
			}
			//console.log('getTabId: __CURTAB = '+__CURTAB);
			if(__CURTAB === null){
				return null
			}
			return __CURTAB
		},
		loadChannel: function(tWidgetId, tabChannelContent){
			console.info("tWidgetId = " + tWidgetId + ", tabChannelContent = " + tabChannelContent);
			var tabchannelpath		= '/widget/gc/tabs/'+ tWidgetId +'?gameId='+ nfl.global.gc.game.id;
			if(window.location.hashparams.get()){if(typeof window.location.hashparams.get().get('contentId') !== 'undefined' && window.location.hashparams.get().get('contentId') !== null){tabchannelpath += '&contentId='+window.location.hashparams.get().get('contentId');}}
			document.fire('tab:content:destroy',tWidgetId);
			document.fire("tab:content:loading",tWidgetId);
			var tabContentRequest	= new Ajax.Updater(tabChannelContent, tabchannelpath, {
				method: 'get', 
				evalScripts: true,
				sanitizeJSON: false,
				onSuccess: function(){
					//$('dt-loading-panel-wrapper').hide();
					document.fire("tab:content:loaded",{path:tabchannelpath,id:tabChannelContent});
					//document.fire("draft:tracker:data:updated",{empty:true});
				}.bind(this)
			});
		}
	}
}();

nfl.GameCenter.DriveChart	= function(){
	var OPTIONS	= {};
	var __SO		= false;
	var __SWFOPTS	= false;
	var __DCSWF		= false;
	var __DCSWFID	= false;
	var __DCSWFDATA	= false;
	var __DCSWFISREADY = false;
	var __DEFAULTVIEW = 'TOP';
	
	
	var onDataChanged = function(event){
		var inc = 1;
		var isFirstLoad	= false;
		if(!__DCSWFDATA){ isFirstLoad = true; __DCSWFDATA = {};}
		__DCSWFDATA			= event.memo;
				
		if(__DCSWFISREADY){
			if(isFirstLoad){
				console.info('publishing data to drive chart',__DCSWFDATA.gtd);
				__DCSWF.loadJsonFromJS(Object.toJSON(__DCSWFDATA.gtd));
			}else{
				//console.info('publishing data to drive chart',__DCSWFDATA.inc);
				__DCSWF.reloadJsonFromJS(Object.toJSON(__DCSWFDATA.inc));
				//__DCSWF.loadJsonFromJS(Object.toJSON(__DCSWFDATA.gtd));
			}
		}else{
			try{ document.observe('gamecenter:drivechart:initialized',loadWhenReady); }catch(e){}
		};
		//console.log('nfl.GameCenter.DriveChart.onDataChanged: '+Object.toJSON(__DCSWFDATA.inc));
	}
	var loadWhenReady = function(event){
		//alert('BLIM!');
		try{
			if(__DCSWF && __DCSWFDATA){
				console.log("publishing data to drive chart",__DCSWFDATA.gtd);
				__DCSWF.loadJsonFromJS(Object.toJSON(__DCSWFDATA.gtd));
				//alert('loaded json');
			}
		}catch(e){
			console.warn('DRIVE CHART LOAD ERROR: '+e);
		}
		//alert('BLAM!');
	}
	var onConfigUpdated = function(event){
		this.config				= event.memo;
		console.log('nfl.GameCenter.DriveChart.onConfigUpdated: '+Object.toJSON(this.config));
	}
	var loadAdWhenReady = function(event){
		console.info('nfl.GameCenter.DriveChart.loadAdWhenReady: ');
		var dims	= false;
		try{
			dims	= nfl.GameCenter.Ads.getRightRailAdSize();
		}catch(e){}
		if(!dims){
			/* do this again, but wait till the dom loads */
			if(!document.loaded){
				document.observe('dom:loaded',loadAdWhenReady);
				return
			}
		}
		onAdEvent({memo:['onload']});
	}
	var onAdEvent = function(event){
		console.info('nfl.GameCenter.DriveChart.onAdEvent: '+event.memo);
		if(!__DCSWFISREADY){
			/* we'll do this after the swf loads */
			document.observe('gamecenter:drivechart:ads:ready',loadAdWhenReady);
			return
		}
		
		/* this calls the ad in the flash */
		if(__DCSWF){
			var adEvt			= event.memo[0];
			var dims 			= nfl.GameCenter.Ads.getRightRailAdSize();
			var CompanionAdSize	= (dims.width+'x'+dims.height);
			var DartAdPath		= (nfl.global.gc.ads.dc[adEvt]);
			//console.log('__DCSWF.breakInAction('+DartAdPath+','+CompanionAdSize+')');
			try{
				__DCSWF.breakInAction(DartAdPath,CompanionAdSize);
			}catch(e){ console.warn('DRIVE CHART BIA AD ERROR: '+e); }
		}
	}
	var onDCInitialize = function(){
		__DCSWFISREADY = true;
		console.info('DCSWF is ready..');
	}
	var checkReady = function(){
		var test	= false;
		if(__DCSWF){
			try{
				test = __DCSWF.ready();
			}catch(e){ /*console.warn('__SO NOT READY:'+e);*/}
		}
		//console.log('nfl.GameCenter.DriveChart.checkReady: '+test+' '+(typeof test));
		if(test == true){ document.fire('gamecenter:drivechart:initialized');}else{ (checkReady.bind(this)).defer();}
	}
	var toggle	= function(event){
		try{
			var c	= $(__SWFOPTS.containerid);
			var cp	= c.up();
			var dc	= $(__DCSWFID);
			
			if(c.style.height == (__SWFOPTS.mh+'px')){
				c.setStyle({'height':__SWFOPTS.h+'px','overflow':'default'});
				cp.setStyle({'height':(parseInt(__SWFOPTS.h) - 25)+'px'});
				dc.writeAttribute('height',__SWFOPTS.h+'px');
				return
			}
			c.setStyle({'height':__SWFOPTS.mh+'px','overflow':'hidden'});
			cp.setStyle({'height':__SWFOPTS.mh+'px'});
			dc.writeAttribute('height',__SWFOPTS.mh+'px');
		}catch(e){}
	}
	/*
	 * this is the new toggle function, need support from flash dev
	 * he needs to collapse from within swf, and move tabs up to score board
	 * then call the event attached to toggle function.
	 *
	var toggle	= function(event){
		var c	= $(__SWFOPTS.containerid);
		var cp	= c.up();
		var dc	= $(__DCSWFID);
		
		if(c.style.height == (__SWFOPTS.mh+'px')){
			cp.setStyle({'height':__SWFOPTS.h+'px'});
			c.setStyle({'height':(parseInt(__SWFOPTS.h)+25)+'px'});
			dc.writeAttribute('height',(parseInt(__SWFOPTS.h)+25)+'px');
			return
		}
		cp.setStyle({'height':__SWFOPTS.mh+'px'});
		c.setStyle({'height':(parseInt(__SWFOPTS.mh)+25)+'px'});
		dc.writeAttribute('height',(parseInt(__SWFOPTS.mh)+25)+'px');
	}
	*/
	var write	= function(options){
		/* create swf */
		if(!__SO){
			console.log('nfl.GameCenter.DriveChart.write: creating __SO object');
			__SWFOPTS = options;
			var o = __SWFOPTS;
			__DCSWFID = o.containerid.replace(/-/g, '') + 'swf';
			__SO    = new SWFObject(nfl.global.flashpath + "/flash/gamecenter/drive-chart.swf", __DCSWFID, o.w, o.h, "10", "#FFFFFF", true);
			__SO.useExpressInstall(nfl.global.flashpath + "/flash/expressinstall.swf");
			__SO.addParam('allowScriptAccess', 'always');
			__SO.addParam('allowFullScreen', 'true');
			__SO.addParam('wmode', 'transparent');
			__SO.addVariable('uniqid', __DCSWFID);
			for(var fvar in o.vars){
				__SO.addVariable(fvar, o.vars[fvar]);
			}
			this.so = __SO;	
		}
		if(typeof __SWFOPTS !== 'undefined'){
			if(!$(__SWFOPTS.containerid)){
				console.log('nfl.GameCenter.DriveChart.write: cannot write, container does not exist yet.');
				(write.bind(this)).defer();
				return
			}
		}
		console.log('nfl.GameCenter.DriveChart.write: writing SWF to container.');
		setTimeout(function() { __SO.write(__SWFOPTS.containerid); __DCSWF = $(__DCSWFID); }, 50);
	}
	/* bind event listeners */
	document.observe('gamecenter:data:changed',onDataChanged);
	document.observe('gamecenter:config:updated',onConfigUpdated);
	document.observe('gamecenter:data:adevent',onAdEvent);
	document.observe('gamecenter:drivechart:initialized',onDCInitialize);
	document.observe('dc:toggle',toggle);

	return {
		initialize: function(options){
			this.ready			= false;
			this.config			= {};
			this.so				= false;
			this.options		= {w:'985',h:'370',mh:'92',containerid:'dc'};
			this.options.vars	= {
					'state':nfl.global.gc.game.state,
					'date':nfl.global.gc.game.date,
					'day':nfl.global.gc.game.day,
					'time':nfl.global.gc.game.time,
					'wk':nfl.global.gc.game.week,
					'gamebook':nfl.global.gc.game.gamebook,
					'cp':nfl.global.gc.game.cp,
					'defaultView':__DEFAULTVIEW
			}

			/* initialize drivechart swf */
			checkReady();
			write(this.options);
		},
		getSWF: function(){
			if(__DCSWFISREADY){ return __DCSWF; }; return null;
		},
		toggle: function(){
			toggle();
		},
		destroy: function(){
			document.stopObserving('gamecenter:data:changed',onDataChanged);
			document.stopObserving('gamecenter:config:updated',onConfigUpdated);
			document.stopObserving('gamecenter:data:adevent',onAdEvent);
			document.stopObserving('gamecenter:drivechart:initialized',loadWhenReady); //NOT YET IMPLEMENTED!
			$(this.options.containerid).update();
		}
	}
};

/**
 * nfl.GameCenter.PlayerCards
 * @namespace contains functionally gamecenter uses to display player cards when a player name is clicked.
 */
nfl.GameCenter.PlayerCards	= function(){
	var __TEMPL		= "";
	var __CURRID	= false;
	__TEMPL += "<div class=\"pc-handle\" onclick=\"nfl.GameCenter.PlayerCards.close()\"></div>";
	__TEMPL += "<ul class=\"pc-head\"><li class=\"first-child\"><a href=\"/players/profile?id=#{id}\">#{fn} #{ln}</a></li><li>##{uniformNumber}</li><li class=\"last-child\">#{pos}</li></ul>";
	__TEMPL += "<h2><a href=\"#{teamlink}\">#{teamname}</a></h2>";
	__TEMPL += "<div class=\"pc-stats\">";
	__TEMPL += "<a href=\"/players/profile?id=#{id}\"><img class=\"pc-headshot\" src=\"#{headshot}\" onerror=\"console.warn('could not find headshot for: '+this.src); this.src = '"+nfl.global.imagepath+"/img/sr_pic0.gif';\" align=\"left\"/></a>";
	__TEMPL += "Years: #{exp} | Age: #{age}<br/>";
	__TEMPL += "Born: #{bdate}<br/>";
	__TEMPL += "Hometown: #{hometown}<br/>";
	__TEMPL += "Height: #{ht}<br/>";
	__TEMPL += "Weight: #{wt}<br/>";
	__TEMPL += "College: #{college}<br/>";
	__TEMPL += "</div>";
	__TEMPL += "<ul class=\"pc-footer\">";
	__TEMPL += "<li><a href=\"/players/#{urlname}/profile?id=#{esbid}\">Stats</a></li>";
	__TEMPL += "<li><a href=\"/search/?query=#{fn}+#{ln}&type=article\">News</a></li>";
	__TEMPL += "<li><a href=\"/search/?query=#{fn}+#{ln}&type=video\">Videos</a></li>";
	__TEMPL += "<li class=\"last-child\"><a href=\"/search/?query=#{fn}+#{ln}&type=image\">Photos</a></li>";
	__TEMPL += "</ul>";
	__TEMPL = new Template(__TEMPL);
	
	var show = function(event){
		/* this version is specific to game center
		 * instead of calling an external widget
		 * we pull the player info from data */
		var ele			= event.element();
		var playerid	= ele.getAttribute('data-id');
		var playerrec	= false;
		console.log("nfl.GameCenter.PlayerCards.show: "+playerid);		
		if(typeof nfl.global.gc.engine !== 'undefined'){
			if(typeof nfl.global.gc.engine.data[nfl.global.gc.engine.gameid] !== 'undefined'){
				playerrec	= nfl.GameCenter.Utility.getPlayerFromKey(playerid);
			}
		}
		if(!playerrec){
			playerrec	= nfl.GameCenter.Utility.getPlayerFromKey(playerid,nfl.global.gc.extdata.players); // try again, fall back to extdata
		}
		//console.log("nfl.GameCenter.PlayerCards.show: playerrec = "+playerrec,nfl.global.gc.engine.data);
		if(playerrec){
			$('player-card-content').update(updatecard(playerrec));/* found player, construct card fill */
			var srcoffset	= ele.cumulativeOffset();
			$('player-card').setStyle({"top":(srcoffset.top - ($('player-card').getHeight()/2) + 15)+'px',"left":(srcoffset.left+ele.getWidth()+5)+'px'});
			//$('player-card').clonePosition(ele,{});
			$('player-card').show();/* show card */
			__CURRID	= playerrec.id;
			return true;
		}
		return false;
	}
	var onTabChanged = function(event){
		hide();
	}
	var onTabLoaded = function(event){
		if(nfl.global.gc.engine.gamestate !== 'ACTIVE'){
			/* do fancy stuff */
		}
	}
	var updatecard = function(player){
		var headshot	= nfl.global.ecmimagepath +"public/image/getty/headshot/";
		if(player.esbid != null && player.esbid.length > 3){
			var esbIdArr	= player.esbid.split("");
			headshot	+= esbIdArr[0] + "/" + esbIdArr[1] + "/";
			headshot	+= ((Object.isString(esbIdArr[2]))?(esbIdArr[2]+"/"):"0/");
			headshot	+= player.esbid +".jpg"
		}
		player.headshot	= headshot;
		player.urlname	= (player.fn+player.ln).toLowerCase();
		if(typeof nfl.global.gc.game.teams[player.team] !== 'undefined'){
			player.teamname	= nfl.global.gc.game.teams[player.team].fullname;
			player.teamlink	= nfl.global.gc.game.teams[player.team].link;
		}else{
			//console.warn('WTH? nfl.global.gc.game.teams['+player.team+'] is undefined.'+Object.toJSON(nfl.global.gc.game.teams))
		}
		return __TEMPL.evaluate(player);
		//return "<h1>player card html goes here</h1><br/>need player game rosters before this can be finished. any href with class=\"nfl-player-link\" and data-gsis-id=\"playerid\" will become player cards.";
	}
	var hide = function(){
		if($('player-card')){$('player-card').hide();__CURRID = false;}
	}
	var create = function(){
		if(!$('player-card')){
			// create empty div
			var pc = document.createElement('div');
			pc.id  = 'player-card';
			pc.style.position = 'absolute';
			pc.style.top		= '-400px';
			pc.style.left		= '-400px';
			pc.style.width		= '246px'; /* 281 minus padding left and right */
			pc.style.height		= '160px'; /* 170 minus padding top and bottom */
			pc.style.zIndex		= '900';
			//pc.style.backgroundColor	= '#fff';
			pc.style.display	 	= 'none';
			pc.style.paddingTop	 	= '5px';
			pc.style.paddingBottom	= '5px';
			pc.style.paddingRight	= '5px';
			pc.style.paddingLeft 	= '30px';
			
			var content		= document.createElement('div');
			content.id		= 'player-card-content';
			content.style.width		= '226px';
			content.style.height	= pc.style.height;
			pc.appendChild(content);
			
			var body = $$('body')[0];
			body.appendChild(pc);
			/* add mouse out to hide listener */
			//$('player-card-content').observe('mouseout',hide);
		}
	}
	/* initialize card holder */
	Event.observe(window,'load',function(){
		create();
		document.observe('tab:tabchanged',onTabChanged);
		document.observe('tab:contentloaded',onTabLoaded);
	});
	return {
		onBodyClick: function(event){
			//console.info('event.element().tagName = '+event.element().tagName+',  event.element().hasClassName(\'nfl-player-link\') = '+ event.element().hasClassName('nfl-player-link'));
			if(event.element().tagName == 'A' && event.element().hasClassName('nfl-player-link')){
				//this.roIds[event.element().identify()] = true;
				if($('player-card').visible() && __CURRID == event.element().getAttribute('data-id')){hide(); event.stop(event); return false;}
				var haspcard	= show(event);
				if(!haspcard){ /* de-linkify */ }
				event.stop(event);
				return false
			}
			return true
		},
		close: function(){
			hide();
		}
	}
}();

/**
 * nfl.GameCenter.Notification
 * @namespace contains functionally gamecenter uses to display notice box at top of page. 
 * @field __NOTICEID is the dom id of the notification box element.
 * @field __MESSAGEID is the dom id of the notification text element.
 */
nfl.GameCenter.Notification	= (function(){
	var __NOTICEID		= 'gc-notice';
	var __MESSAGEID		= 'gc-notice-message';
	/**
	 * getBox returns the notification box list element.
	 * @private
	 */
	var getBox	= function(){
		return $(__NOTICEID);
	}
	/**
	 * onNoticeUpdated is fired when a notice message has been recieved from the config object.
	 * @private
	 */	
	var onNoticeUpdated = function(event){
		if(typeof event.memo !== 'undefined'){
			if(Object.isString(event.memo)){
				$(__MESSAGEID).update(event.memo);
				getBox().addClassName('active');
				return
			}
			// hide it
			getBox().removeClassName('active');
			return
		}
	}
	document.observe('gamecenter:notice:updated',onNoticeUpdated);
	document.observe('dom:loaded',function(event){
		// do inititial check 
		var __notice	= false;
		try{
			if(typeof nfl.global.gc.engine !== 'undefined'){
				if(typeof nfl.global.gc.engine.config !== 'undefined'){
					if(nfl.global.gc.engine.config.notice !== ''){ __notice = nfl.global.gc.engine.config.notice; }
				}
			}
			if(!__notice){
				if(nfl.global.gc.extdata.config.notice !== ''){__notice = nfl.global.gc.extdata.config.notice;}
			}
		}catch(e){}
		if(__notice){
			document.fire('gamecenter:notice:updated',__notice);
		}
	});
})();

/**
 * nfl.GameCenter.Alert
 * @namespace contains functionally gamecenter uses to display pop-up alert tab. 
 * used primarily by score strip for in-game highlight video alerts.
 * @field __TEMPL is the template used to apply video meta data to the game alert
 * @field __CHARLIMIT is an arbitrary character limit applied to the description of a video highlight.
 */
nfl.GameCenter.Alert	= function(){
	var __BOXELE		= null;
	/** @field __TEMPL is the template used to apply video meta data to the game alert */
	var __TEMPL			= new Template('<table><tr><td rowspan="2">#{logo}&nbsp;</td><td><a class="gc-watch-highlight-link"><span>WATCH HIGHLIGHT</span></a></td></tr><tr><td>(#{headline})</td></tr></table>');
	var __hideTimer		= null;
	var __CHARLIMIT		= 95;
	var __ALERTID		= 'gc-alert';
	/**
	 * getBox returns the alert box list element.
	 * @private
	 */
	var getBox	= function(){
		return $(__ALERTID);
	}
	/**
	 * write writes html content to game alert box.
	 * @private
	 * @param content html content to write to game alert box.
	 */
	var write	= function(content){
		getBox().update(content);
	}
	/**
	 * show shows the game alert box via morph.
	 */
	var show	= function(){
		new Effect.Morph(getBox(),{style:'margin:17px 0 0 0; height: 37px;',duration: 0.5});
	}
	/**
	 * hide hides the game alert box via morph.
	 * @private
	 */
	var hide	= function(){
		new Effect.Morph(getBox(),{style:'margin:55px 0 0 0; height: 0px; ',duration: 0.5});
	}
	/**
	 * resetTimer resets the timer used to hide the alert box once it is shown.
	 * @private
	 */
	var resetTimer	= function(){
		try{
			clearTimeout(__hideTimer);
		}catch(e){}
	}
	/**
	 * onNewVideo is called when a 'gamecenter:video:added' event is fired. updates the alert tab 
	 * with video meta-data and shows the alert tab
	 * @private
	 */
	var onNewVideo	= function(event){
		if(typeof event !== 'undefined'){
			if(typeof event.memo !== 'undefined'){
				//console.log('onNewVideo: '+Object.toJSON(event.memo));
				if(typeof event.memo.gameId !== 'undefined'){
					if(event.memo.gameId !== nfl.global.gc.game.id){ return false }
				}
				// reset timer
				resetTimer();
				// unleash the awesome
				var json	= (typeof event.memo === 'string')?(eval('(' + event.memo + ')')):event.memo;
				if(typeof json.team !== 'undefined' && json.team !== null && json.team !== '' && json.team !== 'null'){
					json.logo	= '<img src="'+nfl.global.imagepath+'/img/teams/'+json.team+'/'+json.team+'_logo-20x20.gif" alt="'+json.team+'"/>';
				}
				/* clip length of description */
				if(typeof json.headline !== 'undefined'){
					if(typeof json.headline == 'string'){
						if(json.headline.length > __CHARLIMIT){
							json.headline	= json.headline.truncateAtLastWord(__CHARLIMIT);
						}
					}
				}
				var content	= __TEMPL.evaluate(json);
				getBox().onclick = function(){ document.fire('gamecenter:alert:click',{id:json.videoCMSID})};
				write(content);
				show();
				__hideTimer = setTimeout(hide.bind(this),30000);
			}
		}
	}
	/**
	 * onNotificationClick is the click event handler for the game alert tab. takes user to the watch tab and plays 
	 * video associated with the alert tab.
	 * @private
	 */
	var onNotificationClick = function(event){
		console.info('onNotificationClick:'+event.memo.id);
		var cmsid	= event.memo.id;
		if(!nfl.GameCenter.VideoPlaylist.hasTab()){
			if(!nfl.GameCenter.VideoPlaylist.hasVideos()){
				/* do not refresh the tab, do something else */
				//return false
			}
			/* refresh the page */
			nfl.global.gc.game.reloaduri	= nfl.global.gc.game.uri+'?random='+nfl.GameCenter.Utility.getCacheBustInt()+'#tab:watch/contentId:'+cmsid;
			//var url	= '#tab:watch/contentId:'+cmsid
			window.location	= nfl.global.gc.game.reloaduri;
		}
		nfl.GameCenter.VideoPlaylist.setVideoByContentId(cmsid);
		nfl.GameCenter.VideoPlaylist.play(cmsid);
	}
	document.observe('gamecenter:video:added',onNewVideo);
	document.observe('gamecenter:alert:click',onNotificationClick);
	document.observe('gamecenter:videoicon:click',onNotificationClick);
	
	document.observe('dom:loaded',function(event){
		//$('gc-notification').observe('click',onNotificationClick);
	});
	
	return {
		/**
		 * show shows the game alert tab
		 */
		show: function(){
			show();
		},
		/**
		 * hide hides the game alert tab
		 */
		hide: function(){
			hide();
		},
		/**
		 * write writes the content specified to the game alert tab
		 * @param {string} content
		 */
		write: function(content){
			write(content);
		}
	}
}();

/**
 * nfl.GameCenter.VideoPlaylist
 * @namespace contains functionally gamecenter uses to display videos for it's watch tab. 
 * @requires Prototype framework
 * @field @private __LIST is the internal list used to play videos, and also to write out the video playlist during active games.
 * @field @private __VPLAYERID is the id of the video player swf.
 * @field @private __VLISTCONTROLS is the id of the elements that contain the playlist radio controls.
 * @field @private __VLISTTABID is the id of the tab content container.
 * @field @private __VLISTID is the id of the ul list container element.
 * @field @private __VLISTMETAID is the id of the element in which to process meta data content.
 * @field @private __VLISTMETATMPL is the id of the element to use as the template for list items.
 * @field @private __DEFAULT_THUMB_IMAGE is the path to the default image to use for video thumbnails. used when video has no thumbnail.
 */
nfl.GameCenter.VideoPlaylist = function(){
	var __LIST			= {batch: 0,total: 0,videos:[]};
	var __VPLAYERID		= 'playercontentswf';
	var __VLISTCONTROLS = 'gc-watch-controls';
	var __VLISTTABID	= 'gc-tabs-tab-watch';
	var __VLISTID		= 'video-list-items';
	var __VLISTMETAID	= 'gc-video-list-head';
	var __VLISTMETATMPL	= null;
	var __CINDEX		= 0;
	var __VPSWFISREADY	= false;
	var __VPSWF			= false;
	var __VPLISACTIVE	= true;
	var __TMPLID		= 'video-list-template';
	var __TMPL			= null;
	var __PE			= null;
	var __VCMSID		= null;
	var __LISTSRCXHR	= false;
	
	var __DEFAULT_THUMB_IMAGE = nfl.global.imagepath + '/img/video/poster-frames/poster-frame-80x60.jpg';
	var IS_TIFF          = /\.tiff?$/;
    var RUN_TIME_FIND      = /^(?:00:)?((\d\d:)+\d\d):\d\d$/;
    var RUN_TIME_REPLACE   = "$1";
    var __VIDEOISPLAYING = false;
    var __WATCHTABIDS		= ['cat-pre-watch','cat-active-watch','cat-post-watch','cat-post-watch-fantasy'];
	
    var setTemplates = function(){
		if(__TMPL == null){				__TMPL			= ($(__TMPLID))?(new Template($(__TMPLID).innerHTML)):null; }
		if(__VLISTMETATMPL == null){	__VLISTMETATMPL	= ($(__VLISTMETAID))?(new Template($(__VLISTMETAID).innerHTML)):null; }
    }
    /**
     * playVideo plays a video in the watch tabs video player. if not on watch tab, changes to watch tab, 
     * then loads video once tab has loaded.
     * @param {String} videoCMSID id of video to play
     * @private
     */
	var playVideo = function(videoCMSID){
		__VCMSID		= videoCMSID;
		setIndexByContentId(__VCMSID,__CINDEX);
		if(__WATCHTABIDS.indexOf(nfl.GameCenter.Tabs.getTabId()) < 0){
			//console.info('nfl.GameCenter.VideoPlaylist.playVideo : tab id for watch not correct. '+nfl.GameCenter.Tabs.getTabId()+'. correcting');
			window.location.href = '#tab:watch/contentId:'+videoCMSID;
			return true
		}
		//console.info('nfl.GameCenter.VideoPlaylist.playVideo:'+getPlayer());
		if(getPlayer()){
			getPlayer().loadVideo(__VCMSID, true);
			highlightListRow(__VCMSID);
			__VCMSID		= null;
		}
	}
	/**
	 * getCacheBustInt
	 * @returns random integer based off time 
	 * @private
	 */
	var getCacheBustInt = function(){
		var time, d;
		d = new Date();
		d.setSeconds(Math.floor(d.getUTCSeconds() / 10) * 10);
		d.setMilliseconds(0);
		time = d.getTime();
		return time
	}
	/**
	 * getListFromHTML populates internal __LIST property with video data from html elements already in the page.
	 * used in pre/post states, which are not fed via JSON.
	 * @private
	 */
	var getListFromHTML = function(){
		__LIST.videos		= (typeof __LIST.videos === 'undefined' || __LIST.videos.size() === 0)?[]:__LIST.videos;
		/* create json list from html */
		var list		= $(__VLISTID);
		if(!list){ return false; }
		var listItems	= list.select('li');
		__LIST.videos	= [];
		listItems.each(function(li){
			var itemJSON	= li.readAttribute('data-json');
			if(itemJSON !== null){
				itemJSON		= itemJSON.replace('\n','');
				itemJSON		= eval('(' + itemJSON + ')');
				itemJSON		= videoCleaner(itemJSON);
				//console.log('got list item: '+itemJSON);
				__LIST.videos[parseInt(li.getAttribute('data-index'))]	= itemJSON;
			}
		}.bind(this));
		__LIST.total = __LIST.videos.length;
		//console.info('created video play list from list: '+Object.toJSON(__LIST));
	}
	/**
	 * setHTMLFromList writes out list contents from data in __LIST to __VLISTID element and __VLISTMETAID.
	 * @private
	 */
	setHTMLFromList = function(){
		//console.info('nfl.GameCenter.VideoPlaylist.setHTMLFromList: '+Object.toJSON(__LIST));
		/* for each video in list, print it */
		var ulContents		= '';
		var metaContents	= '';
		if(__TMPL !== null){
			for(vi=0; vi < __LIST.videos.length; vi++){
				var tmpContent	= __TMPL.evaluate(__LIST.videos[vi]);
				//console.info('nfl.GameCenter.VideoPlaylist.setHTMLFromList:'+tmpContent);
				ulContents	+= tmpContent;
			}
		}
		if(__VLISTMETATMPL !== null){
			metaContents	= __VLISTMETATMPL.evaluate(__LIST);
		}
		//console.info('updating #'+__VLISTID+' with '+ulContents+'\n\r__TMPL:'+__TMPL);
		//console.info('updating #'+__VLISTMETAID+' with '+metaContents+'\n\r__VLISTMETATMPL:'+__VLISTMETATMPL);
		try{
			$(__VLISTID).update(ulContents);
			$(__VLISTMETAID).update(metaContents);
		}catch(e){ console.warn('setHTMLFromList: FAIL '+e.message);}
	}
	/**
	 * highlightListRow higlights current video in list.
	 * @param {String} cmsid content id of video
	 * @private
	 */
	var highlightListRow = function(cmsid){
		if($(__VLISTID)){
			/* unset old actives */
			var listItems = $(__VLISTID).select('li.active');
			listItems.each(function(ele){ ele.removeClassName('active');});
			var listItem = $(__VLISTID).down('li[data-ecmid='+cmsid+']');
			if(listItem){ listItem.addClassName('active'); }
		}
	}
	/**
	 * writeList writes out list from __LIST if in active state, otherwise populates __LIST from li elements during pre/post state. 
	 * @private
	 */
	var writeList = function(){
		console.log('nfl.GameCenter.VideoPlaylist.writeList',__LIST);
		/* if in game tab */
		var list		= $(__VLISTID);
		if(!list){ console.warn('nfl.GameCenter.VideoPlaylist.writeList: no list'); return false; }
		//console.info('nfl.GameCenter.VideoPlaylist.writeList: __LISTSRCXHR:'+__LISTSRCXHR);
		setTemplates();
		
		//console.info('nfl.GameCenter.VideoPlaylist.writeList: listItems.size() = '+listItems.size());
		if(__LISTSRCXHR){
			setHTMLFromList();
		}else{
			/* we should already have a list if not using xhr, so read it into memory for updates */
			getListFromHTML();
		}
		if(__LIST.videos.size() > 0 && __CINDEX == -1 && __VCMSID === null){
			/* start playing content */
			//next();
		}
	}
	/**
	 * isBadImage returns boolean indicating whether or not a video thumbnail is a valid image.
	 * @param _url
	 * @returns {Boolean}
	 * @private
	 */
	var isBadImage = function( _url ) {
		return _url.blank() || IS_TIFF.test( _url );
	}
	/**
	 * videoCleaner cleans up meta data associated with a video and adds calculated convenience/display properties.
	 * @returns {Object} video node.
	 * @private
	 */
	var videoCleaner = function( _video ) {
		var thumb = _video.smallImage; // no options. NO SOUP FOR YOO!
		
		if ( isBadImage( thumb ) ) {
			thumb = __DEFAULT_THUMB_IMAGE;
		}
		_video.thumbnailImage = '<img src="' + thumb + '" alt="Watch: ' + _video.briefHeadline + '" />';
		if ( ( ! _video.date ) && _video.published ) {
			_video.date = new Date( _video.published ).toAPStyle( true );
		}
		_video.runTime = _video.runTime.replace( RUN_TIME_FIND, RUN_TIME_REPLACE );
		_video.captionBlurbUnEscaped = _video.captionBlurb;
		_video.captionBlurb = _video.captionBlurb.escapeHTML();
		return _video;
	}
	/**
	 * next moves the internal cursor forward and plays video. fires onStop method if at end of list.
	 * @param {Object} event
	 * @private
	 */
	var next = function(event){
		if(typeof event === 'undefined'){event = {memo:''}};
		console.info('nfl.GameCenter.VideoPlaylist.next: ['+ (__CINDEX+1) +']');
		if(typeof __LIST.videos[(__CINDEX+1)] !== 'undefined' && __VPLISACTIVE){
			/* we have a video, load it up */
			//console.log('nfl.GameCenter.VideoPlaylist.next: __LIST.videos['+(__CINDEX+1)+'] = '+ Object.toJSON(__LIST.videos[(__CINDEX+1)]));
			playVideo(__LIST.videos[(__CINDEX+1)].videoCMSID);
		}else{
			console.log('next video('+(__CINDEX+1)+') out of range. video list = '+Object.toJSON(__LIST));
			if((__CINDEX+1) > (__LIST.videos.length - 1)){
				/* we are at the end, stop */
				console.info('nfl.GameCenter.VideoPlaylist.next: end of the line. don\'t have to go home but you can\'t stay here.');
				onStop();
			}
		}
	};
	/**
	 * prev moves the internal cursor back and plays video.
	 * @private
	 */
	var prev = function(){
		console.info('nfl.GameCenter.VideoPlaylist.prev');
		if(typeof __LIST.videos[(__CINDEX - 1)] !== 'undefined' && __VPLISACTIVE){
			__CINDEX = __CINDEX - 1;
			/* we have a video, load it up */
			console.log('nfl.GameCenter.VideoPlaylist.prev: __LIST.videos['+__CINDEX+'] = '+ Object.toJSON(__LIST.videos[__CINDEX]));
			playVideo(__LIST.videos[__CINDEX].videoCMSID);
		}
	};
	/**
	 * hasList returns boolean indicating whether list element exists in the page currently.
	 * @returns {Boolean}
	 * @private
	 */
	var hasList	= function(){
		if($(__VLISTID)){ return true; }
		return false;
	}
	/* start getters and setters */
		/**
		 * getPlayer returns reference to video player swf object
		 * @private
		 */
		var getPlayer = function(){
			return __VPSWF
		}
		/**
		 * setIndexByContentId moves the internal cursor to the index matching the position of a given video.
		 * @param videoCMSID content id of video.
		 * @param index index of video. optional, saves time doing a lookup if present.
		 * @private
		 */
		var setIndexByContentId	= function(videoCMSID,index){
			if(typeof index !== undefined){
				/* check index for match first */
				if(typeof __LIST.videos[index] !== 'undefined'){
					if(__LIST.videos[index] !== null){
						if(__LIST.videos[index].videoCMSID == videoCMSID){ __CINDEX = index; return }
					}
				}
			}
			for(var i=0; i < __LIST.videos.length; i++){
				if(__LIST.videos[i].videoCMSID == videoCMSID){__CINDEX = i; return;}
			};
			//console.log('setIndexByContentId: could not find '+videoCMSID+' in '+Object.toJSON(__LIST.videos));
			return
		}
		/**
		 * hasVideo returns boolean indicating whether or not a specific video is present in the playlist.
		 * @param videoCMSID content id of video.
		 * @return {Boolean} video exists.
		 * @private
		 */
		var hasVideo = function(videoCMSID){
			for(var i=0; i < __LIST.videos.length; i++){
				if(__LIST.videos[i].videoCMSID == videoCMSID){ return true;}
			};
			return false
		}
		/**
		 * getListFromXHR creates an ajax request to retrieve __LIST contents from json file associated with game. sets list.
		 * fires gamecenter:video:playlist:data:reloaded event upon completion.
		 * @private
		 */
		var getListFromXHR = function(){
			//__LIST	= {"batch": 0, "total": 2, "videos": [{"briefHeadline": "Top 10 Unluckiest Plays", "published": "2009-04-06 17:57:00.0", "captionBlurb": "NFL Total Access counts down the Top 10 unluckiest plays of all time. ", "videoCMSID": "090007578059c14d", "runTime": "02:59", "xSmallImage": "http://origin-test.www.nfl.com/static/content/public/video/2009/090007578059c147_video_thumbnail_60_45.jpg", "smallImage": "http://origin-test.www.nfl.com/static/content/public/video/2009/090007578059c147_video_thumbnail_80_60.jpg", "videoDetailUrl": "/videos/baltimore-ravens/090007578059c14d/Top-10-Unluckiest-Plays"}, {"briefHeadline": "Tom Brady highlights ", "published": "2008-05-06 18:33:00.0", "captionBlurb": "Tom Brady sets a playoff record with a 141.4 passer rating in win vs. Jaguars throwing for 262 yds, 3 td, 0 int.", "videoCMSID": "09000757801f29c1", "runTime": "02:58", "xSmallImage": "http://origin-test.www.nfl.com/static/content/public/video/2008/09000757801f29bc_video_thumbnail_60_45.jpg", "smallImage": "http://origin-test.www.nfl.com/static/content/public/video/2008/09000757801f29bc_video_thumbnail_80_60.jpg", "videoDetailUrl": "/videos/baltimore-ravens/09000757801f29c1/Tom-Brady-highlights"}]}
			//return
			//console.info('nfl.GameCenter.VideoPlaylist.getListFromXHR called');
			if(typeof nfl.global.gc.engine === 'undefined'){
				console.warn('getListFromXHR NO nfl.global.gc.engine! PANIC.NOW!!!')
				//return
			}
			//var requesturl	= '/ajax/mule/json-videos-for-game?gameId='+nfl.global.gc.game.id+'&random=';
			var requesturl	= '/liveupdate/in-game-highlights/'+nfl.global.gc.engine.gameid+'/'+nfl.global.gc.engine.gameid+'.json?random='+getCacheBustInt();
			var request	= new Ajax.Request(requesturl,{
				method:'get',
				evalScripts:true,
				evalJSON: "force",
				onSuccess:function(XHR){
					var json	= false;
					if(typeof XHR.responseJSON != "undefined"){
						json	= XHR.responseJSON;
					}else{
						json	= eval('(' + XHR.responseText + ')');
					}
					__LIST	= json;
					for(vi=0; vi < __LIST.videos.length; vi++){
						__LIST.videos[vi] = videoCleaner(__LIST.videos[vi]);
					}
					if(__LIST.videos.length > 0){
						document.fire('gamecenter:video:playlist:data:reloaded');
					}
					//console.info('nfl.GameCenter.VideoPlaylist.getListFromXHR '+Object.toJSON(__LIST));
				}.bind(this)
			});
		}
	/* end getters and setters */
		
	/* start event handler functions */
		/**
		 * onContentClick is fired when a video thumbnail or link is clicked on in the video playlist. plays video.
		 * @handler
		 * @private
		 */
		var onContentClick = function(event){
			//console.log('onContentClick: tagname = '+ event.element().tagName +'; class = '+event.element().className);
			if(event.element().hasClassName('nfl-video-link')){
				var videoCMSID	= event.element().up('li.thumb-small').readAttribute('data-ecmid');
				var index	= parseInt(event.element().up('li.thumb-small').readAttribute('data-index'));
				
				console.info('video link clicked, load video for:'+videoCMSID);
				getPlayer().loadVideo(videoCMSID, true);
				setIndexByContentId(videoCMSID,index);
				highlightListRow(videoCMSID);
				Effect.ScrollTo('watch');
				event.stop(event);
			}
		}
		/**
		 * onNewVideo is fired when a new highlight video is published via scorestrip. 
		 * adds to __LIST and refreshes gamecenter widgets if not already present.
		 * @handler
		 * @private
		 */
		var onNewVideo	= function(event){
			if(typeof event !== 'undefined'){
				if(typeof event.memo !== 'undefined'){
					console.log('nfl.GameCenter.VideoPlaylist.onNewVideo',event.memo);
					if(typeof event.memo.gameId !== 'undefined'){
						if(event.memo.gameId !== nfl.global.gc.game.id){ return false }
						/* add this video to the internal list */
						if(event.memo.pid != 0){
							var cmsid	= ''+event.memo.videoCMSID;
							if(typeof __LIST.plays[event.memo.pid] !== 'undefined'){
								if(__LIST.plays[event.memo.pid] == cmsid){ return false }
							}
							__LIST.plays[event.memo.pid] = (''+event.memo.videoCMSID);
							// force gamecenter engine to republish current data to all widgets
							console.log('forcing gamecenter widgets to repaint due to a new video associated to a play');
							//document.fire('gamecenter:engine:publish');
						}
					}
				}
			}
		}
		/**
		 * onAdEvent pauses video if one is playing during a break in action ad event.
		 * @private
		 */
		var onAdEvent = function(event){
			console.info('nfl.GameCenter.VideoPlaylist.onAdEvent');
			if(getPlayer()){
				/* pause video player if playing */
				try{
					getPlayer().pauseVideo();
					/* listen for events */
					document.observe('videoplayer:adComplete',onAdEventComplete);
				}catch(e){}
			}
		}
		/**
		 * onAdEventComplete starts video after a break in action event is over with.
		 * @private
		 */
		var onAdEventComplete = function(event){
			//console.info('nfl.GameCenter.VideoPlaylist.onAdEventComplete:',event.memo);
			if(getPlayer()){
				try{
					/* un-pause video player */
					getPlayer().playVideo();
				}catch(e){}
			}
			/* remove event listener to avoid catching normal ads */
			document.stopObserving('videoplayer:adComplete',onAdEventComplete);
		}
		/**
		 * onDataLoaded populates templates and writes out list.
		 * @private
		 */
		var onDataLoaded = function(event){
			/* stricly a redraw */
			//console.log('onDataLoaded: '+$(__TMPLID)+' '+$(__VLISTMETAID));
			setTemplates();
			writeList();
		}
		/**
		 * onContentLoaded is fired when a watch tab has been loaded. writes out video elements if active, 
		 * populates internal list if pre/post.
		 * @private
		 */
		var onContentLoaded = function(event){
			__VPSWFISREADY	= false;
			if(typeof event !== 'undefined'){
				if(typeof event.memo !== 'undefined'){
					if(typeof event.memo.cmsid !== 'undefined'){
						__VCMSID		= event.memo.cmsid;
						/* clear this from the hash without screwing everything up */
						window.location.hashparams.remove('contentId');
					}
				}
			}
			setTemplates();
			//console.info('nfl.GameCenter.VideoPlaylist.onContentLoaded: __LISTSRCXHR? '+__LISTSRCXHR);
			if(__LISTSRCXHR){
				/* we always have to do this for xhr generated content */
				getListFromXHR();
			}else{
				/* we only have to do this if it doesn't have any content, who knows */
				var list		= $(__VLISTID);
				if(!list){ return false; }
				getListFromHTML(); //always keep the internal list up to date
				/*
				 * this is old redundancy logic, probably not needed now
				 * purpose is to populate the list elements from the list
				 * in memory if the html list has zero items.
				 * 
				var listCount	= $('gc-video-list-head');
				if(listCount){
					var cStr	= listCount.innerHTML.strip().toLowerCase();
					cStr		= cStr.substring(0,(cStr.indexOf(' videos')));
					if(parseInt(cStr) !== __LIST.videos.length){
						// something is weird here
					}
				}
				var listItems	= list.select('li');
				if(typeof listItems !== 'undefined'){
				//	if(listItems.size() === 0){setHTMLFromList();}
					return;
				}
				//setHTMLFromList();
				*/
				return;
			}
		}
		/**
		 * onVideoPlayerInitialized fires once the video player has been initialized.
		 * writes out list elements. plays specific video if one has been set prior to load, defaults to first item in list.
		 * @private
		 */
		var onVideoPlayerInitialized = function(event){
			console.info('video player initialized');
			if($(__VPLAYERID)){
				__VPSWF			= $(__VPLAYERID);
				__VPSWFISREADY	= true;
				writeList();
				
				__VCMSID = (__VCMSID === null && typeof __LIST.videos[__CINDEX] !== 'undefined')?__LIST.videos[__CINDEX].videoCMSID:__VCMSID;
				//getPlayer().loadVideo(__VCMSID, true);
				if(typeof nfl.global.gc.vp.config.contentId !== 'undefined'){
					if(nfl.global.gc.vp.config.contentId !== null){ return false; } /* we do this to stop the video player init event from double playing the video */
				}
				console.log('onVideoPlayerInitialized: playing '+__VCMSID);
				playVideo(__VCMSID);
				//try{ __VPSWF.loadVideo(__VCMSID, true); }catch(e){ console.log('onVideoPlayerInitialized: error: '+e.message);}
			}
		}
		/**
		 * onPlayListModeChange fires when a playlist control has been clicked. turns continuous play on/off, 
		 * and changes appropriate classes.
		 * @private
		 */
		var onPlayListModeChange = function(event){
			var playmode	= event.memo;
			var oldplaymode	= __VPLISACTIVE;
			if(typeof playmode === 'boolean'){
				__VPLISACTIVE = playmode;
				if(!oldplaymode && playmode){
					/* was turned off, now its on so advance to next item */
					if((__CINDEX+1) > (__LIST.videos.length - 1)){
						//was off, now on, and at the end, reset to first item
						__CINDEX = -1;
					}
					//console.info('onPlayListModeChange: __VIDEOISPLAYING = '+__VIDEOISPLAYING);
					if(!__VIDEOISPLAYING){
						next();
					}
				}
			}
			//console.info('__VPLISACTIVE = '+__VPLISACTIVE+' typeof playmode = '+(typeof playmode)+' playmode = '+playmode);
			if($(__VLISTCONTROLS)){
				var __inputs	= $(__VLISTCONTROLS).select('input');
				/* has controls */
				if(__VPLISACTIVE){
					$(__VLISTCONTROLS).addClassName('on');
					__inputs[0].checked = true;
					__inputs[1].checked = false;
				}else{
					$(__VLISTCONTROLS).removeClassName('on');
					__inputs[0].checked = false;
					__inputs[1].checked = true;
				}
			}
		}
		/**
		 * onTabDestroy fires when the tab has been destroyed. trys to stop video playback if it can.
		 * @private
		 */
		var onTabDestroy = function(event){
			/* stop video player just in case */
			if(getPlayer()){
				try{getPlayer().pauseVideo();}catch(e){console.warn('onTabDestroy: could not stop video playback. error: '+e.message);}
			}
			console.info('onTabDestroy: stopped video playback');
		}
		/**
		 * onEngineCreated fires when gamecenter engine has been initialized, sets up initial __LIST from XHR request.
		 * @private
		 */
		var onEngineCreated = function(event){
			if(event.memo.gamestate === "ACTIVE"){
				/* get first json */
				getListFromXHR();
				/* poll for json.. or something */
			}
		}
		/**
		 * onHashChange
		 * @private
		 */
		var onHashChange = function(event){
			//if(){}
			//window.location.href = '#tab:watch/contentId:'+videoCMSID;
		}
		/**
		 * onVideoPlay sets internal flag indicating that video is playing. fires when a video begins playing.
		 * @private
		 */
		var onVideoPlay = function(){ __VIDEOISPLAYING = true;}
		/**
		 * onVideoEnd sets internal flag indicating that video is playing. fires when a video stops playing.
		 * @private
		 */
		var onVideoEnd = function(){ __VIDEOISPLAYING = false;}
		/**
		 * onStart occurs when gamecenter:video:playlist:start is fired. starts playback.
		 * @private
		 */
		var onStart = function(){onPlayListModeChange({memo:true});}
		/**
		 * onStop occurs when gamecenter:video:playlist:stop is fired. stops playback.
		 * @private
		 */
		var onStop = function(){onPlayListModeChange({memo:false});}
	/* end event handler functions */
	/**
	 * initialize sets up event listeners when page has been loaded.
	 * @private
	 */
	var initialize = function(){
		/* bind click listeners to watch tab */
		if($('gc-tabs-tab-watch')){
			$('watch-channel-content').observe('click',onContentClick);
			document.observe('gamecenter:video:playlist:reloaded',onContentLoaded);
			document.observe('gamecenter:video:playlist:data:reloaded',onDataLoaded);
			document.observe('gamecenter:video:playlist:stop',onStop);
			document.observe('gamecenter:video:playlist:start',onStart);
			document.observe('videoplayer:playbackComplete',next);
			document.observe('videoplayer:unavailable',next);
			document.observe('videoplayer:play',onVideoPlay);
			document.observe('videoplayer:playbackComplete',onVideoEnd);
			document.observe('videoplayer:initialized',onVideoPlayerInitialized);
			document.observe('tab:content:destroy',onTabDestroy);
		}
	}
	/**
	 * destroy unbinds event listeners.
	 * @private
	 */
	var destroy = function(event){
		$('watch-channel-content').stopObserving('click',onContentClick);
		document.stopObserving('gamecenter:video:playlist:reloaded',onContentLoaded);
		document.stopObserving('gamecenter:video:playlist:data:reloaded');
		document.stopObserving('gamecenter:video:playlist:stop',onStop);
		document.stopObserving('gamecenter:video:playlist:start',onStart);
		document.stopObserving('videoplayer:playbackComplete',next);
		document.stopObserving('tab:content:destroy',onTabDestroy);
		/* stop video player just in case */
	}
	document.observe('gamecenter:engine:created',onEngineCreated);
	document.observe('dom:loaded',initialize);
	document.observe('gamecenter:data:adevent',onAdEvent);
	document.observe('gamecenter:video:added',onNewVideo);
	
	/* return public methods */
	return {
		/**
		 * onTabLoaded calls private onContentLoaded
		 * @member nfl.GameCenter.VideoPlaylist
		 */
		onTabLoaded:function(event){
			onContentLoaded();
		},
		/**
		 * getList returns __LIST.videos the videos array portion of the list json.
		 * @member nfl.GameCenter.VideoPlaylist
		 * @returns {Array} list of videos
		 */
		getList:function(){
			return __LIST.videos
		},
		/**
		 * getVideos returns __LIST.videos the videos array portion of the list json.
		 * @member nfl.GameCenter.VideoPlaylist
		 * @returns {Array} list of videos
		 */
		getVideos:function(){
			return __LIST.videos
		},
		/**
		 * getPlays returns __LIST.plays the plays portion of the list json. this json specifies the videos associated to plays.
		 * @member nfl.GameCenter.VideoPlaylist
		 * @returns {Object} list of plays associated with videos. playid:videocontentid
		 */
		getPlays:function(){
			if(__LIST){
				if(typeof __LIST.plays !== 'undefined'){ return __LIST.plays; }
			}
			return false
		},
		/**
		 * getPlay returns the video content id of a video associated to a play. returns false if the play has no associated video.
		 * @member nfl.GameCenter.VideoPlaylist
		 * @returns {String} video content id
		 */
		getPlay:function(playid){
			if(__LIST){
				if(typeof __LIST.plays !== 'undefined'){ 
					if(typeof __LIST.plays[playid] !== 'undefined'){
						return __LIST.plays[playid]
					}
				}
			}
			return false
		},
		/**
		 * next plays the next video in the playlist.
		 * @member nfl.GameCenter.VideoPlaylist
		 */
		next:function(){
			next();
		},
		/**
		 * previous plays the previous video in the playlist.
		 * @member nfl.GameCenter.VideoPlaylist
		 */
		previous:function(){
			prev();
		},
		/**
		 * setContinuous sets whether or not the playlist should continuously play.
		 * @param {Boolean} value
		 * @member nfl.GameCenter.VideoPlaylist
		 */
		setContinuous:function(value){
			__VPLISACTIVE = value;
		},
		/**
		 * isPlayingVideo returns boolean indicating whether or not a video is currently being played.
		 * @returns {Boolean}
		 * @member nfl.GameCenter.VideoPlaylist
		 */
		isPlayingVideo:function(){
			return __VIDEOISPLAYING;
		},
		/**
		 * setVideoByContentId sets the internal cursor to the index of the specified video.
		 * @param {String} value video content id.
		 * @member nfl.GameCenter.VideoPlaylist
		 */
		setVideoByContentId:function(value){
			setIndexByContentId(value);
		},
		/**
		 * play plays the specified video
		 * @param {String} value video content id.
		 * @member nfl.GameCenter.VideoPlaylist
		 */
		play:function(value){
			playVideo(value);
		},
		/**
		 * hasList returns boolean indicating whether or not the list container ul element is present.
		 * @returns {Boolean} list element is present.
		 * @member nfl.GameCenter.VideoPlaylist
		 */
		hasList:function(){
			return hasList();
		},
		/**
		 * hasTab returns boolean indicating whether or not the watch tab is present.
		 * @returns {Boolean} watch tab is present.
		 * @member nfl.GameCenter.VideoPlaylist
		 */
		hasTab: function(){
			if($(__VLISTTABID)){ return true; }
		},
		/**
		 * hasVideos returns boolean indicating whether or not there are any videos in the play list.
		 * @returns {Boolean} has videos in play list.
		 * @member nfl.GameCenter.VideoPlaylist
		 */
		hasVideos: function(){
			if(this.getVideos()){
				if(this.getVideos().length > 0){
					return true
				}
			}
			return false
		},
		/**
		 * useXHR tells the video playlist that it should populate it's videos using json associated with the game.
		 * @param {Boolean} bool use ajax to power the watch tab.
		 * @member nfl.GameCenter.VideoPlaylist
		 */
		useXHR:function(bool){
			__LISTSRCXHR = (typeof bool === 'undefined')?true:bool;
		}
	}
}();

nfl.GameCenter.ScoreSummary = Class.create(
	/** @lends nfl.GameCenter.ScoreSummary */	
	{
	/**
	 * listens for data change events from nfl.GameCenter.Engine and 
	 * updates a widget on the page with new data
	 * @constructs nfl.GameCenter.ScoreSummary
	 * @requires Prototype framework
	 * @requires nfl.GameCenter.Engine requires instance of engine publicly available as nfl.global.gc.engine
	 * @param {String} container is the container elements id attribute
	 * @param {Object} options is an object of options that pertain to this class instance 
	 * @member nfl.GameCenter
	 * @see nfl.GameCenter.Engine
	 * @return instance of this class with it's methods.
	 * @type Object
	 */
	initialize: function(container,options){
		this.container		= $(container);
		this.data			= null;
		this.gameid			= -1;
		if(typeof nfl.global.gc.engine !== 'undefined'){
			if(typeof nfl.global.gc.engine.data !== 'undefined'){
				this.data			= (Prototype.Browser.IE && Prototype.Browser.Version == 6)?Object.clone(nfl.global.gc.engine.data):nfl.global.gc.engine.data;
			}
			if(typeof nfl.global.gc.engine.gameid !== 'undefined'){
				this.gameid			= nfl.global.gc.engine.gameid;
			}
		}
		this.options		= (typeof options !== 'undefined')?options:{};
		this.maxresults		= (typeof this.options.maxresults !== 'undefined')?this.options.maxresults:false;
		this.templates		= {header:'<tr class="thd2"><td colspan="3">#{qtr}</td></tr>',emptyrow:'<tr class="tbdy1 tbdyempty"><td></td><td colspan="2"><strong>None</strong></td></tr>',row:'<tr class="tbdy1 #{trclass}"><td valign="top">#{icon}</td><td valign="top"><strong>#{type}</strong></td><td>#{desc}#{vlink}</td></tr>'};
		
		this.makeTemplates();
		this.render();

		this.onDataChanged	= this.dataChanged.bind(this);
		this.onDestroy		= this.destroy.bind(this);
		
		document.observe('gamecenter:data:changed',this.onDataChanged);
		document.observe('tab:content:destroy',this.onDestroy);
	},
	/**
	 * makeTemplates used internally to create templates from templates properties.
	 * @member nfl.GameCenter.ScoreSummary
	 */
	makeTemplates: function(){
		this.templates.header	= new Template(this.templates.header);
		this.templates.row		= new Template(this.templates.row);
	},
	/**
	 * dataChanged listens for nfl.GameCenter.Engine data events and 
	 * creates internal references, and calls render.
	 * @member nfl.GameCenter.ScoreSummary
	 * @see render
	 */
	dataChanged: function(event){
		/* re-write this function for ie6, critical memory violation fix */
		if(Prototype.Browser.IE && Prototype.Browser.Version == 6){
			this.dataChanged = function(event){
				this.gameid	= Object.clone(event.memo.gameid);
				this.data	= Object.clone(event.memo.gtd);
				this.render(this.data);
			}
			this.onDataChanged	= this.dataChanged.bind(this);
			this.gameid	= Object.clone(event.memo.gameid);
			this.data	= Object.clone(event.memo.gtd);
			this.render(this.data);
			return
		}
		this.gameid	= event.memo.gameid;
		this.data	= event.memo.gtd;
		this.render(this.data);
	},
	/**
	 * render processes data, merges it with templates, and outputs it 
	 * to the container
	 * @member nfl.GameCenter.ScoreSummary
	 * @param {Object} data reference to the data to be processed, if undefined retrieves data from internal class reference.
	 */
	render: function(data){
		if(typeof data === 'undefined'){data = this.data;}
		var __output	= null;
		if(this.container && typeof data[this.gameid] !== 'undefined'){
			if(typeof data[this.gameid].scrsummary !== 'undefined'){
				/* we have a score summary, output it */
				__output		= '';
				__outputArr		= {'1':null,'2':null,'3':null,'4':null};
				for(var key in data[this.gameid].scrsummary){
					var node	= data[this.gameid].scrsummary[key];
					var pvid	= nfl.GameCenter.VideoPlaylist.getPlay(key);
					if(typeof __outputArr[node.qtr] === 'undefined'){ __outputArr[node.qtr] = null; }
					if(__outputArr[node.qtr] === null){
						__outputArr[node.qtr]	= this.templates.header.evaluate({'qtr':this.getQtrName(node.qtr)});
						node.trclass = 'tbdyfirst';
					}
					/* add team icon */
					node.icon	= '<img src="'+nfl.global.imagepath+'/img/teams/'+node.team+'/'+node.team+'_logo-20x20.gif"/>';
					/* add vidoe link */
					node.vlink			= (!pvid)?'':'<br/><a href="#tab:watch/contentId:'+pvid+'" class="gc-watch-highlight-link"><span>WATCH HIGHLIGHT</span></a>';
					/* replace player names with links */
					nfl.GameCenter.Utility.replacePlayerNames(node);
					__outputArr[node.qtr]	+= this.templates.row.evaluate(node);
				}
				for(var __qtr in __outputArr){
					if(__outputArr[__qtr] === null){
						/* this node has no data, just give it a head/noresult */
						__outputArr[__qtr]	= this.templates.header.evaluate({'qtr':this.getQtrName(parseInt(__qtr))});
						__outputArr[__qtr]	+= this.templates.emptyrow;
					}
					/* add output to total result */
					__output	+= __outputArr[__qtr];
				}
			}
			if(__output !== null && __output !== ''){
				this.container.update(__output);
			}
		}
	},
	/**
	 * getQtrName returns human english version of quarter based on input.
	 * @member nfl.GameCenter.ScoreSummary
	 * @param {Integer} qtr the numeric quarter of the game.
	 * @returns string value of quarter. ie: 1st from 1
	 */
	getQtrName: function(qtr){
		switch(qtr){
			case 1:
				return '1st Quarter';
			case 2:
				return '2nd Quarter';
			case 3:
				return '3rd Quarter';
			case 4:
				return '4th Quarter';
			case 5:
				return 'Overtime';
		}
		return qtr;
	},
	/**
	 * destroy unbinds event listeners
	 * @member nfl.GameCenter.ScoreSummary
	 */
	destroy: function(){
		/* blue pill */
		document.stopObserving('gamecenter:data:changed',this.onDataChanged);
		document.stopObserving('tab:content:destroy',this.onDestroy);
		console.info('destroyed ScoreSummary');
	}
});
nfl.GameCenter.LastScore	= Class.create(
	/** @lends nfl.GameCenter.LastScore */	
	{
	/**
	 * is a widget engine class. It 
	 * listens for data change events from nfl.GameCenter.Engine and 
	 * updates a widget on the page with new data
	 * @constructs nfl.GameCenter.LastScore
	 * @requires Prototype framework
	 * @requires nfl.GameCenter.Engine requires instance of engine publicly available as nfl.global.gc.engine
	 * @param {String} container is the container elements id attribute
	 * @param {String} parent is the container's parent element id attribute
	 * @param {Object} options is an object of options that pertain to this class instance 
	 * 
	 * @property {Object} container dom element for the container. created from container param.
	 * @property {Object} parent dom element for the containers parent. used to hide widget when there are no results. created from awaycontainer param.
	 * @property {Object} data internal reference to data fed via engines data changed event.
	 * @property {Object} gameid the games elias id. automatically populated from engine instance.
	 * 
	 * @see nfl.GameCenter.Engine
	 * @return instance of this class with it's methods.
	 * @type Object
	 */
	initialize: function(container,parent,options){
		this.container		= $(container);
		this.parent			= $(parent);
		this.data			= null;
		this.gameid			= -1;
		if(typeof nfl.global.gc.engine !== 'undefined'){
			if(typeof nfl.global.gc.engine.data !== 'undefined'){
				this.data			= (Prototype.Browser.IE && Prototype.Browser.Version == 6)?Object.clone(nfl.global.gc.engine.data):nfl.global.gc.engine.data;
			}
			if(typeof nfl.global.gc.engine.gameid !== 'undefined'){
				this.gameid			= nfl.global.gc.engine.gameid;
			}
		}
		this.options		= (typeof options !== 'undefined')?options:{};
		this.maxresults		= (typeof this.options.maxresults !== 'undefined')?this.options.maxresults:false;
		this.templates		= {emptyrow:'<tr class="tbdy1 tbdyempty"><td></td><td colspan="2"><strong>None</strong></td></tr>',row:'<tr class="tbdy1#{rowClass}"><td valign="top">#{icon}</td><td valign="top"><strong>#{type}</strong></td><td>#{desc}#{vlink}</td></tr>'};
		this.inc			= {};
		this.__oldscore		= {};
		
		this.makeTemplates();
		this.render();
		
		this.onDataChanged	= this.dataChanged.bind(this);
		this.onDestroy		= this.destroy.bind(this);
		
		document.observe('gamecenter:data:changed',this.onDataChanged);
		document.observe('tab:content:destroy',this.onDestroy);
	},
	/**
	 * makeTemplates used internally to create templates from templates properties.
	 * @member nfl.GameCenter.LastScore
	 */
	makeTemplates: function(){
		this.templates.row		= new Template(this.templates.row);
	},
	/**
	 * dataChanged listens for nfl.GameCenter.Engine data events and 
	 * creates internal references, and calls render.
	 * @member nfl.GameCenter.LastScore
	 * @see render
	 */
	dataChanged: function(event){
		/* re-write this function for ie6, critical memory violation fix */
		if(Prototype.Browser.IE && Prototype.Browser.Version == 6){
			this.dataChanged = function(event){
				this.gameid	= Object.clone(event.memo.gameid);
				this.data	= Object.clone(event.memo.gtd);
				this.inc	= Object.clone(event.memo.inc);
				this.render(this.data);
			}
			this.onDataChanged	= this.dataChanged.bind(this);
			this.gameid	= Object.clone(event.memo.gameid);
			this.data	= Object.clone(event.memo.gtd);
			this.inc	= Object.clone(event.memo.inc);
			this.render(this.data);
			return
		}
		this.gameid	= event.memo.gameid;
		this.data	= event.memo.gtd;
		this.inc	= event.memo.inc;
		this.render(this.data);
	},
	/**
	 * isUpdated checks to see if a specific play's data matches what we had 
	 * in memory the last time the data was updated. 
	 * @param {String} pid play id to check
	 * @member nfl.GameCenter.LastScore
	 */
	isUpdated:function(pid){
		var __lastScore		= this.data[this.gameid].scrsummary[pid];
		//var __dbg	= 'looking for '+cd.plays[pid].down+'-'+cd.plays[pid].ydstogo+'-'+cd.plays[pid].yrdln+'\n\r';
		if(typeof this.__oldscore !== 'undefined'){
			if(typeof this.__oldscore.desc === 'undefined'){ return false; }
			if(this.__oldscore.desc === null){ return false; }
			//__dbg		+= 'exists ? '+(typeof old_cd.plays[pid])+' '+ Object.toJSON(old_cd.plays[pid]) +'\n';
			if(this.__oldscore.desc !== __lastScore.desc){
				return true
			}
		}
		//console.log(__dbg);
		return false
	},
	/**
	 * render processes data, merges it with templates, and outputs it 
	 * to the container
	 * @member nfl.GameCenter.LastScore
	 * @param {Object} data reference to the data to be processed, if undefined retrieves data from internal class reference.
	 */
	render: function(data){
		if(typeof data === 'undefined'){data = this.data;}
		var __output	= null;
		var __lastScore	= null;
		var __lskey		= null;
		if(this.container && typeof data[this.gameid] !== 'undefined'){
			if(typeof data[this.gameid].scrsummary !== 'undefined'){
				/* we have a score summary, output it */
				__output	= '';
				__qtr		= 0;
				for(var key in data[this.gameid].scrsummary){
					/* we're only outputting the last one */
					__lastScore	= data[this.gameid].scrsummary[key];
					__lskey		= key;
				}
				if(__lastScore !== null){
					if(this.parent.getStyle('display') === 'none'){this.parent.show();}
					var node	= __lastScore;
					var pvid	= nfl.GameCenter.VideoPlaylist.getPlay(__lskey);
					// add team icon
					node.icon	= '<img src="'+nfl.global.imagepath+'/img/teams/'+node.team+'/'+node.team+'_logo-20x20.gif"/>';
					/* replace player names with links */
					nfl.GameCenter.Utility.replacePlayerNames(node);
					node.rowClass	= (this.isUpdated(__lskey))?' updated':'';
					node.vlink		= (!pvid)?'':'<br/><a href="#tab:watch/contentId:'+pvid+'" class="gc-watch-highlight-link"><span>WATCH HIGHLIGHT</span></a>';
					__output		= this.templates.row.evaluate(node);
					this.container.update(__output);
					this.__oldscore	= __lastScore;
				}else{
					/* hide this widgets parent */
					this.parent.hide();
				}
			}
		}
		if((__output === null || __output === '') && this.container){
			/* it's pretty safe to assume we don't have any scoring plays yet. display something else */
			this.container.update(this.templates.emptyrow);
		}
		this.onRender();
	},
	/**
	 * onRender is processed after a successful call to the render method. It 
	 * selects the updated rows by class, calls the highlight method on them, 
	 * and sets a timer event on which to unhighlight the rows.
	 * @member nfl.GameCenter.LastScore
	 */
	onRender: function(){
		var rows	= this.container.select('tr.updated');
		this.highlight(rows);
		setTimeout(this.unhighlight.bind(this,rows),2000);
	},
	/**
	 * highlight ads the class name 'highlighted' to the rows passed to it.
	 * @param {Object[]} rows to add the class to
	 * @member nfl.GameCenter.LastScore
	 */
	highlight: function(rows){
		rows.each(function(ele){
			ele.addClassName('highlighted');
		});
	},
	/**
	 * unhighlight calls the morph method on the elements passed to it. slowly fading the elements 
	 * background-color property to white, and then removing the class 'highlighted' from the element.
	 * @param {Object[]} rows to fade and remove class from.
	 * @member nfl.GameCenter.LastScore
	 */
	unhighlight: function(rows){
		rows.each(function(ele){
			ele.morph('background-color:#fff',{duration:3.0,afterFinish:function(){
				ele.removeClassName('highlighted');
				ele.writeAttribute('style','');
			}});
		});
	},
	/**
	 * destroy unbinds event listeners
	 * @member nfl.GameCenter.LastScore
	 */
	destroy: function(){
		/* blue pill */
		document.stopObserving('gamecenter:data:changed',this.onDataChanged);
		document.stopObserving('tab:content:destroy',this.onDestroy);
		console.info('destroyed Last Score');
	}
});
nfl.GameCenter.CurrentDrive	= Class.create(
	/** @lends nfl.GameCenter.CurrentDrive */	
	{
	/**
	 * nfl.GameCenter.CurrentDrive is a widget engine class. It 
	 * listens for data change events from nfl.GameCenter.Engine and 
	 * updates a widget on the page with new data
	 * @constructs nfl.GameCenter.CurrentDrive
	 * @param {String} container is the container elements id attribute
	 * @param {Object} options is an object of options that pertain to this class instance.
	 * @property {Object} container dom element for the container. created from container param.
	 * @property {Object} data internal reference to data fed via engines data changed event.
	 * @property {Object} gameid the games elias id. automatically populated from engine instance.
	 *             
	 * @member nfl.GameCenter.CurrentDrive
	 * @requires Prototype framework
	 * @requires nfl.GameCenter.Engine requires instance of engine publicly available as nfl.global.gc.engine
	 * @see nfl.GameCenter.Engine
	 * @return instance of this class with it's methods.
	 * @type Object
	 */
	initialize: function(container,options){
		this.container		= $(container);
		this.data			= null;
		this.gameid			= -1;
		if(typeof nfl.global.gc.engine !== 'undefined'){
			if(typeof nfl.global.gc.engine.data !== 'undefined'){
				this.data			= (Prototype.Browser.IE && Prototype.Browser.Version == 6)?Object.clone(nfl.global.gc.engine.data):nfl.global.gc.engine.data;
			}
			if(typeof nfl.global.gc.engine.gameid !== 'undefined'){
				this.gameid			= nfl.global.gc.engine.gameid;
			}
		}
		this.inc			= {};
		this.__oldcd		= {plays:null}
		this.options		= (typeof options !== 'undefined')?options:{};
		this.templates		= {
				emptyrow:'<tr class="thd2 tbdyempty"><td colspan="3"><strong>#{msg}</strong></td></tr>',
				row:'<tr class="tbdy1#{rowClass}"><td valign="top" class="icon"><div class="icon-clipper">#{icon}</div></td><td valign="top"><strong>#{pstat}</strong></td><td valign="top">#{desc}#{vlink}</td></tr>'
		};
		
		this.makeTemplates();
		this.render();
		
		this.onDataChanged	= this.dataChanged.bind(this);
		this.onDestroy		= this.destroy.bind(this);
		
		document.observe('gamecenter:data:changed',this.onDataChanged);
		document.observe('tab:content:destroy',this.onDestroy);
	},
	/**
	 * dataChanged listens for nfl.GameCenter.Engine data events and 
	 * creates internal references, and calls render.
	 * @member nfl.GameCenter.CurrentDrive
	 * @see render
	 */
	dataChanged: function(event){
		/* re-write this function for ie6, critical memory violation fix */
		if(Prototype.Browser.IE && Prototype.Browser.Version == 6){
			this.dataChanged = function(event){
				this.gameid	= Object.clone(event.memo.gameid);
				this.data	= Object.clone(event.memo.gtd);
				this.inc	= Object.clone(event.memo.inc);
				this.render(this.data);
			}
			this.onDataChanged	= this.dataChanged.bind(this);
			this.gameid	= Object.clone(event.memo.gameid);
			this.data	= Object.clone(event.memo.gtd);
			this.inc	= Object.clone(event.memo.inc);
			this.render(this.data);
			return
		}
		this.gameid	= event.memo.gameid;
		this.data	= event.memo.gtd;
		this.inc	= event.memo.inc;
		this.render(this.data);
	},
	/**
	 * makeTemplates used internally to create templates from templates properties.
	 * @member nfl.GameCenter.LastScore
	 */
	makeTemplates: function(){
		this.templates.row		= new Template(this.templates.row);
		this.templates.emptyrow	= new Template(this.templates.emptyrow);
	},
	/**
	 * isUpdated checks to see if a specific play's data matches what we had 
	 * in memory the last time the data was updated. 
	 * @param {String} pid play id to check
	 * @member nfl.GameCenter.CurrentDrive
	 */
	isUpdated:function(pid){
		var cd		= this.data[this.gameid].drives[this.data[this.gameid].drives.crntdrv];
		//var __dbg	= 'looking for '+cd.plays[pid].down+'-'+cd.plays[pid].ydstogo+'-'+cd.plays[pid].yrdln+'\n\r';
		if(typeof this.__oldcd !== 'undefined'){
			if(this.__oldcd.plays === null){ return false; }
			//__dbg		+= 'old data exists\n';
			var cd		= this.data[this.gameid].drives[this.data[this.gameid].drives.crntdrv];
			var old_cd	= this.__oldcd;
			//__dbg		+= 'exists ? '+(typeof old_cd.plays[pid])+' '+ Object.toJSON(old_cd.plays[pid]) +'\n';
			if((typeof old_cd.plays[pid] === 'undefined' || typeof old_cd.plays[pid] === null) && typeof cd.plays[pid] !== 'undefined'){
				return true
			}
		}
		//console.log(__dbg);
		return false
	},
	getPlaysContent: function(dnode){
		var __output	= null;
		if(typeof dnode !== 'undefined'){
			if(dnode.numplays > 0){
				var __output	= '';
				
				
				for(var pid in dnode.plays){
					var pnode	= Object.clone(dnode.plays[pid]);
					var pvid	= nfl.GameCenter.VideoPlaylist.getPlay(pid);
					nfl.GameCenter.Utility.replacePlayerNames(pnode);
					if(pnode.posteam !== ''){
						// add team icon
						pnode.icon	= '<img src="'+nfl.global.imagepath+'/img/teams/'+pnode.posteam+'/'+pnode.posteam+'_logo-20x20.gif"/>';
					}
					var isUpdatedRow	= this.isUpdated(pid);
					pnode.rowClass		= (isUpdatedRow)?' updated':'';
					pnode.pstat			= '';
					if(pnode.yrdln !== null){
						pnode.pstat			= (pnode.down+pnode.ydstogo+parseInt((pnode.yrdln).substring(pnode.yrdln.lastIndexOf(' ')+1)) > 0)?(pnode.down +'-'+pnode.ydstogo+'-'+pnode.yrdln):'';
					}
					pnode.vlink			= (!pvid)?'':'<br/><a href="#tab:watch/contentId:'+pvid+'" class="gc-watch-highlight-link"><span>WATCH HIGHLIGHT</span></a>';
					__output	= (this.templates.row.evaluate(pnode) + __output);
				}
			}
		}
		return __output
	},
	/**
	 * render processes data, merges it with templates, and outputs it 
	 * to the container
	 * @member nfl.GameCenter.CurrentDrive
	 * @param {Object} data reference to the data to be processed, if undefined retrieves data from internal class reference.
	 */
	render: function(data){
		if(typeof data === 'undefined'){data = this.data;}
		if(typeof data[this.gameid] !== 'undefined'){
			if(typeof data[this.gameid].drives !== 'undefined'){
				var __empty		= (data[this.gameid].drives.crntdrv <= 0)?({msg:'Waiting for first play...'}):({msg:'None'});
				var __content	= this.templates.emptyrow.evaluate(__empty);
				var cd	= data[this.gameid].drives[data[this.gameid].drives.crntdrv];
				if(cd !== null){
					/* write the current drive plays to the container */
					var __tout	= this.getPlaysContent(cd);
					if(__tout !== null){
						if(typeof nfl.global.gc.game.teams[(cd.posteam)] !== 'undefined'){
							cd.posteamname	= nfl.global.gc.game.teams[cd.posteam].fullname;
						}
						__content	= __tout;
					}
					//console.log('current drive node not null. content = '+__tout);
					this.container.update(__content);
				}
			}	
		}
		//console.info('cloning data to old data, '+Object.toJSON(this.__olddata));
		if(typeof data[this.gameid] !== 'undefined'){
			if(typeof data[this.gameid].drives !== 'undefined'){
				if(typeof data[this.gameid].drives[data[this.gameid].drives.crntdrv] !== 'undefined'){this.__oldcd.plays	= Object.clone(data[this.gameid].drives[data[this.gameid].drives.crntdrv].plays);}
			}
		}
		//console.info('cloned data to old data, '+Object.toJSON(this.__oldcd));
		document.fire('gamecenter:controls:currentdrive:changed');
		this.onRender();
	},
	/**
	 * onRender is processed after a successful call to the render method. It 
	 * selects the updated rows by class, calls the highlight method on them, 
	 * and sets a timer event on which to unhighlight the rows.
	 * @member nfl.GameCenter.CurrentDrive
	 */
	onRender: function(){
		var rows	= this.container.select('tr.updated');
		this.highlight(rows);
		setTimeout(this.unhighlight.bind(this,rows),2000);
	},
	/**
	 * highlight ads the class name 'highlighted' to the rows passed to it.
	 * @param {Object[]} rows to add the class to
	 * @member nfl.GameCenter.CurrentDrive
	 */
	highlight: function(rows){
		rows.each(function(ele){
			ele.addClassName('highlighted');
		});
	},
	/**
	 * unhighlight calls the morph method on the elements passed to it. slowly fading the elements 
	 * background-color property to white, and then removing the class 'highlighted' from the element.
	 * @param {Object[]} rows to fade and remove class from.
	 * @member nfl.GameCenter.CurrentDrive
	 */
	unhighlight: function(rows){
		rows.each(function(ele){
			ele.morph('background-color:#fff',{duration:3.0,afterFinish:function(){
				ele.removeClassName('highlighted');
				ele.writeAttribute('style','');
			}});
		});
	},
	/**
	 * destroy unbinds event listeners
	 * @member nfl.GameCenter.CurrentDrive
	 */
	destroy: function(){
		/* blue pill */
		document.stopObserving('gamecenter:data:changed',this.onDataChanged);
		document.stopObserving('tab:content:destroy',this.onDestroy);
		console.info('destroyed CurrentDrive');
	}
});
nfl.GameCenter.PlayByPlayLists	= Class.create(
	/** @lends nfl.GameCenter.PlayByPlayLists */		
	{
	/**
	 * listens for data change events from nfl.GameCenter.Engine and 
	 * updates a play by play widget on the page with new data
	 * @constructs nfl.GameCenter.PlayByPlayLists
	 * @requires Prototype framework
	 * @requires nfl.GameCenter.Engine requires instance of engine publicly available as nfl.global.gc.engine
	 * @param {Object[]} containers is a collection of objects containing key value pairs {'cd':<elementid>,'1':<elementid>} in which the 
	 * keys correspond to quarters that the plays occured in and the value is the element id of a container in which to render the data.
	 * @param {Object} options is an object of options that pertain to this class instance 
	 * @property {Object} containers dom element for the container. created from container param.
	 * @property {Object} data internal reference to data fed via engines data changed event.
	 * @property {Object} gameid the games elias id. automatically populated from engine instance.
	 * 
	 * @member nfl.GameCenter
	 * @see nfl.GameCenter.Engine
	 * @return instance of this class with it's methods.
	 * @type Object
	 */
	initialize: function(containers,options){
		this.containers		= containers; //for(key in containers){ containers[key] = $(containers[key]);}
		this.data			= null;
		this.gameid			= -1;
		if(typeof nfl.global.gc.engine !== 'undefined'){
			if(typeof nfl.global.gc.engine.data !== 'undefined'){
				this.data			= (Prototype.Browser.IE && Prototype.Browser.Version == 6)?Object.clone(nfl.global.gc.engine.data):nfl.global.gc.engine.data;
			}
			if(typeof nfl.global.gc.engine.gameid !== 'undefined'){
				this.gameid			= nfl.global.gc.engine.gameid;
			}
		}
		this.options		= (typeof options !== 'undefined')?options:{};
		this.templates		= {
				header:'<tr class="thd2 #{posteam}font"><td colspan="2">#{posteamname}</td><td>#{postime}</td></tr>',
				emptyrow:'<tr class="thd2 tbdyempty"><td colspan="3"><strong>Waiting for first play</strong></td></tr>',
				row:'<tr class="tbdy1 #{trclass}"><td valign="top">#{icon}</td><td><strong>#{pstat}</strong>#{desc}#{vlink}</td><td></td></tr>'
		};
		
		this.makeTemplates();
		this.render();
		
		this.onDataChanged	= this.dataChanged.bind(this);
		this.onDestroy		= this.destroy.bind(this);
		
		document.observe('gamecenter:data:changed',this.onDataChanged);
		document.observe('tab:content:destroy',this.onDestroy);
	},
	/**
	 * dataChanged listens for nfl.GameCenter.Engine data events and 
	 * creates internal references, and calls render.
	 * @member nfl.GameCenter.PlayByPlayLists
	 * @see render
	 */
	dataChanged: function(event){
		/* re-write this function for ie6, critical memory violation fix */
		if(Prototype.Browser.IE && Prototype.Browser.Version == 6){
			this.dataChanged = function(event){
				this.gameid	= Object.clone(event.memo.gameid);
				this.data	= Object.clone(event.memo.gtd);
				this.render(this.data);
			}
			this.onDataChanged	= this.dataChanged.bind(this);
			this.gameid	= Object.clone(event.memo.gameid);
			this.data	= Object.clone(event.memo.gtd);
			this.render(this.data);
			return
		}
		this.gameid	= event.memo.gameid;
		this.data	= event.memo.gtd;
		this.render(this.data);
	},
	/**
	 * makeTemplates used internally to create templates from templates properties.
	 * @member nfl.GameCenter.PlayByPlayLists
	 */
	makeTemplates: function(){
		this.templates.header	= new Template(this.templates.header);
		this.templates.row		= new Template(this.templates.row);
	},
	/**
	 * getPlaysContent only used for displaying result of current drive.
	 * @member nfl.GameCenter.PlayByPlayLists
	 * @param {Object} dnode drive node from the json.
	 * @returns {String} result of template evaluation on all play nodes contained within dnode param.
	 */
	getPlaysContent: function(dnode){
		var __output	= null;
		if(typeof dnode !== 'undefined'){
			if(dnode.numplays > 0){
				var __output	= '';
				for(var pid in dnode.plays){
					var pnode	= Object.clone(dnode.plays[pid]);
					var pvid	= nfl.GameCenter.VideoPlaylist.getPlay(pid);
					nfl.GameCenter.Utility.replacePlayerNames(pnode);
					if(pnode.posteam !== ''){
						// add team icon
						pnode.icon	= '<img src="'+nfl.global.imagepath+'/img/teams/'+pnode.posteam+'/'+pnode.posteam+'_logo-20x20.gif"/>';
					}
					pnode.pstat			= '';
					if(pnode.yrdln !== null){
						pnode.pstat			= (pnode.down+pnode.ydstogo+parseInt((pnode.yrdln).substring(pnode.yrdln.lastIndexOf(' ')+1)) > 0)?(pnode.down +'-'+pnode.ydstogo+'-'+pnode.yrdln):'';
					}
					pnode.vlink			= (!pvid)?'':'<br/><a href="#tab:watch/contentId:'+pvid+'" class="gc-watch-highlight-link"><span>WATCH HIGHLIGHT</span></a>';
					__output	+= this.templates.row.evaluate(pnode);
				}
			}
		}
		return __output
	},
	/**
	 * render processes data, merges it with templates, and outputs it 
	 * to the values in the containers collection whose keys match the quarter that the play occurred in.
	 * @member nfl.GameCenter.PlayByPlayLists
	 * @param {Object} data reference to the data to be processed, if undefined retrieves data from internal class reference.
	 */
	render: function(data){
		if(typeof data === 'undefined'){data = this.data;}
		//console.info('nfl.GameCenter.PlayByPlayLists.render()');
		var hasDrives	= false;
		if(typeof data[this.gameid] !== 'undefined'){
			if(typeof data[this.gameid].drives !== 'undefined'){
				var __content	= {};
				hasDrives		= (data[this.gameid].drives.crntdrv > 0)?true:false;
				var cd	= data[this.gameid].drives[data[this.gameid].drives.crntdrv];
				//console.log('nfl.GameCenter.PlayByPlayLists.render drives: '+ Object.toJSON(cd));
				if(cd !== null && typeof this.containers.cd !== 'undefined'){
					/* write the current drive plays to the cd container */
					var __tout	= this.getPlaysContent(cd);
					if(__tout !== null){
						if(typeof nfl.global.gc.game.teams[(cd.posteam)] !== 'undefined'){
							cd.posteamname	= nfl.global.gc.game.teams[cd.posteam].fullname;
						}
						__content[this.containers.cd]	= __tout;
						__content[this.containers.cd]	= (this.templates.header.evaluate(cd))+__tout;
					}
					//console.log('current drive node not null. content = '+__tout);
				}
				
				for(var dseq in data[this.gameid].drives){
					if(dseq !== 'crntdrv'){
						/* regular old drive, check against quarter property */
						if(typeof this.containers[data[this.gameid].drives[dseq].qtr] !== 'undefined'){
							/* we have a matching container for this drives quarter */
							//var __tout		= this.getPlaysContent(data[this.gameid].drives[dseq]);
							var __data				= Object.clone(data[this.gameid].drives[dseq]);
							if(typeof nfl.global.gc.game.teams[(data[this.gameid].drives[dseq].posteam)] !== 'undefined'){
								__data.posteamname	= nfl.global.gc.game.teams[__data.posteam].fullname;
							}
							var writtenDriveHeader	= false;
							if(typeof data[this.gameid].drives[dseq] !== 'undefined'){
								if(data[this.gameid].drives[dseq].numplays > 0){
									for(var pid in data[this.gameid].drives[dseq].plays){
										var pnode	= data[this.gameid].drives[dseq].plays[pid];
										var pvid	= nfl.GameCenter.VideoPlaylist.getPlay(pid);
										nfl.GameCenter.Utility.replacePlayerNames(pnode);
										if(pnode.posteam !== ''){
											// add team icon
											pnode.icon	= '<img src="'+nfl.global.imagepath+'/img/teams/'+pnode.posteam+'/'+pnode.posteam+'_logo-20x20.gif"/>';
										}
										pnode.vlink			= (!pvid)?'':'<br/><a href="#tab:watch" onclick="document.fire(\'gamecenter:videoicon:click\',{id:\''+pvid+'\'}); return false" class="gc-watch-highlight-link"><span>WATCH HIGHLIGHT</span></a>';
										pnode.pstat			= '';
										if(pnode.yrdln !== null){
											pnode.pstat			= (pnode.down+pnode.ydstogo+parseInt((pnode.yrdln).substring(pnode.yrdln.lastIndexOf(' ')+1)) > 0)?(pnode.down +'-'+pnode.ydstogo+'-'+pnode.yrdln):'';
										}
										if(typeof __content[this.containers[pnode.qtr]] === 'undefined'){ __content[this.containers[pnode.qtr]] = '';}
										if(!writtenDriveHeader){
											//we only write one header per drive sequence
											writtenDriveHeader	= true;
											__content[this.containers[pnode.qtr]]	= __content[this.containers[pnode.qtr]] + (this.templates.header.evaluate(__data));
										}
										__content[this.containers[pnode.qtr]]	= __content[this.containers[pnode.qtr]] + this.templates.row.evaluate(pnode);
									}
								}
							}
						}
					}
				}
				for(var domele in __content){
					//console.warn('updating '+ domele +' with '+__content[domele]);
					$(domele).update(__content[domele]);
				}
			}
		}
		if(!hasDrives){
			for(var key in this.containers){
				var cont = $(this.containers[key]);
				if(cont){
					cont.update(this.templates.emptyrow);
				}
			}
		}
	},
	/**
	 * destroy unbinds event listeners
	 * @member nfl.GameCenter.PlayByPlayLists
	 */
	destroy: function(){
		/* blue pill */
		document.stopObserving('gamecenter:data:changed',this.onDataChanged);
		document.stopObserving('tab:content:destroy',this.onDestroy);
		console.info('destroyed PlayByPlayLists');
	}
})
nfl.GameCenter.TeamStats = Class.create(
	/** @lends nfl.GameCenter.TeamStats */
	{
	/**
	 * nfl.GameCenter.TeamStats is a widget engine class. It 
	 * listens for data change events from nfl.GameCenter.Engine and 
	 * updates a widget on the page with new data
	 * @constructs nfl.GameCenter.TeamStats
	 * @param {String} container is the container elements id attribute
	 * @param {Object} options is an object of options that pertain to this class instance .             
	 * @property {Object} container dom element for the container. created from container param.
	 * @property {Object} data internal reference to data fed via engines data changed event.
	 * @property {Object} gameid the games elias id. automatically populated from engine instance.
	 * 
	 * @member nfl.GameCenter
	 * @requires Prototype framework
	 * @requires nfl.GameCenter.Engine requires instance of engine publicly available as nfl.global.gc.engine
	 * @see nfl.GameCenter.Engine
	 * @return instance of this class with it's methods.
	 * @type Object
	 */
	initialize: function(container,options){
		this.container		= $(container);
		this.data			= null;
		this.gameid			= -1;
		if(typeof nfl.global.gc.engine !== 'undefined'){
			if(typeof nfl.global.gc.engine.data !== 'undefined'){
				this.data			= (Prototype.Browser.IE && Prototype.Browser.Version == 6)?Object.clone(nfl.global.gc.engine.data):nfl.global.gc.engine.data;
			}
			if(typeof nfl.global.gc.engine.gameid !== 'undefined'){
				this.gameid			= nfl.global.gc.engine.gameid;
			}
		}
		this.options		= (typeof options !== 'undefined')?options:{};
		this.template		= new Template(this.container.innerHTML);
	
		this.render();
		this.container.show();
		
		this.onDataChanged	= this.dataChanged.bind(this);
		this.onDestroy		= this.destroy.bind(this);
		
		document.observe('gamecenter:data:changed',this.onDataChanged);
		document.observe('tab:content:destroy',this.onDestroy);
	},
	/**
	 * dataChanged listens for nfl.GameCenter.Engine data events and 
	 * creates internal references, and calls render.
	 * @member nfl.GameCenter.TeamStats
	 * @see render
	 */
	dataChanged: function(event){
		/* re-write this function for ie6, critical memory violation fix */
		if(Prototype.Browser.IE && Prototype.Browser.Version == 6){
			this.dataChanged = function(event){
				this.data	= Object.clone(event.memo.gtd);
				this.render(this.data);
			}
			this.onDataChanged	= this.dataChanged.bind(this);
			this.data	= Object.clone(event.memo.gtd);
			this.render(this.data);
			return
		}
		this.data	= event.memo.gtd;
		this.render(this.data);
	},
	/**
	 * render processes data, merges it with templates, and outputs it 
	 * to the container
	 * @member nfl.GameCenter.TeamStats
	 * @param {Object} data reference to the data to be processed, if undefined retrieves data from internal class reference.
	 */
	render: function(data){
		if(typeof data === 'undefined'){data = this.data;}
		if(this.container){
			if(typeof data[this.gameid] === 'undefined' || data[this.gameid].qtr === 'Pregame'){
				data	= {
					"home":{"score":{"1": 0,"2": 0,"3": 0,"4": 0,"5": 0,"T": 0},"stats": {"team":{"totfd": "-","totyds": "-","pyds": "-","ryds": "-","pen": "-","penyds": "-","trnovr": "-","pt": "-","ptyds": "-","top": "-"}},"players": null},
					"away":{"score":{"1": 0,"2": 0,"3": 0,"4": 0,"5": 0,"T": 0},"stats": {"team":{"totfd": "-","totyds": "-","pyds": "-","ryds": "-","pen": "-","penyds": "-","trnovr": "-","pt": "-","ptyds": "-","top": "-"}},"players": null}
				}
			}else{
				data = data[this.gameid];
			}
			var __output	= this.template.evaluate(data);
			this.container.update(__output);
		}
	},
	/**
	 * destroy unbinds event listeners
	 * @member nfl.GameCenter.TeamStats
	 */
	destroy: function(){
		/* blue pill */
		document.stopObserving('gamecenter:data:changed',this.onDataChanged);
		document.stopObserving('tab:content:destroy',this.onDestroy);
		console.info('destroyed TeamStats');
	}
});

nfl.GameCenter.Stats = Class.create(
	/** @lends nfl.GameCenter.Stats */
	{
	/**
	 * nfl.GameCenter.TeamStats is a widget engine class. It 
	 * listens for data change events from nfl.GameCenter.Engine and 
	 * updates a widget on the page with new data
	 * @constructs nfl.GameCenter.Stats
	 * @param {String} homecontainer is the home teams container element id attribute
	 * @param {String} awaycontainer is the away teams container element id attribute
	 * @param {Object} options is an object of options that pertain to this class instance .             
	 * @property {Object} containers object containing two elements.
	 * @property {Object} containers.home dom element for the home container. created from homecontainer param.
	 * @property {Object} containers.away dom element for the away container. created from awaycontainer param.
	 * @property {Integer} maxresults specifies how many rows to return for each type of stat. applies to all stats.
	 * @property {Object} maxresults json object specifying how many rows to return per stat type. the value for each key applies to a specific type of stat.
	 * @property {Object} templates object containing template collections.
	 * @property {Object} templates.headers object containing collection of header templates.
	 * @property {Object} templates.rows object containing collection of row templates.
	 * 
	 * @requires Prototype framework
	 * @requires nfl.GameCenter.Engine requires instance of engine publicly available as nfl.global.gc.engine
	 * @see nfl.GameCenter.Engine
	 * @return instance of this class with it's methods.
	 * @type Object
	 */
	initialize: function(homecontainer,awaycontainer,options){
		this.containers		= {home:$(homecontainer),away:$(awaycontainer)}
		this.data			= null;
		this.gameid			= -1;
		if(typeof nfl.global.gc.engine !== 'undefined'){
			if(typeof nfl.global.gc.engine.data !== 'undefined'){
				this.data	= (Prototype.Browser.IE && Prototype.Browser.Version == 6)?Object.clone(nfl.global.gc.engine.data):nfl.global.gc.engine.data;
			}
			if(typeof nfl.global.gc.engine.gameid !== 'undefined'){
				this.gameid			= nfl.global.gc.engine.gameid;
			}
		}
		this.options		= (typeof options !== 'undefined')?options:{};
		this.maxresults		= (typeof this.options.maxresults !== 'undefined')?this.options.maxresults:false;
		this.templates		= {headers:{},rows:{}};
		this.inc			= {};
		
		/* absorb current content */
		this.__ccontent		= {home:this.containers.home.innerHTML,away:this.containers.away.innerHTML};
		
		/* header templates */
		this.templates.headers.passing	= '<tr class="stat-head thd2"><td>Passing</td><td>CP/AT</td><td>YDS</td><td>TD</td><td>INT</td></tr>';
		this.templates.headers.rushing	= '<tr class="stat-head thd2"><td>Rushing</td><td>CAR</td><td>YDS</td><td>TD</td><td>LG</td></tr>';
		this.templates.headers.receiving	= '<tr class="stat-head thd2"><td>Receiving</td><td>REC</td><td>YDS</td><td>TD</td><td>LG</td></tr>';
		this.templates.headers.fumbles	= '<tr class="stat-head thd2"><td>Fumbles</td><td>FUM</td><td>LOST</td><td>REC</td><td>YDS</td></tr>';
		this.templates.headers.kicking	= '<tr class="stat-head thd2"><td>Kicking</td><td>FG</td><td>LG</td><td>XP</td><td>PTS</td></tr>';
		this.templates.headers.punting	= '<tr class="stat-head thd2"><td>Punting</td><td>NO</td><td>AVG</td><td>I20</td><td>LG</td></tr>';
		this.templates.headers.kickret	= '<tr class="stat-head thd2"><td>Kickoff Returns</td><td>NO</td><td>AVG</td><td>TD</td><td>LG</td></tr>';
		this.templates.headers.puntret	= '<tr class="stat-head thd2"><td>Punt Returns</td><td>NO</td><td>AVG</td><td>TD</td><td>LG</td></tr>';
		this.templates.headers.defense	= '<tr class="stat-head thd2"><td>Defense</td><td>T-A</td><td>SCK</td><td>INT</td><td>FF</td></tr>';
		
		/* row templates */
		this.templates.rows.passing		= '<tr class="stat-row tbdy1#{rowClass}"><td>#{playerlink}</td><td>#{cmp}/#{att}</td><td>#{yds}</td><td>#{tds}</td><td>#{ints}</td></tr>';
		this.templates.rows.rushing		= '<tr class="stat-row tbdy1#{rowClass}"><td>#{playerlink}</td><td>#{att}</td><td>#{yds}</td><td>#{tds}</td><td>#{lng}</td></tr>';
		this.templates.rows.receiving	= '<tr class="stat-row tbdy1#{rowClass}"><td>#{playerlink}</td><td>#{rec}</td><td>#{yds}</td><td>#{tds}</td><td>#{lng}</td></tr>';
		this.templates.rows.fumbles		= '<tr class="stat-row tbdy1#{rowClass}"><td>#{playerlink}</td><td>#{tot}</td><td>#{lost}</td><td>#{trcv}</td><td>#{yds}</td></tr>';
		this.templates.rows.kicking		= '<tr class="stat-row tbdy1#{rowClass}"><td>#{playerlink}</td><td>#{fgm}/#{fga}</td><td>#{fgyds}</td><td>#{xpmade}/#{xptot}</td><td>#{tot}</td></tr>';
		this.templates.rows.punting		= '<tr class="stat-row tbdy1#{rowClass}"><td>#{playerlink}</td><td>#{pts}</td><td>#{avg}</td><td>#{i20}</td><td>#{lng}</td></tr>';
		this.templates.rows.kickret		= '<tr class="stat-row tbdy1#{rowClass}"><td>#{playerlink}</td><td>#{ret}</td><td>#{avg}</td><td>#{tds}</td><td>#{lng}</td></tr>';
		this.templates.rows.puntret		= '<tr class="stat-row tbdy1#{rowClass}"><td>#{playerlink}</td><td>#{ret}</td><td>#{avg}</td><td>#{tds}</td><td>#{lng}</td></tr>';
		this.templates.rows.defense		= '<tr class="stat-row tbdy1#{rowClass}"><td>#{playerlink}</td><td>#{tkl}-#{ast}</td><td>#{sk}</td><td>#{int}</td><td>#{ffum}</td></tr>';		
		this.templates.empty			= '<tr class="stat-row tbdy1"><td></td><td>-</td><td>-</td><td>-</td><td>-</td></tr>';
		
		this.makeTemplates();
		this.render();
		
		this.onDataChanged	= this.dataChanged.bind(this);
		this.onDestroy		= this.destroy.bind(this);
		
		document.observe('gamecenter:data:changed',this.onDataChanged);
		document.observe('tab:content:destroy',this.onDestroy);
	},
	/**
	 * dataChanged listens for nfl.GameCenter.Engine data events and 
	 * creates internal references, and calls render.
	 * @member nfl.GameCenter.Stats
	 * @see render
	 */
	dataChanged: function(event){
		/* re-write this function for ie6, critical memory violation fix */
		if(Prototype.Browser.IE && Prototype.Browser.Version == 6){
			this.dataChanged = function(event){
				this.gameid	= Object.clone(event.memo.gameid);
				this.data	= Object.clone(event.memo.gtd);
				this.inc	= Object.clone(event.memo.inc);
				this.render(this.data);
			}
			this.onDataChanged	= this.dataChanged.bind(this);
			this.gameid	= Object.clone(event.memo.gameid);
			this.data	= Object.clone(event.memo.gtd);
			this.inc	= Object.clone(event.memo.inc);
			this.render(this.data);
			return
		}
		this.gameid	= event.memo.gameid;
		this.data	= event.memo.gtd;
		this.inc	= event.memo.inc;
		this.render(this.data);
	},
	/**
	 * makeTemplates used internally to create templates from templates properties.
	 * @member nfl.GameCenter.Stats
	 */
	makeTemplates: function(){
		for(var key in this.templates.headers){
			//this.templates.headers[key]	= new Template(this.templates.headers[key]);
		}
		for(var key in this.templates.rows){
			this.templates.rows[key]	= new Template(this.templates.rows[key]);
		}
	},
	getTeamContent: function(teamnode){
		//console.log('getTeamContent: '+Object.toJSON(teamnode.stats));
		var __output	= null;
		//if(typeof teamnode.stats !== 'undefined'){
			/* we have stats, filler up */
			for(var key in this.templates.rows){
				//console.log('nfl.GameCenter.Stats.render: data:',data);
				var __limit		= (typeof this.maxresults[key] !== 'undefined')?this.maxresults[key]:false;
				if(__limit > 0 || __limit === false){
					/* always print out the header */
					if(__output === null){ __output = '';}
					__output	+= this.templates.headers[key];
					var __routput = null;
					if(typeof teamnode.stats !== 'undefined' && teamnode.stats !== null){
						if(typeof teamnode.stats[key] != 'undefined'){
							/* add each player to output */
							var __pTemplate	= this.templates.rows[key];
							var __limit		= (typeof this.maxresults[key] !== 'undefined')?this.maxresults[key]:false;
							var __inc		= 0;
							if(typeof this.maxresults === 'integer'){ __limit = this.maxresults; /* limit all stats by this number*/; }
							for(var pkey in teamnode.stats[key]){
								if(typeof teamnode.stats[key] !== 'undefined'){
									if(typeof teamnode.stats[key][pkey] !== 'undefined'){
										if(__inc >= __limit && __limit !== false){ /* console.log('breaking on limit '+ __limit+' for '+key+' stats');*/ break; }
										try{
											var __pJSON		 	= teamnode.stats[key][pkey];
											//var isUpdatedRow	= this.isUpdated(pkey,key);
											var isUpdatedRow	= __pJSON.updated;
											if(__pJSON.updated ){
												//console.log('updated row? '+isUpdatedRow,__pJSON);
											}
											__pJSON.playerlink	= (__pJSON.name+'');
											try{
												var prec	= nfl.GameCenter.Utility.getPlayerFromKey(pkey);
												if(prec !== false){
													__pJSON.playerlink	= '<a href="/players/profile?id='+teamnode.players[pkey].esbid+'" data-id="'+pkey+'" class="nfl-player-link">'+(teamnode.players[pkey].fn.substring(0,1))+'. '+ teamnode.players[pkey].ln +'</a>';
												}
											}catch(e){}
											if(key === 'kicking'){
												__pJSON.tot			= (parseInt(__pJSON.xptot)+parseInt(__pJSON.totpfg));
											}
											__pJSON.rowClass		= (isUpdatedRow === true)?' updated':'';
											if(__routput === null){__routput	= '';}
											__routput	+= __pTemplate.evaluate(__pJSON);
											__inc++;
										}catch(e){ console.warn('error: '+e.message);}
									}
								}
							}
						}					
					}
					if(__routput !== null){
						__output	+= __routput;
					}else{
						__output	+= this.templates.empty;
					}
				}
			}
		//}
		
		//console.log('getTeamContent: returning: '+__output);
		return __output;
	},
	/**
	 * render processes data, merges it with templates, and outputs it 
	 * to the containers specified by the containers property.
	 * @requires getTeamContent
	 * @member nfl.GameCenter.Stats
	 * @param {Object} data reference to the data to be processed, if undefined retrieves data from internal class reference.
	 */
	render: function(data){
		if(typeof data === 'undefined'){data = this.data;}
		//console.log('nfl.GameCenter.Stats.render: data:',data);
		if(this.containers.home && typeof data[this.gameid] !== 'undefined'){
			if(typeof data[this.gameid].home !== 'undefined'){
				var homeContent	= this.getTeamContent(data[this.gameid].home);
				if(homeContent !== null){
					/* write it to container */
					//console.log('writing original content for home:\n\r'+this.__ccontent.home,data[this.gameid].home.stats);
					this.containers.home.update(this.__ccontent.home+homeContent);
				}
			}
		};
		if(this.containers.away && typeof data[this.gameid] !== 'undefined'){
			if(typeof data[this.gameid].away !== 'undefined'){
				var awayContent	= this.getTeamContent(data[this.gameid].away);
				if(awayContent !== null){
					/* write it to container */
					//console.log('writing original content for away:\n\r'+this.__ccontent.away,data[this.gameid].away.stats);
					this.containers.away.update(this.__ccontent.away+awayContent);
				}
			}
		};
		this.onRender();
	},
	/**
	 * onRender is processed after a successful call to the render method. It 
	 * selects the updated rows by class, calls the highlight method on them, 
	 * and sets a timer event on which to unhighlight the rows.
	 * @member nfl.GameCenter.Stats
	 */
	onRender: function(){
		var rows	= this.containers.home.select('tr.updated');
		rows		= rows.concat(this.containers.away.select('tr.updated'));
		this.highlight(rows);
		setTimeout(this.unhighlight.bind(this,rows),2000);
	},
	highlight: function(rows){
		rows.each(function(ele){
			ele.addClassName('highlighted');
		});
	},
	unhighlight: function(rows){
		rows.each(function(ele){
			ele.morph('background-color:#fff',{duration:3.0,afterFinish:function(){
				ele.removeClassName('highlighted');
				ele.writeAttribute('style','');
			}});
		});
	},
	/**
	 * destroy unbinds event listeners
	 * @member nfl.GameCenter.Stats
	 */
	destroy: function(){
		/* blue pill */
		document.stopObserving('gamecenter:data:changed',this.onDataChanged);
		document.stopObserving('tab:content:destroy',this.onDestroy);
		console.info('destroyed Stats');
	}
});
/**
 * nfl.GameCenter.Analytics
 * @namespace contains functionally gamecenter uses for omniture tracking.
 */
nfl.GameCenter.Analytics	= function(options){
	/** 
	 * getTabId returns the current active tabs id or null.
	 * @private 
	 * @return current active tab id.
	 */
	var getTabId = function(){
		try{ return nfl.GameCenter.Tabs.getTabId(); }catch(e){ console.warn('nfl.GameCenter.Tabs.getTabId() error:'+ e.message); }
		return null
	}
	/** 
	 * getNameCatString returns proper analytics name cat fragment for current category.
	 * @private 
	 * @param {string} sep the separator character to be used. defaults to colon.
	 * @param {string} category
	 * @return proper analytics string for current category.
	 */
	var getNameCatString = function(sep,cat){
		var sep		= (typeof sep === 'undefined')?':':sep;
		var retStr	= '';
		retStr	+= 'gamecenter'+sep;
		retStr	+= cat.replace('-',sep);
		return retStr.toLowerCase();
	}
	/** 
	 * getNameString returns proper analytics name fragment for current game.
	 * @private 
	 * @param sep the separator character to be used. defaults to colon.
	 * @return proper analytics string for current game. ie: nfl:gamecenter:2009:reg1
	 */
	var getNameString	= function(sep){
		var sep		= (typeof sep === 'undefined')?':':sep;
		var retStr	= '';
		retStr	+= 'nfl:gamecenter'+sep;
		retStr	+= nfl.global.gc.game.year+sep;
		retStr	+= nfl.global.gc.game.seasontype+nfl.global.gc.game.week;
		return retStr.toLowerCase();
	}
	/** 
	 * getPropString returns proper analytics prop fragment for current game.
	 * @private 
	 * @param sep the separator character to be used. defaults to pipe.
	 * @returns proper prop string for current game. ie: nfl:gamecenter|2009|week reg1|bal@dal|pre
	 */
	var getPropString	= function(sep){
		var sep		= (typeof sep === 'undefined')?'|':sep;
		var retStr	= '';
		retStr	+= 'nfl:gamecenter'+sep;
		retStr	+= nfl.global.gc.game.year+sep;
		retStr	+= 'week '+nfl.global.gc.game.seasontype+nfl.global.gc.game.week+sep;
		retStr	+= nfl.global.gc.extdata.teams.away.abbr+'@'+nfl.global.gc.extdata.teams.home.abbr+sep;
		retStr	+= nfl.global.gc.game.state;
		
		return retStr.toLowerCase();
	}
	/** 
	 * log creates an omniture request and sends it.
	 * @private 
	 * @param props a key value collection of properties and their values to attach to the omniture request.
	 * @param linkname the name of the link to attach to the event.
	 */
	var log = function(props,linkname){
		console.log('nfl.GameCenter.Analytics.log ',props);
		
		var __propkeys		= [];
		var s_analytics 	= s_gi(s_account);
		for(var key in props){
			if(key !== 'linkTrackEvents' && key !== 'linkTrackVars'){
				__propkeys[__propkeys.length]	= key;
			}
			s_analytics[key]		= props[key];
		}
		if(typeof props.events !== 'undefined' && typeof props.linkTrackEvents === 'undefined'){ s_analytics.linkTrackEvents = props.events; }
		if(typeof props.linkTrackVars === 'undefined'){ s_analytics.linkTrackVars = __propkeys.join(','); }
		s_analytics.channel	= 'gamecenter'; 
		s_analytics.eVar3	= 'gamecenter';
		//s_analytics.linkTrackVars = s_analytics.linkTrackVars+',eVar3,channel';

		if(typeof linkname === 'undefined'){
			void(s_analytics.t());
			return
		}
		s_analytics.tl(true,'o',linkname);
	}
	/** 
	 * ping creates an omniture request for the tabid. if no tab id is passed in, it uses the currently active tabs id.
	 * @private 
	 * @tabid {string} tabid. the id of the tab for this request.
	 */
	var ping = function(tabid){
		//console.info('nfl.GameCenter.Analytics.ping:'+tabid);
		var tabid		= (typeof tabid === 'undefined' || tabid === null)?((getTabId() !== null)?(getTabId()):'tab_gamecenter'):tabid;
		var proppath	= getPropString('|')+'|'+tabid;
		log({
			'events':'event1',
			'pageName':getNameString(':'),
			'hier1':proppath,
			'prop2':getNameCatString(':',tabid),
			'prop5':proppath,
			'eVar4':proppath
		});
	}
	/** 
	 * onTabClick resets the internal ping timer and performs a ping request for the tab that was clicked.
	 * @private 
	 * @param event
	 */
	var onTabClick	= function(event){
		//console.info('nfl.GameCenter.Analytics.onTabClick');
		/* reset elapsed */
		elapsed.reset();
		/* ping */
		ping(event.memo);
	}
	/** 
	 * onElapsed is the wrapper function for the ping method. this is called by the internal timer.
	 * @private 
	 * @param event
	 */
	var onElapsed = function(event){
		//console.info('nfl.GameCenter.Analytics.onElapsed');
		ping(getTabId());
	}
	/** 
	 * elapsed contains methods used for manipulating the internal timer of ping requests.
	 * fires 'nfl:gamecenter:analytics:elapsed'.
	 * @private 
	 */
	var elapsed = function(){
		var __PE = null;
		var onPE = function(){
			/* fire elapsed event */
			document.fire('nfl:gamecenter:analytics:elapsed');
		}
		return {
			/**
			 * stop stops the internal ping timer
			 */
			stop: function(){
				if(__PE !== null){
					try{ __PE.stop(); }catch(e){}
					__PE = null;
				}
			},
			/**
			 * start starts the internal ping timer
			 */
			start: function(){
				if(__PE === null){
					/* create timed ping */
					__PE = new PeriodicalExecuter(onPE,300);
				}
			},
			/**
			 * reset resets the internal ping timer
			 */
			reset: function(){
				if(__PE !== null){
					this.stop();
					this.start();
				}
			}
		}
	}();
	
	document.observe("tab:content:loading",onTabClick);
	document.observe('nfl:gamecenter:analytics:elapsed',onElapsed);
	document.observe('dom:loaded',function(){
		elapsed.start();
	});
	
	return {
		/** 
		 * log creates an analytics event call with the properties passed to it.
		 * @param {Object} props is an object of key value pairs to assign to the omniture call
		 * @param {String} linkname is the linkname to pass to an omniture click event. optional.
		 */
		log: function(props,linkname){
			log(props,linkname);
		},
		/**
		 * resetTimer resets the internal timer that periodically pings omniture.
		 */
		resetTimer: function(){
			elapsed.reset();
		},
		/**
		 * ping pings omniture with relevant page/event data. this is the same call that is made automatically every five minutes.
		 */
		ping: function(event){
			ping(event);
		},
		/**
		 * elapsed returns the internal elapsed object
		 * @returns {Object} elapsed
		 */
		elapsed: function(){
			return elapsed
		}
	}
}()

/**
 * nfl.GameCenter.Ads
 * @namespace contains functionally gamecenter uses for ad manipulation.
 */
nfl.GameCenter.Ads	= function(options){
	var __adIsPlaying 		= false;
	var __videoAdIsPlaying 	= false;
	var __sponsorHeader		= null;
	var __rrAdIframe		= null;
	var __rrAdID			= null;
	var __lastAdFuncId		= null;
	var __rrAdSize			= null;
	var __oRRAd				= null;

	/** 
	 * tabAdFunctions is an object containing functions that return ads for specific tabs.
	 * the key of object refers to the name of the tab. ie: 'watch' or 'discuss'.
	 * @private 
	 * @field
	 */
	var tabAdFunctions	= {
		'watch':function(){
			if(getRRAdIframe().readAttribute('height') == '600'){
				/* this is the wrong ad size change it */
				console.warn('change right rail ad to 300x250');
				// swap out class
				$('main-content').removeClassName('grid-160');
				$('main-content').addClassName('grid-250');
				__rrAdIframe	= null;
			}
			return new nfl.ads.Ad(getRRAdId(),getTabAdPath(nfl.global.gc.ads.rr['300x250'],'watch'),{width:300, height:250, rotating: true, write:true});
		},
		/*
		'discuss':function(){
			return new nfl.ads.Ad(getRRAdId(),getTabAdPath(nfl.global.gc.ads.rr['160x600'],'discuss'),{width:160, height:600, rotating: true, write:true});
		},
		*/
		'other':function(tabId){
			var tabId	= (typeof tabId === 'undefined')?'gamecenter':tabId;
			return new nfl.ads.Ad(getRRAdId(),getTabAdPath(nfl.global.gc.ads.rr['160x600'],tabId),{width:160, height:600, rotating: true, write:true});
		}
	}
	/**
	 * getTabAdPath returns the ad path with #{tab.id} replaced with the current active tab id.
	 * @private
	 * @return {string} evaluated ad path
	 */
	var getTabAdPath = function(adPath,tabId){
		var retAdPath 	= (new Template(adPath)).evaluate({tab:{id:('tab_'+tabId)}});
		//console.log('getTabAdPath: '+retAdPath);
		return retAdPath
	}
	/**
	 * getRRAdSize returns the current size of the right rail ad.
	 * @private
	 * @return {object{string:int}} object containing width and height properties.
	 */
	var getRRAdSize = function(){
		var adcol	= $('gc-ad');
		var ad		= adcol.down('iframe');
		return {width:parseInt(ad.readAttribute('width')),height:parseInt(ad.readAttribute('height'))}
	}
	/**
	 * getRRAdId returns the id of the ad container.
	 * @private
	 * @return {string} ad id
	 */
	var getRRAdId = function(){
		if(__rrAdID == null){ var __void = getRRAdIframe(); }
		return __rrAdID
	}
	/**
	 * getRRAdIframe retrieves the ad's iframe via dom query
	 * @return {HTML Element} ad iframe
	 */
	var getRRAdIframe = function(){
		if(__rrAdIframe == null){
			var adcol	= $('gc-ad');
			var ad		= adcol.down('iframe');
			
			__rrAdIframe	= ad;
			__rrAdID    	= (ad.id).replace('-iframe','');
		}
		return __rrAdIframe
	}
	/**
	 * showSponsoredHeader shows the sponsored header
	 * @private
	 */
	var showSponsoredHeader	= function(){
		if(__sponsorHeader == null){ try{ __sponsorHeader = $('dc-header').down('.adcontainer'); }catch(e){};}
		//console.log('showSponsoredHeader: __sponsorHeader'+ __sponsorHeader);
		if(__sponsorHeader){
			__sponsorHeader.show();
			//console.log('showSponsoredHeader: show() called');
		}
	}
	/**
	 * hideSponsoredHeader hides the sponsored header
	 * @private
	 */
	var hideSponsoredHeader	= function(){
		//console.log('hideSponsoredHeader');
		if(__sponsorHeader == null){ try{ __sponsorHeader = $('dc-header').down('.adcontainer'); }catch(e){};}
		//console.log('hideSponsoredHeader: __sponsorHeader'+ __sponsorHeader);
		if(__sponsorHeader){
			__sponsorHeader.hide();
		}
	}
	/**
	 * onTabChanged performs logic for swapping out the right rail ad based on the active tab when a tab 
	 * is clicked if the tab has an entry in tabAdFunctions.
	 * @private
	 */
	var onTabChanged = function(event){
		/* change ad for video */
		var adID    = getRRAdId();
		//console.info('onTabChanged: event.memo = '+event.memo+', rr iframe height = '+ getRRAdIframe().readAttribute('height'));
		if(event.memo !== 'watch' && !($('main-content').hasClassName('grid-160'))){
			/* this is the wrong ad size change it */
			//console.warn('change right rail ad to 160x600');
			$('main-content').removeClassName('grid-250');
			$('main-content').addClassName('grid-160');	
		}
		/* get specific ad object for tab */
		var newAdFuncId	= (typeof tabAdFunctions[event.memo] !== 'undefined')?event.memo:'other';
		//console.info('nfl.GameCenter.Ads: new ad for '+newAdFuncId);
		if(__lastAdFuncId !== newAdFuncId){
			var newAd = (tabAdFunctions[newAdFuncId])();
			/* write ad to the page */
			newAd.write();
			//console.info('nfl.GameCenter.Ads: wrote new ad to the page for '+newAdFuncId);
		}
		__lastAdFuncId = newAdFuncId;
	}
	/**
	 * onIntervalChange occurs when the ad rotation interval changes, this occurs through a config update coming 
	 * from the gamecenter engine class. when the interval changes, this method changes the rotation interval and restarts 
	 * the rotation.
	 * @private
	 */
	var onIntervalChange = function(event){
		if(nfl.ads.rotation.interval !== event.memo){
			console.info('nfl.GameCenter.Ads: interval changed to '+ event.memo);
			nfl.ads.rotation.interval = event.memo;
			nfl.ads.rotation.start(); /* we call a start on it after the change to restart with the new value */
		}
	}
	/**
	 * onAdEvent is called when an ad from a video player or the drive chart starts to play. 
	 * it hides the sponsored header and stops ad rotation.
	 * @private
	 */
	var onAdEvent	= function(event){
		if(!__videoAdIsPlaying){
			__adIsPlaying	= true;
			/* hide sponsor header */
			hideSponsoredHeader();
			/* stop ad rotator */
			nfl.ads.rotation.stop();
		}
	}
	/**
	 * onAdEventComplete is called when an ad from a video player or the drive chart finishes playing.
	 * it shows the sponsored header, replaces the co-ad if there is any with a regular right rail ad and starts the rotation.
	 * @private 
	 */
	var onAdEventComplete = function(event){
		console.log('nfl.GameCenter.Ads.onAdEventComplete:'+event.memo.swfid);
		if(event.memo.swfid === 'drive_chart_ads'){
			__adIsPlaying	= false;
			/* show sponsor header */
			showSponsoredHeader();
			/* replace co-ad with something we can sink our teeth into */
			if(__oRRAd !== null){ nfl.ads.replace(__oRRAd);}
			/* start rotator back up */
			nfl.ads.rotation.start();
		}else{
			__videoAdIsPlaying = false;
		}
	}
	/**
	 * onAdLoad is called when a drive chart ad has a co-ad, if it does then it replaces the current 
	 * right rail ad with the appropriate co-ad
	 * @private
	 */
	var onAdLoad	= function(event){
		console.info('onVideoAdLoad: '+ event.memo);
		if(event.memo.swfid === 'drive_chart_ads' && event.memo.companionURL !== ''){
			var dims	= getRRAdSize();
			console.info('Showing Companion AD for BIA Ad: '+dims.width+'x'+dims.height);
			__oRRAd	= nfl.ads.getById(getRRAdId());
			new nfl.ads.Ad(getRRAdId(),'',{url:event.memo.companionURL,width:dims.width, height:dims.height, rotating: true, write:true});
		}else{
			if(event.memo.swfid !== 'drive_chart_ads'){
				/* other video player ad loaded */
				__videoAdIsPlaying	= true;
			}
		}
	}
	
	
	/* end event handler functions */
	document.observe('gamecenter:data:adevent',onAdEvent);
	document.observe('tab:tabchanged',onTabChanged);
	document.observe('gamecenter:ads:interval:changed',onIntervalChange);
	// wait for video player events
	document.observe('videoplayer:adLoaded', onAdLoad );
	document.observe('videoplayer:adComplete',onAdEventComplete);
	//document.observe( VideoPlayerEvent.UNAVAILABLE, this.writeAd ); 
	//initialize(options);
	
	return {
		/**
		 * stopRotation stops right rail ad rotation
		 */
		stopRotation: function(){
			nfl.ads.rotation.stop();
		},
		/**
		 * startRotation starts right rail ad rotation
		 */
		startRotation: function(){
			nfl.ads.rotation.start();
		},
		/**
		 * adIsPlaying returns boolean whether or not bia or video ad is currently playing
		 * @return {bool} bia/video ad is playing
		 */
		adIsPlaying: function(){
			return __adIsPlaying;
		},
		/**
		 * getRightRailAd retrieves the ad's container element
		 * @return {HTML Element} ad container
		 */
		getRightRailAd: function(){
			return $(__rrAdID);
		},
		/**
		 * getRightRailAdSize returns the current size of the right rail ad.
		 * @return {object{string:int}} object containing width and height properties.
		 */
		getRightRailAdSize: function(){
			return getRRAdSize();
		}
	}
}();

if(nfl.global.gc.dc.load){
	/* initialize game center drive chart*/
	nfl.global.gc.dc		= new nfl.GameCenter.DriveChart(); nfl.global.gc.dc.initialize();
}
/* initialize game center polling engine */
var engineopts			= {"config":nfl.global.gc.extdata.config,"players":nfl.global.gc.extdata.players,"media":nfl.global.gc.extdata.media,"weather":nfl.global.gc.extdata.weather,"stadium":nfl.global.gc.extdata.stadium,"rooftype":nfl.global.gc.extdata.rooftype,"teams":nfl.global.gc.extdata.teams};
if(typeof nfl.global.gc.predata !== 'undefined'){ engineopts['skeletondata'] = nfl.global.gc.predata; }
if(typeof nfl.global.gc.dc.load !== 'undefined'){if( nfl.global.gc.dc.load == false){engineopts['donotloadgtd'] = true;}}
nfl.global.gc.engine	= new nfl.GameCenter.Engine(nfl.global.gc.game.id,nfl.global.gc.game.state,engineopts);
if(nfl.global.gc.game.state === 'ACTIVE'){
	/* tell the video list class to load from XHR */
	nfl.GameCenter.VideoPlaylist.useXHR();
}


if(nfl.global.gc.game.tenminutewindow){
	/* check periodically to see if game has started */
	nfl.global.gc.pregameexecuter	= new PeriodicalExecuter(function(){
		nfl.global.gc.engine.getState(function(XHR){
			if(typeof XHR.responseJSON != "undefined"){
				json	= XHR.responseJSON;
			}else{
				json	= eval('(' + XHR.responseText + ')');
			}
			if(typeof json !== 'undefined'){
				if(typeof json[nfl.global.gc.engine.gameid] !== 'undefined'){
					if(typeof json[nfl.global.gc.engine.gameid].qtr !== 'undefined'){
						var qtr	= json[nfl.global.gc.engine.gameid].qtr;
						if(qtr == '1' || qtr == '2' || qtr == '3' || qtr == '4'){
							/* game is in progress, alert users to this */
							document.fire('gamecenter:data:gip');
						}
					}
				}
			}
		});
	},300);
}

document.observe('gamecenter:data:gip',function(event){
	console.info('gamecenter:data:gip: state = '+nfl.global.gc.game.state);
	/* game is in progress */
	if(nfl.global.gc.game.state === 'PRE'){
		/* whoa, the game is in progress but the user is still viewing 
		 * pre game gamecenter, flip it for them 
		 */
		nfl.global.gc.game.reloaduri	= nfl.global.gc.game.uri+'?random='+nfl.GameCenter.Utility.getCacheBustInt();
		if(window.location.search !== ''){
			if((window.location.search).indexOf('gameState') > -1){
				nfl.global.gc.game.reloaduri+='&gameState=ACTIVE'; /* has game state, so pass in the new correct one */
			}
		}
		console.info('gamecenter:data:gip: isPlayingVideo = '+nfl.GameCenter.VideoPlaylist.isPlayingVideo());
		if(nfl.GameCenter.VideoPlaylist.isPlayingVideo()){
			/* wait until it's done */
			document.observe('videoplayer:playbackComplete',function(){
				window.location = nfl.global.gc.game.reloaduri;
			});
			return false
		}
		window.location = nfl.global.gc.game.reloaduri;
	}
});

document.observe('dom:loaded',function(event){
	/* initialize game center player cards */
	var body				= $$('body')[0];
	body.observe('click',nfl.GameCenter.PlayerCards.onBodyClick);
	/* set ad rotation to elapse so we can pause */
	//nfl.ads.rotation.useElapsed = true;
});
document.observe('gc:tabs:load',function(event){
	nfl.GameCenter.Tabs.initialize(event.memo);
});
