/** utility functions **/
var file = '/15s/.element/ssi/auto/3.0/sdms/leaderboards/'+tId+'/data/current/leaderboard-1-all.json';
var msgFile = '/30s/.element/ssi/auto/3.0/sdms/leaderboards/'+tId+'/data/current/message.json';

var timestamp = '';
var cts = '';
var mcts = '';
var pMap = {};
var pOrderMap = {};
var cMap = {};
var oWGCTournaments = {"r470":"r470", "r473":"r473", "r476":"r476","r489":"r489"};

//initialize the lb
function initLb() {
	cts = getTimeStamp();	//first time through, generate a timestamp based on client time
	mcts = cts;
	
	new Ajax.Request(file, {
			method:'get',
			parameters:'ts='+cts,
			onSuccess: function(transport){
				jsonData = transport.responseText;
				
				//populate these variables for omniture
				json = jsonData.evalJSON();
				
				lb = json.lb;
				lb_id = lb.tc + lb.tid;
				
				lb_year = lb.y;
				lb_tour = lb.tc;
				lb_tournId = lb.tid;
				
				timestamp = lb.ts;
				
				buildPlayerMap(true);
				buildCourseMap();
				determineSortTableHeaderImage();
				
				//remove the sort cookie
				lb_eraseCookie('lb_sort');
				
				var leader = json.lb.pds.p[0];	//leader will be first player in data file
				populateCurrentLeader(leader);
				
				var pIndex = 1;
				var playerString = '';
				
				if(!isInlinePbpOn()) {	//
					lb_createPlayByPlayCookie(false); //turn off play-by-play
				}
				
				/* Start - Leaderboard pop-up video feed code: Sets feed year, and calls the necessary JSON */				
				// Set global variable to check if the leaderboard is initializing
				isLeaderboardInitializing = true;
				var lbYearRegex = new RegExp('^/leaderboards/([0-9]{4})/');
				var thisLbYear = lbYearRegex.exec(window.location.pathname);
				lbVideoFeedYear = '';
				if(thisLbYear != null){
					lbVideoFeedYear = thisLbYear[1];
				}
				else{
					lbVideoFeedYear = lb.y;
				}
				var leaderboardVideoFeedUrl = '/.element/ssi/auto/5.0/aps/multimedia/tournaments/'+tId+'/leaderboard/'+lbVideoFeedYear+'/players-with-videos/player_id_hash.json';
				new Ajax.PeriodicalUpdater("", leaderboardVideoFeedUrl, {
					method:'get',
					frequency:vR,
					decay:1,
					onSuccess: function(transport) {
						eval(transport.responseText);
						// If leaderboard is initializing, run all remaining pre-existing code from the "initLb" function
						if(isLeaderboardInitializing == true){
							isLeaderboardInitializing = false;
							for(var it in pOrderMap) {
								if (pOrderMap.hasOwnProperty(it)) {
									var pDiv = buildPlayerDiv(pMap[pOrderMap[it]], 'lb', pIndex);
									
									populateLeaderboard(pDiv, pIndex, true);
								}
								pIndex++;
							}
							
							populateEmptyMyLb();
							doCookieUpdates();
							
							var refresh = dR*1000;
							setInterval(refreshLb, refresh);
							
							//get message, then setup interval to check for new messages
							populateMessage();
							if(mR != null) {
								setInterval(populateMessage, mR*1000);	//setup message refresh timeframe
							}
							
							//if we are in a playoff round, add table to the DOM
							if(isPlayoff()) {
								buildPlayoffRound();
							}
							
							/*not doing this for launch
							if(aR == 0 || !isEventLive()) {
								clearRefreshIntervals();	//turn off ad and omniture refreshes
							}
							*/
							
							leader = null;
							
							cts = lb.cts;	//get feed cts
						}
					},
					onFailure: function(transport) {
						// If leaderboard is initializing, run all remaining pre-existing code from the "initLb" function
						if(isLeaderboardInitializing == true){
							isLeaderboardInitializing = false;
							for(var it in pOrderMap) {
								if (pOrderMap.hasOwnProperty(it)) {
									var pDiv = buildPlayerDiv(pMap[pOrderMap[it]], 'lb', pIndex);
									
									populateLeaderboard(pDiv, pIndex, true);
								}
								pIndex++;
							}
							
							populateEmptyMyLb();
							doCookieUpdates();
							
							var refresh = dR*1000;
							setInterval(refreshLb, refresh);
							
							//get message, then setup interval to check for new messages
							populateMessage();
							if(mR != null) {
								setInterval(populateMessage, mR*1000);	//setup message refresh timeframe
							}
							
							//if we are in a playoff round, add table to the DOM
							if(isPlayoff()) {
								buildPlayoffRound();
							}
							
							/*not doing this for launch
							if(aR == 0 || !isEventLive()) {
								clearRefreshIntervals();	//turn off ad and omniture refreshes
							}
							*/
							
							leader = null;
							
							cts = lb.cts;	//get feed cts
						}
					}
				});
				/* End - Leaderboard video feed code: Sets feed year, and calls the necessary JSON */
			}
	});
}

//auto-refresh lb
function refreshLb() {
	if(!cts) {
		cts = '';
	}
	
	new Ajax.Request(file, {
			method:'get',
			parameters:'ts='+cts,
			onSuccess: function(transport){
				jsonData = transport.responseText;
				
				//populate these variables for omniture
				json = jsonData.evalJSON();
				
				lb = json.lb;
				
				if(timestamp == lb.ts) {	//if the file has the same timestamp as the previous file, just return
					return;
				}
				
				//store the timestamp for the next refresh
				var newCTS = lb.cts;
				if(newCTS && Number(newCTS) > Number(cts)) {	//let's try to avoid getting an old cache file
					cts = newCTS;
				} else {	//stale data, just return
					return;
				}
				
				//clear out lb divs
				destroyLb(false);
				
				buildPlayerMap(false);
				buildCourseMap();
				
				var leader = json.lb.pds.p[0];
				
				populateCurrentLeader(leader);
				
				var pIndex = 1;
				if(isDefaultSort()) {
					$('tourMyLbPlayersDiv').update();
					for(var it in pOrderMap) {
						if (pOrderMap.hasOwnProperty(it)) {
							var pDiv = buildPlayerDiv(pMap[pOrderMap[it]], 'lb', pIndex);
							
							populateLeaderboard(pDiv, pIndex, true);
						}
						pIndex++;
					}
					doCookieUpdates();
				} else {
					var cookieSortArr = getSortColAndDirectionArray();
					var tOrF = cookieSortArr[1] == "true" ? true : false;	//convert string to boolean
					lbSort(cookieSortArr[0], tOrF);
				}
				
				populateEmptyMyLb();
				
				//if we are in a playoff round, add table to the DOM
				if(isPlayoff()) {
					buildPlayoffRound();
				}
				
				/*not doing this for launch
				if(aR == 0 || !isEventLive()) {
					clearRefreshIntervals();	//turn off ad and omniture refreshes
				}
				*/
			}
	});
}

//return timestamp to append to url as a query var to break file caching
function getTimeStamp() {
	var now = new Date();
	var y = now.getFullYear();
	var mo = now.getMonth()+1;
	var d = now.getDate();
	var h = now.getHours();
	var mi = now.getMinutes();
	var sec = now.getSeconds();
	
	if(mo < 10) {
		mo = '0'+mo;
	}
	
	if(d < 10) {
		d = '0'+d;
	}
	
	if(h < 10) {
		h = '0'+h;
	}
	
	if(mi < 10) {
		mi = '0'+mi;
	}
	
	if(sec < 10) {
		sec = '0'+sec;
	}
	
	return y+mo+d+h+mi+sec;
}

/** remove contents of lb divs **/
function destroyLb(isSort) {
	removeEventHandlers();
	
	if(!isSort) {	//if we're clearing divs for a sort, don't trash the player maps
		pMap = null;
		pOrderMap = null;
	}
	
	if(!isPlayoff()) {	//ensure playoff div is empty if there is no playoff
		$('lbPlayoffContent').update();
	}
	
	$('tourLbTopTenDiv').innerHTML = '';
	$('tourLbElevenToFifty').innerHTML = '';
	$('tourLBFiftyoneToCut').innerHTML = '';
	$('tourLbCutlineContainer').innerHTML = '';
	$('tourLbEndDiv').innerHTML = '';
}

//populate certain lb divs based on index of row
function populateLeaderboard(pDiv, index, defaultSort) {
	if(index <= 10) {	//top 10
		$('tourLbTopTenDiv').insert({bottom: pDiv});
	} else if(index >10 && index <= 50) {	//11 - 50
		$('tourLbElevenToFifty').insert({bottom: pDiv});
	} else if(index > 50 && index <= getTournamentProjCutLine()) {	//51 - cutline
		$('tourLBFiftyoneToCut').insert({bottom: pDiv});
		
		if(index == getTournamentProjCutLine() && defaultSort) {
			$('tourLbCutlineContainer').insert({top: buildCutline()});
			$('tourLbCutLineDiv').style.display = 'inline';
		} else if(index == getTournamentProjCutLine() && !defaultSort) {
			$('tourLbCutLineDiv').style.display = 'none';
		}
	} else {
		$('tourLbEndDiv').insert({bottom: pDiv});	//cutline+1 - end
	}
}

//build quick-lookup hash map of players; player id as key
function buildPlayerMap(isInit) {
	if(!isInit) {
		pMap = {};
		pOrderMap = {};
	}
	var pDataSheet = lb.pds;
	var len = pDataSheet.fs;
	var players = pDataSheet.p;
	
	for(var i=0; i<=len-1; i++) {
		pMap[players[i].id] = players[i];
		pOrderMap[i] = players[i].id;
		if(isInit) {
			playerSearchArray.push([players[i].ln+', '+players[i].fn, players[i].id]);
		}
	}
	
	if(isInit) {
		//alpha sort playerSearchArray
		playerSearchArray.sort();
	}
}

//build quick-lookup hash map of courses; course id as key
function buildCourseMap() {
	var courses = lb.c.c;
	var len = (courses.length == undefined)?1:courses.length;	//if courses.length is undef, len is 1.  otherwise, courses.length
	
	if(len == 1) {
		cMap[courses.id] = courses;
	} else {
		for(var i=0; i<=len-1; i++) {
			cMap[courses[i].id] = courses[i];
		}
	}
}

//borrowed from ShotTracker, thanks ShotTracker
function getURLfromAd(adLocation){
	var adText = "";
	var adURL = "";
	var req = new Ajax.Request(adLocation, {
			method:'get',
			asynchronous: false,
			onSuccess: function(transport){	
				adText = transport.responseText || "";
			},
			onFailure: function(transport){
			},
			onException: function(transport, exception) {
					throw exception;
			}
		}
	);

	while (adText.charAt(adText.length -1) == " ") {
		adText = adText.substring(0,adText.length -1);
	}
	
	if (adText.indexOf("http://") == -1) {
		adText = "http://ads.pgatour.com/html.ng/" + adText;
	} else {
		var startchar = adText.indexOf("http://");
		
		if (startchar != 0) {
			adText = adText.substring(startchar,adText.length);
		}
	}
	return adText;
}

function getAds() {
	switch(tId) {
		case "r476":
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_bridgestone_invitational/top.728x90.ad','top728x90ad',[728,90]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_bridgestone_invitational/mid.668x40.ad','mid668x40ad',[668,40]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_bridgestone_invitational/right.300x250.ad','right300x250ad',[300,250]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_bridgestone_invitational/right.300x600.ad','rgt300x600ad',[300,600]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_bridgestone_invitational/mid3.668x40.ad','mid3668x40ad',[668,40]);
			break;
		case "r473":
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_ca_champ/top.728x90.ad','top728x90ad',[728,90]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_ca_champ/mid.668x40.ad','mid668x40ad',[668,40]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_ca_champ/right.300x250.ad','right300x250ad',[300,250]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_ca_champ/right.300x600.ad','rgt300x600ad',[300,600]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_ca_champ/mid3.668x40.ad','mid3668x40ad',[668,40]);
			break;
		case "r489":
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_hsbc_invitational/top.728x90.ad','top728x90ad',[728,90]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_hsbc_invitational/right.300x250.ad','right300x250ad',[300,250]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_hsbc_invitational/right.300x600.ad','rgt300x600ad',[300,600]);
			break;
		case "r060":
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/top.728x90.ad','top728x90ad',[728,90]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/mid.668x40.ad','mid668x40ad',[668,40]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/right.300x250.ad','right300x250ad',[300,250]);
			break;
		case "r494":
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/top.728x90.ad','top728x90ad',[728,90]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/right.300x250.ad','right300x250ad',[300,250]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/rgt.300x600.ad','rgt300x600ad',[300,600]);
			break;		
		default:
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/top.728x90.ad','top728x90ad',[728,90]);						
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/mid.668x40.ad','mid668x40ad',[668,40]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/right.300x250.ad','right300x250ad',[300,250]);
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/rgt.300x600.ad','rgt300x600ad',[300,600]);			
			acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/mid3.668x40.ad','mid3668x40ad',[668,40]);
	}
	
	/*if(tId == 'r476') {
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_bridgestone_invitational/top.728x90.ad','top728x90ad',[728,90]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_bridgestone_invitational/middle.668x40.ad','mid668x40ad',[668,40]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_bridgestone_invitational/right.300x250.ad','right300x250ad',[300,250]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_bridgestone_invitational/right.300x600.ad','rgt300x600ad',[300,600]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_bridgestone_invitational/mid3.668x40.ad','mid3668x40ad',[668,40]);
	}
	else if(tId == 'r473') {
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_ca_champ/top.728x90.ad','top728x90ad',[728,90]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_ca_champ/middle.668x40.ad','mid668x40ad',[668,40]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_ca_champ/right.300x250.ad','right300x250ad',[300,250]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_ca_champ/right.300x600.ad','rgt300x600ad',[300,600]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_ca_champ/mid3.668x40.ad','mid3668x40ad',[668,40]);
	}
	else if(tId == 'r489') {
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_hsbc_invitational/top.728x90.ad','top728x90ad',[728,90]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_hsbc_invitational/right.300x250.ad','right300x250ad',[300,250]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_hsbc_invitational/right.300x600.ad','rgt300x600ad',[300,600]);
	}
	else if(tId == 'r060') {
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/top.728x90.ad','top728x90ad',[728,90]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/mid.668x40.ad','mid668x40ad',[668,40]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/right.300x250.ad','right300x250ad',[300,250]);
	}
	else if(tId == 'r494') {
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/top.728x90.ad','top728x90ad',[728,90]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/right.300x250.ad','right300x250ad',[300,250]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/rgt.300x600.ad','rgt300x600ad',[300,600]);
	}
	else {
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/top.728x90.ad','top728x90ad',[728,90]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/mid.668x40.ad','mid668x40ad',[668,40]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/rgt.300x600.ad','rgt300x600ad',[300,600]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/right.300x250.ad','right300x250ad',[300,250]);
		acdManager.getInstance().push('/pgatour_adspaces/2.0/live_scoring/pgatour/mid3.668x40.ad','mid3668x40ad',[668,40]);
	}*/
	
	acdManager.getInstance().displayAds();
}

//setup the ad refresh interval
function setAdRefresh() {
	acdManager.getInstance().displayAds();
	/**TODO: turned off for this test
	var refresh = aR*1000;
	adRefreshIntervalId = setInterval(cnnad_refreshAds, refresh);
	
	//refresh tenth and 50th row ads
	if(tId == 'r473') {
		cnnad_setRefreshAdURL('TenthRow', getURLfromAd('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_ca_champ/middle.668x40.ad'));
		tenthRowRefreshId = setInterval(function() {
			cnnad_setRefreshAdURL('TenthRow', getURLfromAd('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_ca_champ/middle.668x40.ad'));
		}, refresh);
		
		cnnad_setRefreshAdURL('FiftiethRow', getURLfromAd('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_ca_champ/mid3.668x40.ad'));
		cutlineRefreshId = setInterval(function() {
			cnnad_setRefreshAdURL('FiftiethRow', getURLfromAd('/pgatour_adspaces/2.0/live_scoring/pgatour/wgc_ca_champ/mid3.668x40.ad'));
		}, refresh);
	} else {
		cnnad_setRefreshAdURL('TenthRow', getURLfromAd('/pgatour_adspaces/2.0/live_scoring/pgatour/mid.668x40.ad'));
		tenthRowRefreshId = setInterval(function() {
			cnnad_setRefreshAdURL('TenthRow', getURLfromAd('/pgatour_adspaces/2.0/live_scoring/pgatour/mid.668x40.ad'));
		}, refresh);
		
		cnnad_setRefreshAdURL('FiftiethRow', getURLfromAd('/pgatour_adspaces/2.0/live_scoring/pgatour/mid3.668x40.ad'));
		cutlineRefreshId = setInterval(function() {
			cnnad_setRefreshAdURL('FiftiethRow', getURLfromAd('/pgatour_adspaces/2.0/live_scoring/pgatour/mid3.668x40.ad'));
		}, refresh);
	}
	*/
	
	/*
	cnnad_setRefreshAdURL('Cutline', getURLfromAd('/pgatour_adspaces/2.0/live_scoring/pgatour/mid2.668x40.ad'));
	cutlineRefreshId = setInterval(function() {
		cnnad_setRefreshAdURL('Cutline', getURLfromAd('/pgatour_adspaces/2.0/live_scoring/pgatour/mid2.668x40.ad'));
	}, refresh);
	*/
}

//setup refresh for the omniture call
function setOmnitureRefresh() {
	var refresh = omR*1000;

	omnitureRefreshIntervalId = setInterval(function() {
	try{
		jsmd.trackMetrics("leaderboard-refresh",pgt_metadata);
	}catch(e){}
	}, refresh);
}

//turn off the ad refresh rates
function clearRefreshIntervals() {
	//turn off ad refreshing
	clearInterval(adRefreshIntervalId);
	clearInterval(tenthRowRefreshId);
	//clearInterval(cutlineRefreshId);
	
	//turn off omniture call
	clearInterval(omnitureRefreshIntervalId);
}

//setup the message refresh interval
function populateMessage() {
	if(!mcts) {
		mcts = '';
	}
	
	new Ajax.Request(msgFile, {
			method:'get',
			parameters:'ts='+mcts,
			onSuccess: function(transport){
				var msgJson = transport.responseText.evalJSON();
				
				var message;
				if(msgJson.msg.content == null) {
					message = '';
				} else {
					message = msgJson.msg.content;
				}
				
				//update <p> with message content
				if(message != '') {
					$('tourLBMessageBox').update(message);
					$('tourLBMessageBox').setStyle({ display: 'block' });
				} else {
					$('tourLBMessageBox').setStyle({ display: 'none' });
				}
				mcts = msgJson.msg.cts;
			},
			onFailure: function(transport){
			}
	});
}

//construct a playoff object and update the div
function buildPlayoffRound() {
	$('lbPlayoffContent').update();	//blank out the div
	var playoff = new Playoff(json.lb.playoff);
	if(playoff.players) {	//only try to build playoff table if there are players in the playoff
		playoff.buildTournamentPlayoffTable();
	}
}

//call the necessary cookie functions to update the leaderboard, shared between lb init and auto-refresh
function doCookieUpdates() {
	//update my lb with cookie info
	lb_generateMyLbFromCookie();
	lb_generateRowHighlightsFromCookie();
	lb_generateExpRowsFromCookie(true);
	
	if(!isInlinePbpOn()) {	//ensure that the play-by-play radio button is set correctly
		toggleInlinePBPRadio(false);
	}
}

//populate leader header area with current leader info
function populateCurrentLeader(leader) {
	$('lbHeadTitle').update(lb.y+' Leaderboard');
	$('lbHeadUpdate').update('Last updated: '+lb.lt);
	if(lb.rn == '401') {	//if there is a playoff, the round number will be 401.  use the totalRounds value instead
		$('lbHeadRound').update('Round '+lb.tr);
	} else {
		$('lbHeadRound').update('Round '+lb.rn);
	}
	$('lbHeadCurrLeader').update('current leader: <span id="lbHeadCurrLeaderName">'+leader.fn+' '+leader.ln+'</span>');
	
	//show tee time if player has not teed off yet
	if(hasTeedOff(leader, isFlagYes('pbp'), getTournamentRoundNumber())) {
		$('lbHeadToday').update('Today:&nbsp;&nbsp;<span class="lbHeadBigFont">'+leader.crs+'</span>');
	} else {
		var teeTime = splitTeeTime(leader.tt.r[leader.rn-1].tt);
		$('lbHeadToday').update('Today:&nbsp;<span class="lbHeadTeeTimeFont">'+teeTime+'</span>');
	}
	
	$('lbHeadTotal').update('Total:&nbsp;&nbsp;<span class="lbHeadBigFont">'+leader.tp+'</span>');
}

//strip off am or pm from end of tee time string
function splitTeeTime(teeTime) {
	return teeTime.slice(0, teeTime.indexOf(' '));
}

function lbClickExpansion_click(playerId, rowType, isPageRefresh, destination) {
	sendToOmniture('Info:View:'+playerId+':'+rowType);
	
	lbClickExpansion(playerId, rowType, isPageRefresh, destination);
}

//show expansion row
function lbClickExpansion(playerId, rowType, isPageRefresh, destination) {
	cancelEventBubble();
	var classNameComponent = (destination == "lb")?"Main":"MyLB";	//switch class name for lb vs. my lb
	
	//make sure player is playing this week before expanding row
	if(!isPlayerInField(playerId)) {
		return;
	}
	
	var expRow = new ExpansionRow('tourLBExpRowOnOff', playerId, destination, rowType);
	expRow.showExpansionRow(destination+'ShowHide', isPageRefresh);
	
	//only call updateClassBorderBottom if inline play by play is turned off
	if($(destination+playerId+'ExpRow').previous().style.display == 'none') {
		updateClassBorderBottom(destination+playerId+'Row', 'tourLB'+classNameComponent+'CellBorderBottom', true);
	}
}

function lbClickExpansionHide_click(playerId, rowType, isPageRefresh, destination) {
	sendToOmniture('Info:Hide:'+playerId);
	
	lbClickExpansionHide(playerId, rowType, isPageRefresh, destination);
}

//remove expansion row
function lbClickExpansionHide(playerId, rowType, isPageRefresh, destination) {
	cancelEventBubble();
	var classNameComponent = (destination == "lb")?"Main":"MyLB";	//switch class name for lb vs. my lb
	
	//only call updateClassBorderBottom if inline play by play is turned off
	if($(destination+playerId+'ExpRow').previous().style.display == 'none') {
		updateClassBorderBottom(destination+playerId+'Row', 'tourLB'+classNameComponent+'CellBorderBottom', false);
	}
	
	var el = $(destination+playerId+'ExpRow');	//element to remove
	el.parentNode.removeChild(el);	//get the parent node of element to remove and then remove element
	
	//change hide to view
	$(destination+'ShowHide'+playerId).innerHTML = '<a onclick="lbClickExpansion_click(\''+playerId+'\', \'SC\', '+isPageRefresh+', \''+destination+'\')">info</a>';
	
	lb_removePlayerExpFromCookie(playerId, rowType, destination+'_exp');
}

function buildSCExpTable_click(playerId, destination, isPageRefresh) {
	sendToOmniture('Info:View:'+playerId+':SC');
	
	buildSCExpTable(playerId, destination, isPageRefresh)
}

//build the Scorecard expansion row table
function buildSCExpTable(playerId, destination, isPageRefresh) {
	cancelEventBubble();
	destroyExpTableTbody(playerId, destination);
	var player = pMap[playerId];
	var course = cMap[player.cid];
	var expBuff = new StringBuffer();
	
	var holeBuff = new StringBuffer();
	var parBuff = new StringBuffer();
	var scoreBuff = new StringBuffer();
	
	var length = player.h.h.length;
	for(var i=0; i<=length-1; i++) {
		var score = player.h.h[i].sc;
		var par = course.h[i].p;
		
		if(i != length-1) {
			//create row with hole numbers
			holeBuff.append('<td>'+player.h.h[i].id+'</td>');
			
			//create row with par for each hole
			parBuff.append('<td>'+par+'</td>');
			
			//create row with player score
			scoreBuff.append('<td class="'+determineScoreColor(score, par)+' bottom">'+score+'</td>');
		} else {	//last cell, append class that does not have a right border
			holeBuff.append('<td class="tourLastCell">'+player.h.h[i].id+'</td>');
			parBuff.append('<td class="tourLastCell">'+par+'</td>');
			scoreBuff.append('<td class="'+determineScoreColor(score, par)+' tourLastCellBot bottom">'+score+'</td>'); //no bottom border
		}
	}
	
	expBuff.append('<tr class="tourLBScorTitle"><td class="tourLBScorTitletd">Hole</td>'+holeBuff.toString()+'</tr>');
	expBuff.append('<tr class="tourLBParRow"><td class="tourLBScorTitletd">Par</td>'+parBuff.toString()+'</tr>');
	expBuff.append('<tr class="tourLastCellBot"><td class="tourLBScorTitletd bottom">R'+player.rn+'</td>'+scoreBuff.toString()+'</tr>');
	
	document.getElementById(destination+playerId+'ExpTbody').update(expBuff.toString());
	
	$(destination+playerId+'ExpTable').className = 'tourLBScoreTbl';
	$(destination+playerId+'SC').className += ' LBExpRowactive';

	if(!isPageRefresh) {
		lb_addPlayerExpToCookie(playerId, 'SC', destination+'_exp');
	}
	
	document.getElementById(destination+'ShowHide'+playerId).innerHTML = '<a class="pointer" onclick="lbClickExpansionHide_click(\''+playerId+'\',\'SC\', '+false+', \''+destination+'\')">hide</a>';
	document.getElementById(destination+'ExpRowClose'+playerId).innerHTML = '<div class="tourLBExpRowClose" onclick="lbClickExpansionHide_click(\''+playerId+'\', \'SC\', '+false+', \''+destination+'\')">x</div>';
}

function buildPPExpTable_click(playerId, destination, isPageRefresh) {
	sendToOmniture('Info:View:'+playerId+':PP');
	
	buildPPExpTable(playerId, destination, isPageRefresh);
}

//build the Play-by-Play expansion row table
function buildPPExpTable(playerId, destination, isPageRefresh) {
	cancelEventBubble();
	destroyExpTableTbody(playerId, destination);
	
	var player = pMap[playerId];
	var pbpArray = getPlayerPBP(player);
	var expBuff = new StringBuffer();
	
	var pbpDataString = buildPBPDataCell(pbpArray)
	expBuff.append('<tr><td class="tourLBExpPPHole" id="'+destination+playerId+'PBPHead"></td>');
	expBuff.append(pbpDataString+'</tr>');
	
	document.getElementById(destination+playerId+'ExpTbody').update(expBuff.toString());
	
	$(destination+playerId+'ExpTable').className = 'tourLBExpPbP';
	
	if(pbpDataString == '') {	//has not teed off yet
		var currHole = '';
		if(player.ch == '') {	//player is on first hole, which could also be 10 if they start on the back nine
			currHole = player.sh;
		} else {
			currHole = player.ch;
		}
	
		$(destination+playerId+'PBPHead').update('<p>Waiting to tee off on hole '+currHole+'</p>');
	} else {	//at least one shot to display
		var holeData = getHoleData(player.cid, pbpArray[0]);
		$(destination+playerId+'PBPHead').update('<p>'+getHoleSuffix(pbpArray[0])+' Hole</p><p>Par '+holeData[0]+'</p><p>'+holeData[1]+' yards</p>');
	}
	
	$(destination+playerId+'PP').className += ' LBExpRowactive';
	
	if(!isPageRefresh) {
		lb_addPlayerExpToCookie(playerId, 'PP', destination+'_exp');
	}
	
	document.getElementById(destination+'ShowHide'+playerId).innerHTML = '<a class="pointer" onclick="lbClickExpansionHide_click(\''+playerId+'\',\'PP\', '+false+', \''+destination+'\')">hide</a>';
	document.getElementById(destination+'ExpRowClose'+playerId).innerHTML = '<div class="tourLBExpRowClose" onclick="lbClickExpansionHide_click(\''+playerId+'\', \'PP\', '+false+', \''+destination+'\')">x</div>';
}

//build cells for the latest 8 play-by-play shots
function buildPBPDataCell(pbpArr) {
	var cloneArr = pbpArr.clone();
	var pbpBuff = new StringBuffer();
	
	//remove the first array item, which is the hole number
	cloneArr.shift();
	
	//get 8 most recent shots
	if((cloneArr.length)/4 > 2) {
		for(; cloneArr.length>8; ){
			cloneArr.shift();
		}
	}
	
	//length of shot array
	var length = cloneArr.length;
	
	if(length == 0) {
		return '';
	}
	
	//create 2 cols of pbp, as needed
	var colCheck = 0;
	for(var j=0; j<=1; j++) {
		pbpBuff.append('<td class="tourLBExpPPShotList"><ul>');
		if(colCheck+4 > length-1) {
			for(var i=colCheck; i<=length-1; i++) {
				pbpBuff.append('<li title="Shot '+cloneArr[i][0]+': '+cloneArr[i][1]+'">'+cloneArr[i][0]+'. '+cloneArr[i][1]+'</li>');
			}
		} else {
			for(i=colCheck; i<=colCheck+3; i++) {
				pbpBuff.append('<li title="Shot '+cloneArr[i][0]+': '+cloneArr[i][1]+'">'+cloneArr[i][0]+'. '+cloneArr[i][1].truncate(45)+'</li>');
			}
		}
		colCheck += 4;
		
		pbpBuff.append('</ul></td>');
	}
	cloneArr = null;
	return pbpBuff.toString();
}

//return an array of play-by-play data for a passed-in player
function getPlayerPBP(player) {
	var numHoles = cMap[player.cid].h.length;
	var pbpArray = [];
	
	//loop over holes
	for(var i=0; i<=numHoles-1; i++) {
		if(player.h.h[i].sh != null) {
			pbpArray.push(player.h.h[i].id);
			
			var length = player.h.h[i].sh.length;
			//length will be undef or null if there is not an array of shots (for example: only 1 shot detail so far)
			if(length != null) {
				for(var j=0; j<=length-1; j++) {
					pbpArray.push([player.h.h[i].sh[j].id, player.h.h[i].sh[j].content]);
				}
			} else {
				pbpArray.push([player.h.h[i].sh.id, player.h.h[i].sh.content]);
			}
			
			return pbpArray;
		}
	}
	return pbpArray;	//return empty array
}

function buildSTExpTable_click(playerId, destination, isPageRefresh) {
	sendToOmniture('Info:View:'+playerId+':ST');

	buildSTExpTable(playerId, destination, isPageRefresh);
}

//build the Stats expansion row table  [PRE-10599]
function buildSTExpTable(playerId, destination, isPageRefresh) {
	cancelEventBubble();
	destroyExpTableTbody(playerId, destination);
	var player = pMap[playerId];
	
	var expBuff = new StringBuffer();
	expBuff.append('<tr><td>'+buildStatsDataTable(player, 0)+'</td><td>'+buildStatsDataTable(player, 3)+'</td><td>'+buildStatsDataTable(player, 6)+'</td></tr>');
	document.getElementById(destination+playerId+'ExpTbody').update(expBuff.toString());
	
	$(destination+playerId+'ExpTable').className = 'tourLBExpPlyrStat';
	$(destination+playerId+'ST').className += ' LBExpRowactive';
	
	if(!isPageRefresh) {
		lb_addPlayerExpToCookie(playerId, 'ST', destination+'_exp');
	}
	
	document.getElementById(destination+'ShowHide'+playerId).innerHTML = '<a class="pointer" onclick="lbClickExpansionHide_click(\''+playerId+'\',\'ST\', '+false+', \''+destination+'\')">hide</a>';
	document.getElementById(destination+'ExpRowClose'+playerId).innerHTML = '<div class="tourLBExpRowClose" onclick="lbClickExpansionHide_click(\''+playerId+'\', \'ST\', '+false+', \''+destination+'\')">x</div>';
}

function buildStatsDataTable (player, startIndex) {
	cancelEventBubble();
	var statsBuff = new StringBuffer();
	
	statsBuff.append('<table class="tourLBExpStat" cellpadding="0" cellspacing="0" border="0"><tbody>');
	
	for (var i=startIndex; i<startIndex+3; i++) {		
		if(typeof player.stat.s[i] != 'undefined'){			
			statsBuff.append('<tr><td class="b">'+getStatName(player.stat.s[i].id)+'</td>');
			statsBuff.append('<td class="value">');
			if(player.stat.s[i].v == '') {
				statsBuff.append('--');
			} else {
				statsBuff.append(player.stat.s[i].v);
			}
			statsBuff.append('</td></tr>');
		}
		else{
			statsBuff.append('<tr><td class="b">&nbsp;</td><td class="value">&nbsp;</td></tr>');
		}	
	}
	
	statsBuff.append('</tbody></table>');
	
	return statsBuff.toString();
}

//build the Stats expansion row table
/*function buildSTExpTable(playerId, destination, isPageRefresh) {
	cancelEventBubble();
	destroyExpTableTbody(playerId, destination);
	var player = pMap[playerId];
	
	var expBuff = new StringBuffer();
	expBuff.append('<tr><td>'+buildStatsDataTable(player, 0)+'</td><td>'+buildStatsDataTable(player, 3)+'</td>'+newStatColumn(player)+'</tr>');
	
	document.getElementById(destination+playerId+'ExpTbody').update(expBuff.toString());
	
	$(destination+playerId+'ExpTable').className = 'tourLBExpPlyrStat';
	$(destination+playerId+'ST').className += ' LBExpRowactive';
	
	if(!isPageRefresh) {
		lb_addPlayerExpToCookie(playerId, 'ST', destination+'_exp');
	}
	
	document.getElementById(destination+'ShowHide'+playerId).innerHTML = '<a class="pointer" onclick="lbClickExpansionHide_click(\''+playerId+'\',\'ST\', '+false+', \''+destination+'\')">hide</a>';
	document.getElementById(destination+'ExpRowClose'+playerId).innerHTML = '<div class="tourLBExpRowClose" onclick="lbClickExpansionHide_click(\''+playerId+'\', \'ST\', '+false+', \''+destination+'\')">x</div>';
}

function newStatColumn(player){//T10599: Used to add 7th stat to third Col.
//If 7th stat exists, 8th & 9th do not exist, and 4th (Strokes Gained) is not blank
try{
	if(player.stat.s.length > 6){
		return '<td>'+buildStatsDataTable(player, 6, 1)+'</td>';
	}
	else{
		return '';	
	}
	} catch(e){}//catch	
}

function buildStatsDataTable (player, startIndex, isThirdCol) {
	try{
	cancelEventBubble();
	var statsBuff = new StringBuffer();
	var statCount = 0;
	
	statsBuff.append('<table class="tourLBExpStat" cellpadding="0" cellspacing="0" border="0"><tbody>');
	
	if(isThirdCol == 1){
		statCount = 1;
	}
	else{
		statCount = 3;	
	}
		
	for (var i=startIndex; i<startIndex+statCount; i++) {
		if(player.stat.s[i].id == '02564'){
			if(player.stat.s[i].v == '') {
				statsBuff.append('<tr id="strokesRow'+player.id+'" style="display:none;"><td class="b">'+getStatName(player.stat.s[i].id)+'</td>');
			}
			else{
				statsBuff.append('<tr id="strokesRow'+player.id+'"><td class="b">'+getStatName(player.stat.s[i].id)+'</td>');
			}
			statsBuff.append('<td id="strokesValue'+player.id+'" class="value">');
		}
		else{
			statsBuff.append('<tr><td class="b">'+getStatName(player.stat.s[i].id)+'</td>');
			statsBuff.append('<td class="value">');
		}
			if(player.stat.s[i].v == '') {
				statsBuff.append('--');
				if(player.stat.s[i].id == '02564'){
					statCount++;	
				}
			} else {
				statsBuff.append(player.stat.s[i].v);
			}
			statsBuff.append('</td></tr>');
	}
	if(isThirdCol == 1){//Add static rows for 7th col. Occurs only when 7th row is Sand Saves, no data for the 8th and 9th rows exist, and Strokes Gained is not blank
		statsBuff.append('<tr><td class="b">&nbsp;</td></tr><tr><td class="b">&nbsp;</td></tr>');
	}
	
	statsBuff.append('</tbody></table>');
	
	return statsBuff.toString();
	} catch(e){}//catch	
}*/

function buildFCExpTable_click(playerId, destination, isPageRefresh) {
	//TODO: track omniture here?
	
	buildFCExpTable(playerId, destination, isPageRefresh);
}

//build the FEC expansion row table
function buildFCExpTable(playerId, destination, isPageRefresh) {
	cancelEventBubble();
	destroyExpTableTbody(playerId, destination);
	
	var player = pMap[playerId];
	var expBuff = new StringBuffer();
	
	expBuff.append('<tr class="tourFECHeaderRow"><td class="tourFECRank">Rank</td><td class="tourFECPoints">Points</td><td class="tourFECPtsBehind">Points Behind 1st</td><td class="tourFECProjRank">Projected Rank</td><td class="tourFECProjPoints">Projected Points</td><td class="tourFECProjEvent tourLastCell">Projected Event Points</td></tr>');
	expBuff.append('<tr class="tourFECDataRow">');
	expBuff.append('<td>'+player.fcr+'</td>');
	expBuff.append('<td>'+player.fcp+'</td>');
	expBuff.append('<td>'+player.fct+'</td>');
	expBuff.append('<td class="tourLBFECProj">'+player.pfcr+'</td>');
	expBuff.append('<td class="tourLBFECProj">'+player.pfcp+'</td>');
	expBuff.append('<td class="tourLBFECProj tourLastCell">'+player.pfct+'</td>');
	expBuff.append('</tr>');
	
	document.getElementById(destination+playerId+'ExpTbody').update(expBuff.toString());
	
	$(destination+playerId+'ExpTable').className = 'tourLBFECTbl';
	$(destination+playerId+'FC').className += ' LBExpRowactive';
	
	if(!isPageRefresh) {
		lb_addPlayerExpToCookie(playerId, 'FC', destination+'_exp');
	}
	
	//update hide and close links so that the cookie will be updated properly upon close
	document.getElementById(destination+'ShowHide'+playerId).innerHTML = '<a class="pointer" onclick="lbClickExpansionHide_click(\''+playerId+'\',\'FC\', '+false+', \''+destination+'\')">hide</a>';
	document.getElementById(destination+'ExpRowClose'+playerId).innerHTML = '<div class="tourLBExpRowClose" onclick="lbClickExpansionHide_click(\''+playerId+'\', \'FC\', '+false+', \''+destination+'\')">x</div>';
}

//clean up the tbody in the expansion row table so that new data can be appended
function destroyExpTableTbody(playerId, destination) {
	cancelEventBubble();
	//prototype function to update an element's content.  works in ie, innerHTML does not for tbody elements
	$(destination+playerId+'ExpTbody').update();
	if(isFlagYes('sc')) {
		$(destination+playerId+'SC').className = 'tourLBExpRowItem';
	}
	
	if(isFlagYes('pbp')) {
		$(destination+playerId+'PP').className = 'tourLBExpRowItem';
	}
	
	if(isFlagYes('stat')) {
		$(destination+playerId+'ST').className = 'tourLBExpRowItem';
	}
	
	if(getTournamentIsFEC()) {
		$(destination+playerId+'FC').className = 'tourLBExpRowItem';
	}
}

//process movement string
function getMovementOutputString(movement) {
	if(movement == 0) {
		return '&nbsp;';
	} else if(movement.indexOf('-') >= 0) {
		//strip minus sign
		return movement.substr(1, movement.length);
	} //default, return original movement string.
	return movement;
}

//return arrow up or arrow down image filename
function determineMovementArrow(movement, destination) {
	if(movement == 0) {
		return null;
	} else if (movement.indexOf('-') >= 0) {
		return destination+'_dwn_arrow.gif';
	}
	return destination+'_up_arrow.gif';
}

function colorRow_click(playerId, colorIndex, isPageRefresh) {
	sendToOmniture('Color:'+colorIndex+':'+playerId);
	
	colorRow(playerId, colorIndex, isPageRefresh);
}

//rotate through a set number of colors for a given row
function colorRow(playerId, colorIndex, isPageRefresh) {
	cancelEventBubble();
	
	//make sure player is playing this week before highlighting row
	if(!isPlayerInField(playerId)) {
		return;
	}
	
	var nextColor = parseInt(colorIndex)+1;
	
	doHighlightOperations(playerId, colorIndex);
	
	//if you are at last color in cycle, reset things to initial classes
	if(colorIndex == 0) {
		var index = $(playerId+'lbTable').tBodies[0].id.replace(/lbPlayer/, '');
		
		if(index%2 == 0) {
			doResetHighlightOperations(playerId, 'tourLBMainAlt');
		} else {
			doResetHighlightOperations(playerId, 'tourLBMainDefault');
		}
	}
	
	if(!isPageRefresh) {
		lb_addPlayerHighlightComboToCookie(playerId, colorIndex);
	}
}

//common functionality for colorRow and highlightFromMSCookie
function doHighlightOperations(playerId, colorIndex) {
	var playerHighlight = $(playerId+'lbDiv');
	playerHighlight.className = 'tourLBColor'+colorIndex;
	
	var nextColor = parseInt(colorIndex)+1;
	
	//set linkable text to 'none' if you are at the last color in the cycle
	if(nextColor > 4) {	//last color
		$('nextColorlb'+playerId).update('<a onclick="colorRow_click(\''+playerId+'\', '+0+', '+false+')">none</a>');
	} else if(nextColor == 1) {	//first color
		$('nextColorlb'+playerId).update('<a onclick="colorRow_click(\''+playerId+'\', '+nextColor+', '+false+')">color</a>');
	} else {	//other colors
		$('nextColorlb'+playerId).update('<a onclick="colorRow_click(\''+playerId+'\', '+nextColor+', '+false+')">next</a>');
	}
}

//resets row classes to default after last color in next color cycle
function doResetHighlightOperations(playerId, className) {
	var playerHighlight = $(playerId+'lbDiv');
	playerHighlight.className = className;
}

function pinPlayer_click(playerId, isPageRefresh) {
	sendToOmniture('Player Pin:'+playerId+':On');
	
	pinPlayer(playerId, isPageRefresh);
}

//add a player to my leaderboard
function pinPlayer(playerId, isPageRefresh) {
	cancelEventBubble();
	
	//make sure player is playing this week before pinning
	if(!isPlayerInField(playerId)) {
		return;
	}
	
	//if already pinned, return
	if(isPinned(playerId)) {
		return;
	}
	
	lb_addPlayerToCookie(playerId);
	
	myLbSort();	//sort and pin players
	lb_generateExpRowsFromCookie(false);
}

function unPinPlayer_click(playerId) {
	sendToOmniture('Player Pin:'+playerId+':Off');
	
	unPinPlayer(playerId);
}

//remove a player from my leaderboard
function unPinPlayer(playerId) {
	cancelEventBubble();
	
	var myLbDiv = $('tourMyLbPlayersDiv');
	var myLbTbody = $('myLb_tbody');
	
	var el = $(playerId+'myLbDiv');	//element to remove
	el.parentNode.removeChild(el);	//get the parent node of element to remove and then remove element
	
	$('lbPin'+playerId).innerHTML = '<a onclick="pinPlayer_click(\''+playerId+'\', '+false+')"><img src="http://i.cdn.turner.com/pgatour/.element/img/4.0/sect/leaderboard/plus_pin.gif" /></a>';
	
	alternateRowColor(myLbTbody);
	//remove from cookie
	lb_removePlayerFromCookie(playerId);
	
	//show default text for an empty my lb
	if(isMyLbEmpty()) {
		populateEmptyMyLb();
	} else {
		myLbSort();
	}
	
	lb_generateExpRowsFromCookie(false);
	
	myLbDiv = null;
	myLbTbody = null;
	el = null;
}

/** post sort, ensure that pinned players icons are correct (-) in full lb*/
function updatePinImage() {
	var pArray = getPinnedPlayers();
	var len = pArray.length;
	
	for(var i=0; i<=len-1; i++) {
		var pId = pArray[i];
		if(isPlayerInField(pId)) {
			$('lbPin'+pId).innerHTML = '<a onclick="unPinPlayer_click(\''+pId+'\')"><img src="http://i.cdn.turner.com/pgatour/.element/img/4.0/sect/leaderboard/minus_Main_pin.gif" /></a>';
		}
	}
}

//return true if empty, false otherwise
function isMyLbEmpty() {
	var pTables = $('tourMyLbPlayersDiv').getElementsByTagName('table');
	
	if(pTables.length <= 0) {
		return true;
	} else if(pTables.length == 1 && pTables[0].id =='tourMyLbDefault') {	//this is the case where the default text is being shown, count that as empty
		return true;
	}
	pTables = null;
	
	return false;
}

//insert a row and td with some default text if My Lb is empty
function populateEmptyMyLb() {
	cancelEventBubble();
	if(isMyLbEmpty()) {
		var myLbBuff = new StringBuffer();
		
		myLbBuff.append('<table id="tourMyLbDefault"><tr class="tourMyLBZeroLenText"><td colspan="30">Track your favorite players by adding them to My Leaderboard</td></tr></table>');
		$('tourMyLbPlayersDiv').innerHTML = myLbBuff.toString();
	}
}

//
function getPinnedPlayers() {
	var players = lb_getCookie('lb_myLb');
	var pArray = [];
	
	if(!players) { //no cookie previously et
		return pArray;
	}
	
	pArray = players.split(',');
	return pArray;
}

//determine if a player is pinned from the cookie
function isPinned(playerId) {
	var current = lb_getCookie('lb_myLb');
	if(!current) { //no cookie previously set
		return false;
	}
	var pArray = current.split(',');
	
	if(pArray.indexOf(playerId) >= 0) {
		return true;
	}
	return false;
}

//alternates the color of the rows.  used when players are pinned/unpinned to update alternating row colors
function alternateRowColor() {
	cancelEventBubble();
	var count = 0;
	
	var pDivs = $('tourMyLbPlayersDiv').childNodes;
	var len = pDivs.length;
	
	for(var i=0; i<=len-1; i++) {
		if(count%2 != 0) {
			pDivs[i].className = "tourLBMyLBAlt";
		} else {
			pDivs[i].className = "tourLBMyLBDefault";
		}
		count++;
	}
	
	pDivs = null;
}

//returns true if a player is playing this week, false otherwise
function isPlayerInField(playerId) {
	if(pMap[playerId] == null) {
		return false;
	} else {
		return true;
	}
}

//determine if the cookie contains the default sort
function isDefaultSort() {
	var current = lb_getCookie('lb_sort');
	if(!current) {	//no cookie set, defauult sort is true
		return true;
	}
	
	var sortArray = current.split('|');
	
	if(sortArray[0] == 'p' && sortArray[1] == 'true') {
		return true;
	}
	return false;
}

//get the sort col and direction (ascending/descending) from the sort cookie
function getSortColAndDirectionArray() {
	var current = lb_getCookie('lb_sort');
	if(!current) {	//no cookie set, return default sort
		return ['p', 'true'];
	}
	
	return current.split('|');
}

//return array of player objects that are pinned based on the cookie
function getMyLbPlayersArray(sortCol) {
	var myLbPlayers = lb_getCookie('lb_myLb');
	
	//no players pinned
	if(!myLbPlayers) {
		return;
	} else {
		//build array of player ids from cookie
		var myPlayerIds = myLbPlayers.split(',');	//players are delimited by a comma
		var myPlayers = [];
		
		//build array player ids + sort value
		for(var i=0; i<=myPlayerIds.length-1; i++) {
			if(isPlayerInField(myPlayerIds[i])) {	//only put players that are in the field into the array
				if(sortCol == 'strokes') {	//this is really ugly and i hate it
					myPlayers.push([myPlayerIds[i], pMap[myPlayerIds[i]]['rs']['t'], pMap[myPlayerIds[i]]['s']]);
				} else if (/[r][1-6]/.test(sortCol)) {
					myPlayers.push([myPlayerIds[i], pMap[myPlayerIds[i]]['rs']['r'][parseInt(sortCol.substring(1))-1]['sc']]);
				} else {
					myPlayers.push([myPlayerIds[i], pMap[myPlayerIds[i]][sortCol]]);
				}
			}
		}
		return myPlayers;
	}
}

//returns an array with playerId and sort column
function buildSortArray(sortCol) {
	var sortArray = [];
	
	switch (sort_pref){	//hate this switch.  would love to find a better solution.
		case('strokes'):
			for (var it in pOrderMap) {
				if (pOrderMap.hasOwnProperty(it)) {
					var pId = pOrderMap[it];
					sortArray.push([pMap[pId].id, pMap[pId].rs.t, pMap[pId].s]);	//add player status so we can sort non-active players to the bottom
				}
			}
			break;
		case('r1'):
		case('r2'):
		case('r3'):
		case('r4'):
		case('r5'):
		case('r6'):
			for (var it in pOrderMap) {
				if (pOrderMap.hasOwnProperty(it)) {
					var pId = pOrderMap[it];
					sortArray.push([pMap[pId].id, pMap[pId]['rs']['r'][parseInt(sortCol.substring(1))-1]['sc']]);
				}
			}
			break;
		default: 
			for (var it in pOrderMap) {
				if (pOrderMap.hasOwnProperty(it)) {
					var pId = pOrderMap[it];
					sortArray.push([pMap[pId].id, pMap[pId][sortCol]]);
				}
			}
			break;
	}
	return(sortArray);
}

//sort lb cols
function lbSort(col, sortUp, player) {
	cancelEventBubble();
	
	sort_pref = col;
	sort_up = sortUp;
	player_sort = player;
	
	//write out cookie
	if(!player) {
		lb_addSortToCookie(col, sortUp, null);
	} else {
		lb_addSortToCookie(col, sortUp, player);
	}
	
	var sortArray = buildSortArray(sort_pref);
	doSortOperations(sortArray, 'tourLbPlayersDiv', 'lb', sort_up );
	
	resetTableHeaders();
	
	//update link to sort this col the other way (ascend/descend)
	if(sort_pref == 'fn' && sort_up == true) {	//awesome 4-way player sort (fName up, fName down, lName up, lName down)
		updateSortArrow(sort_pref, sort_up, 'fn');
		$('tourLB_fn').update('<span class="lbPNameSortType">First Name</span>');
	} else if(sort_pref == 'fn' && sort_up == false) {
		updateSortArrow(sort_pref, sort_up, 'ln');
		$('tourLB_fn').update('<span class="lbPNameSortType">First Name</span>');
	} else if (sort_pref == 'ln' && sort_up == true) {
		updateSortArrow(sort_pref, sort_up, 'ln');
		$('tourLB_fn').update('<span class="lbPNameSortType">Last Name</span>');
	} else if (sort_pref == 'ln' && sort_up == false) {
		updateSortArrow(sort_pref, sort_up, 'fn');
		$('tourLB_fn').update('<span class="lbPNameSortType">Last Name</span>');
	} else {	//or every other column
		updateSortArrow(sort_pref, sort_up, null);
	}
	
	if(!isMyLbEmpty()) {	//only attempt to sort my lb if there are players pinned
		sortArray = getMyLbPlayersArray(sort_pref);
		doSortOperations(sortArray, 'tourMyLbPlayersDiv', 'myLb', sort_up);
	}
	
	//do cookie updates
	lb_generateRowHighlightsFromCookie();
	lb_generateExpRowsFromCookie(true);
	
	updatePinImage();
}

//sort the My Leaderboard section
function myLbSort() {
	var cookieSortArr = getSortColAndDirectionArray();
	var tOrF = cookieSortArr[1] == "true" ? true : false;	//convert string to boolean
	
	sort_pref = cookieSortArr[0];
	var sortArray = getMyLbPlayersArray(sort_pref);
	
	sortArray.sort(sortColumns);
	if(!tOrF) {
		sortArray.reverse();
	}
	
	var el = $('tourMyLbPlayersDiv');
	el.update();	//clear out div
	
	var len = sortArray.length;
	for(var i=0; i<=len-1; i++) {
		var pId = sortArray[i][0];
		var pDiv = buildPlayerDiv(pMap[pId], 'myLb', i+1);
		Element.insert(el, {'bottom':pDiv});
		
		$('lbPin'+pId).innerHTML = '<a onclick="unPinPlayer_click(\''+pId+'\')"><img src="http://i.cdn.turner.com/pgatour/.element/img/4.0/sect/leaderboard/minus_Main_pin.gif" /></a>';
	}
	
	el = null;
}

//sort operations that are shared between the full lb and my lb
function doSortOperations(arrayToSort, div, destination, up) {
	arrayToSort.sort(sortColumns);
	if(!up) {	//reverse sort
		arrayToSort.reverse();
	}
	var len = arrayToSort.length;
	
	if(destination == 'lb') {
		destroyLb(true);
		
		for(var i=0; i<=len-1; i++) {
			var pDiv = buildPlayerDiv(pMap[arrayToSort[i][0]], destination, i+1);
			populateLeaderboard(pDiv, i+1, isDefaultSort());
		}
	} else {
		var el = $(div);
		el.update();	//clear out div
		
		for(var i=0; i<=len-1; i++) {
			var pDiv = buildPlayerDiv(pMap[arrayToSort[i][0]], destination, i+1);
			Element.insert(el, {'bottom':pDiv});
		}
	}
	
	return;
}

//swap between ascending/descending arrow and onclick boolean
function updateSortArrow(sortPref, sortUp, playerSort) {
	var el = '';
	if(playerSort == null) {
		el = $('tourLB_'+sortPref);
		el.onclick = function(){ lbSort(sortPref, !sortUp); }
	} else {
		el = $('tourLB_fn');
		el.onclick = function(){ lbSort(playerSort, !sortUp); }
	}
	
	el.className = 'sortcol';
	//determine which orange arrow to show based on ascending or descending sort
	if(sort_up) {
		el.className += ' sortdesc';
	} else {
		el.className += ' sortasc';
	}
	
	el = null;
}

//reset sort to descending and update arrow to white
function resetSortArrow(sortPref, playerSort) {
	$('tourLB_'+sortPref).className = 'sortcol';
	$('tourLB_'+sortPref).onclick = function(){ lbSort(sortPref, true); }
}

//compares actual values for sort, switch on col id
function sortColumns(a,b) {
	switch (sort_pref){
		case('p'):
			//alphanum - number or 'F'
			aa=(a[1]).replace(/T/,'');
			bb=(b[1]).replace(/T/,'');
			if (isNaN(aa) || a[1]=='') {
				if(aa =='mdf') { aa=1000; }	//sort should be mdf > cut > wd
				else if(aa == 'cut') { aa=2000; }
				else { aa=3000; }
			}
			if (isNaN(bb) || b[1]=='') {
				if(bb=='mdf') { bb=1000; }
				else if(bb=='cut') { bb=2000; }
				else { bb=3000; }
			}
			return parseFloat(aa) - parseFloat(bb);
		case('m'):
			return b[1]-a[1];
		case('pfcr'):
			aa=(a[1]).replace(/T/,'');	//ties cause sort problems, remove the T
			bb=(b[1]).replace(/T/,'');
			if(isNaN(aa)) { aa = 1000; }
			if(isNaN(bb)) { bb = 1000; }
			return parseInt(aa)-parseInt(bb);
		case('f'):
			if (a[1]<b[1]) { return -1; }
			if (a[1]>b[1]) { return 1; }
			return 0;  //return zero to avoid js error in IE for strings that match
		case('fn'):	//player name sorts, same for both first and last name cases
		case('ln'):
			if (a[1]<b[1]) { return -1; }
			if (a[1]>b[1]) { return 1; }
			return 0;
		case('tp'):
		case('crs'):
			aa=(a[1]).replace(/E/,'0');
			bb=(b[1]).replace(/E/,'0');
			if (isNaN(aa) || a[1]=='') { aa=1000; }//bottom
			if (isNaN(bb) || b[1]=='') { bb=1000; }
			return parseFloat(aa) - parseFloat(bb);
		case('t'):
			aa=(a[1]).replace(/F/,'1000');
			aa=aa.replace(/\*/,'');
			bb=(b[1]).replace(/F/,'1000');
			bb=bb.replace(/\*/,'');
			if (isNaN(aa) || a[1]=='') { aa=-1000; }//bottom
			if (isNaN(bb) || b[1]=='') { bb=-1000; }
			return parseFloat(bb) - parseFloat(aa);
		case('r1'):
		case('r2'):
		case('r3'):
		case('r4'):
		case('r5'):
		case('r6'):
			aa=Number(a[1]);
			if (isNaN(aa) || a[1]=='' || a[1]==0) { aa=1000; }//bottom
			bb=Number(b[1]);
			if (isNaN(bb) || b[1]=='' || b[1]==0) { bb=1000; }
			
			return aa-bb;
		case('strokes'):
			aa = Number(a[1]);
			if(isNaN(aa) || a[2]!='A') { aa=1000; }
			bb = Number(b[1]);
			if(isNaN(bb) || b[2]!='A') { bb=1000; }
			
			return aa-bb;
		default:
			break;
	}
}

//resets sort cols to default values
function resetTableHeaders() {
	resetSortArrow('p', null);
	resetSortArrow('m', null);
	resetSortArrow('pfcr', null);
	resetSortArrow('f', null);
	resetSortArrow('tp', null);
	resetSortArrow('t', null);
	resetSortArrow('crs', null);
	
	//this will handle variable number of rounds
	var totalRnds = getTournamentTotalRounds();
	for(var i=1; i<=totalRnds; i++) {
		resetSortArrow('r'+i, null);
	}
	
	resetSortArrow('strokes', null);
	//special condition for player col since it has the awesome 4-way sort feature
	resetSortArrow('fn', null);
	$('tourLB_fn').update();
}

function showHidePBP_click(show) {
	sendToOmniture('Inline Play-by-Play:'+show);
	
	showHidePBP(show);
}

//show or hide the inline play-by-play rows
function showHidePBP(show) {
	//fill an array with all the inline play by play rows in my lb
	var elementArray = $('tourMyLbPlayersDiv').select('tr.tourPlayByPlayRow');
	//show or hide each
	showHideElements(elementArray, show, 'MyLB');
	
	//fill an array with all the inline play by play rows in the main lb
	elementArray = $('tourLbPlayersDiv').select('tr.tourPlayByPlayRow');
	showHideElements(elementArray, show, 'Main');
	
	//create or destroy the inline play by play cookie
	if(show) {
		lb_createPlayByPlayCookie('true');
	} else {
		lb_createPlayByPlayCookie('false');
	}
	
	toggleInlinePBPRadio(show);
	
	elementArray = null;
}


//toggle the radio button for inline pbp. useful after checking cookie for pbp setting
function toggleInlinePBPRadio(show) {
	$('inline_pbpOn').checked = show;
	$('inline_pbpOff').checked = !show;
}

//show or hide an array of DOM elements
function showHideElements(elementArray, show, destination) {
	if(show) {
		for(var i=0; i<=elementArray.length-1; i++) {
			elementArray[i].setStyle({ display: '' });
			updateClassBorderBottom(elementArray[i].previous().id, 'tourLB'+destination+'CellBorderBottom', true);
		}
	} else {
		for(var i=0; i<=elementArray.length-1; i++) {
			elementArray[i].setStyle({ display: 'none' });
			if(elementArray[i].next()) {
				if(elementArray[i].next().className.indexOf('tourLBExpRowOnOff') < 0) {
					updateClassBorderBottom(elementArray[i].previous().id, 'tourLB'+destination+'CellBorderBottom', false);
				}
			} else {
				updateClassBorderBottom(elementArray[i].previous().id, 'tourLB'+destination+'CellBorderBottom', false);
			}
		}
	}
}

//add function for auto-complete player search
function autocompleterAdd() {
	var pId = $('playerSearchDD').value;
	
	//if not already pinned, add a row and add player to the cookie
	if(!isPinned(pId)) {
		pinPlayer(pId, false);
	}

	//select the first entry in the drop-down, default value
	$('playerSearchDD').selectedIndex = 0;
	
	sendToOmniture('Player Pin Dropdown:'+pId);
	
	pId = null;
}

//return an array of hole data (yardage and par)
function getHoleData(cId, holeId) {
	var holeData = [];
	var course = cMap[cId];
	
	//holeId-1 because array indexes start at 0
	holeData.push(course.h[holeId-1].p);	//par
	holeData.push(course.h[holeId-1].d);	//yards
	return(holeData);
}

//return the stat name based on a passed in id, part of Leaderboard class
function getStatName(statId) {
	var length = lb.stat.s.length;
	
	for(var i=0; i<=length-1; i++) {
		if(lb.stat.s[i].id == statId) {
			return lb.stat.s[i].n;
		}
	}
	return '';
}

//return course code based on passed in id
function getCourseCode(cId) {
	return cMap[cId].c;
}

//return st, nd, rd, or th -- as in 1st, 2nd, 3rd, 18th, etc
function getHoleSuffix(holeNum) {
	switch (holeNum){
		case('1'):
			return holeNum+'st';	//1st
		case('2'):
			return holeNum+'nd';	//2nd
		case('3'):
			return holeNum+'rd';	//3rd
		default:
			return holeNum+'th';	//4th, 5th..., 18th
	}
}

//return class name for ace, eagle, birdie, par, bogey, double+ bogey based on passed in score and par
function determineScoreColor(score, par) {
	if(score == '1') {	//no need for par/score math if score == 1, return ace class
		return 'tourStrokeAce';
	} else if(score == par || score == '--') {	//if score equals par or there is no score, return par class
		return 'tourStrokePar';
	}
	
	//par minus score to get a difference
	var diff = parseInt(par) - parseInt(score);
	
	//based on diff, return correct class
	if(diff > 0) {	//diff is > 0 when score is under par
		if(diff == 1) {
			return 'tourStrokeBirdie';
		} else if (diff == 2) {
			return 'tourStrokeEagle';
		}else if (diff == 3) {
			return 'tourStrokeDoubleEagle';
		}
	} else {	//diff is < 0 when score is over par
		if(diff == -1) {
			return 'tourStrokeBogey';
		} else {
			return 'tourStrokeDBogey';
		}
	}
	
	//par as default
	return 'tourStrokePar';
}

//the following functions return various tournament wide data
function getTournamentRoundNumber() {
	return lb.rn;
}

//number of rounds for this tournament
function getTournamentTotalRounds() {
	if(lb.tr) {
		return parseInt(lb.tr);
	}
	return 4;	//default to 4
}

function getTournamentYear() {
	return lb.y;
}

function getTournamentCode() {
	return lb.tc;
}

function getTournamentId() {
	return lb.tid;
}

function getTournamentProjCutLine() {
	return lb.pds.pcl;
}

function getTournamentCutScore() {
	return lb.pds.cs;
}

//return true or false
function getTournamentIsFEC() {
	return (lb.ic == 'y');	//ic = is cup
}

//return true or false
function moreThanOneCourse() {
	var courses = lb.c.c;
	var len = (courses.length == undefined)?1:courses.length;	//if courses.length is undef, len is 1.  otherwise, courses.length
	
	return (len > 1)?true:false;
}

//return 'y' or 'n' value of flag from JSON
function getTournamentFlag(flag) {
	switch(flag) {
		case('sc'):
			return lb.scf;	//score flag
		case('pbp'):
			return lb.pbpf;	//play-by-play flag
		case('stat'):
			return lb.sf;	//stat flag
		default:
			return 'no';
	}
}

//true or false if the cut flag is set
function isCutFlag() {
	return (lb.pds.cf == 'y');
}

//true or false if the projected cut flat is set
function isProjCutFlag() {
	return (lb.pds.pcf == 'y');
}

//true or false if there is a playoff round
function isPlayoff() {
	return (lb.ipl == 'y');
}

//create cutline row and ad row and add them to the lb tbody
function buildCutline() {
	var cutline = '<table id="tourLbCutlineTable" cellpadding="0" cellspacing="0"><tr><td class="tourLBMainCutLineRow" colspan="30">';
	if(isCutFlag()) {
		cutline += 'The following players failed to make the cut: ' + getTournamentCutScore();
	} else if(isProjCutFlag()) {
		cutline += 'Projected cut: ' + getTournamentCutScore();
	}
	cutline +='</td></tr></table>';
	return cutline;
}

//checks the cookie to see if inline pbp is turned on
function isInlinePbpOn() {
	var current = lb_getCookie('lb_pbp');
	
	if(current) {
		var tOrF = current == "true" ? true : false;
		return tOrF;
	}
	//TODO: return true;	//no cookie set, default to true
	return false;	//no cookie set, default to false
}

//add/remove border under cells right of player name when turning on inline pbp or expanding a row
function updateClassBorderBottom(rowId, clName, on) {
	cancelEventBubble();
	var elements = $(rowId).select('td.'+clName+'_'+!on);
	
	if(elements.length == 0) {
		return;
	}
	
	var i = 0;
	for(i=0; i<=elements.length-1; i++) {
		elements[i].className = elements[i].className.replace(clName+'_'+!on, clName+'_'+on);
	}
}

//check if a flag is set to 'y'
function isFlagYes(flag) {
	return (getTournamentFlag(flag) == 'y');
}

//turn off event bubbling in IE
function cancelEventBubble() {
	if(window.event) {
		window.event.cancelBubble = true;
	}
}

//switch table header image for tournaments with 3, 5, 6, etc. rounds -- defaults to 4 rounds
function determineSortTableHeaderImage() {
	var totalRounds = getTournamentTotalRounds();
	var isFEC = getTournamentIsFEC();
	
	if(totalRounds == 4 && isFEC) {	//default header image class is already in leaderboard-1.html, no need to do anything else
		return;
	} else {
		//set the class name based on if this is an FEC or money rank event
		var classBase = (isFEC == true)?'tourHeaderLegend_':'tourHeaderLegendMoney_';
		
		var sortTableEl = $('tourLBSortTableHeader');
		sortTableEl.className = classBase+totalRounds;
		sortTableEl = null;
		
		if(totalRounds != 4) {
			loadRoundSpecificCSSFile(totalRounds);
		}
	}
}

//load css file for 3, 5, or 6 round tournaments
function loadRoundSpecificCSSFile(rndNum) {
	var cssNode = document.createElement('link');
	cssNode.type = 'text/css';
	cssNode.rel = 'stylesheet';
	cssNode.href = '/.element/css/5.0/sect/leaderboards/lb'+rndNum+'Rounds.css';
	cssNode.media = 'screen';
	document.getElementsByTagName("head")[0].appendChild(cssNode);
}

//load a js file dynamically
function loadJsInclude(jsPath) {
	var js = document.createElement("script");
	js.setAttribute("type", "text/javascript");
	js.setAttribute("src", jsPath);
	document.getElementsByTagName("head")[0].appendChild(js);
}

//populate the player search drop-down
function populatePlayerSearchDropDown() {
	var playerSearchSelect = $('playerSearchDD');	//<select> element
	var playerSearchLen = playerSearchArray.length;
	
	for(var i=0; i<=playerSearchLen-1; i++) {
		var option = document.createElement('option');
		option.value = playerSearchArray[i][1];	//set plyer id as the value
		option.appendChild(document.createTextNode(playerSearchArray[i][0]));	//player name
		playerSearchSelect.appendChild(option);
	}
	
	playerSearchSelect = null;
}

//send metrics to omniture on user interaction (click color row, info, etc.)
function sendToOmniture(metricString) {
	return;	//for now, turn off click-specific omniture tracking
	
	s.prop8 = metricString;
	s.eVar8 = metricString;
	
	void(s.t());
}

//returns true if a player has play-by-play data, false otherwise
function hasPbp(player) {
	var numHoles = cMap[player.cid].c.length;
	for(var i=0; i<=numHoles-1; i++) {	//loop over number of holes
		if(player.h.h[i].sh != null) {	//if there is at least one shot, return true
			return true;
		}
	}
	return false;
}

//determine if a player has teed off or not
function hasTeedOff(player, playByPlay, tournRndNum) {
	if(player.t != '--') {	//if there is a value for the thru field, they have teed off.  no need to check the other logic.
		return true;
	}
	
	if(playByPlay) {	//supports play by play, check to see if they are in the current round and have taken a shot
		return (tournRndNum == player.rn && player.t == '--' && hasPbp(player));
	} else {	//else check to see if they are in the current round and have a score
		return !(tournRndNum == player.rn && player.crs == '--');
	}
	return false;	//default to false
}

//remove T and other funny rank things
function getRank(rank) {
	rank = rank.replace(/T/,'');
	//if fedexcup, show -- for places > 144
	if(getTournamentIsFEC()) {
		if(rank > 144) {
			//return '--';
		}
	}
	return rank;
}

//return the correct rank CSS class
function getRankClass(rank) {
	rank = rank.replace(/T/,'');	//remove T so we can compare just the number rank
	//FedExCup rank classes
	if(getTournamentIsFEC()) {
		if(rank >= 1 && rank <= 30) {
			return 'tourLBFEC1';
		} else if (rank >= 31 && rank <= 70) {
			return 'tourLBFEC2';
		} else if (rank >= 71 && rank <= 100) {
			return 'tourLBFEC3';
		} else if (rank >=101 && rank <= 125) {
			return 'tourLBFEC4';
		} else {
			return 'tourLBFEC5';
		}
	} else {	//money rank classes
		if(rank>=1 && rank <=125) {
			return 'tourLBMoney125';
		} else {
			return 'tourLBMoney126Plus';
		}
	}
}

function tourConstructWGCPlayerURIPathException(pId) {
	var uri = pId;
	
	//ensure the player's id is 6 chars long
	for(; uri.length<6;) {
		uri = '0'+uri;
	}
	
	//create array of string chars
	var uriArr = uri.toArray();
	
	//uri in the style of: ##/##/##/scorecards/[year]/[tournament code][tournament id].html
	uri = uriArr[0]+uriArr[1]+'/'+uriArr[2]+uriArr[3]+'/'+uriArr[4]+uriArr[5]+'/scorecards/'+getTournamentYear()+'/'+getTournamentCode()+getTournamentId()+'.html';
	
	return uri;
}

//constructs the uri to link to a player's scorecard from the player id
function constructPlayerURIPath(pId) {
	var playerFN = pMap[pId].fn.split(' ');
	
	if(typeof playerFN[1] != 'undefined'){//If spaces are present, player has multiple first names or mid-initial
    	if(playerFN[1].match('[a-zA-Z]{1}[.]')){//If mid-initial present in 2nd position, use only the first name 
        	playerFN = playerFN[0].replace(/['.,]/g, '').toLowerCase();
    	}
		else{//If mid-initial is not present in 2nd position, assume player has multiple first names
			playerFN = pMap[pId].fn.replace(/['.,]/g, '').replace(/ /g, '-').toLowerCase();	
		}
	}
	else{//When no spaces are present, parse FN as normal
    	playerFN = pMap[pId].fn.replace(/['.,]/g, '').replace(/ /g, '-').toLowerCase();
	}
	
	var playerLN = pMap[pId].ln.replace(/['.,]/g, '').replace(/ /g, '-').toLowerCase();
	var uri = pId;
	
	//ensure the player's id is 6 chars long
	for(; uri.length<6;) {
		uri = '0'+uri;
	}
	//variableName.replace(/['.,]/g, '').replace(/ /g, '-').toLowerCase();
	//create array of string chars
	var uriArr = uri.toArray();
	
	//uri in the style of: /[fname]-[lname]/scorecards/. Middile Init will be added if present
	uri += '/' + playerFN + '-' + playerLN + '/scorecards/#!/' + tId + '/' + lb_year;
	
	return uri;
}

//return true if the player is in the previous round (due to weather/darkness delay, etc.)
function inPreviousRound(rndNum) {
	var tournRound = getTournamentRoundNumber();
	if(tournRound == '401') {	//round will be 401 if there is a playoff round, return false
		return false;
	}
	return tournRound > rndNum;
}

function openLeaderboardVideo(y,t,p){
	var leaderboardVideoURL = LB_VIDEO_POPUP_URL+"?y="+y+"&t="+t+"&p="+p;
	var leaderboardVideoWindow = window.open(leaderboardVideoURL,"leaderboardVideo","width=1000,height=674,left=0,top=0,location=0,scrollbars=0");
	leaderboardVideoWindow.focus();
}
	
function buildPlayerDiv(player, destination, pIndex) {
	cancelEventBubble();
	var classNameComponent = (destination == "lb")?"Main":"MyLB";
	var pId = player.id;
	var borderBottomBoolString  = '';
	
	if(isInlinePbpOn()) {
		borderBottomBoolString = (player.s == 'A')?"true":"false";	//don't show inline pbp for people not playing (cut, mdf, etc)
	} else {
		borderBottomBoolString = "false";
	}
	
	var totalRounds = getTournamentTotalRounds();
	
	var pBuff = new StringBuffer();
	
	//alternate row colors
	var divClass = '';
	if(pIndex%2 == 0) {
		divClass = 'tourLB'+classNameComponent+'Alt';
	} else {
		divClass = 'tourLB'+classNameComponent+'Default';
	}
	
	var divOpen = '<div class="'+divClass+'" id="'+pId+destination+'Div">';
	var tableOpen = '<table id="'+pId+destination+'Table" cellpadding="0" cellspacing="0" border="0"><tbody id="'+destination+'Player'+pIndex+'">';
	
	var rowString = '<tr id="'+destination+pId+'Row" class="tourLB'+classNameComponent+'PlayerRow">';
	pBuff.append(divOpen);
	pBuff.append(tableOpen);
	pBuff.append(rowString);
	
	var tdString = '';
	
	//pin cell
	var pinString = '';
	if(destination == 'lb') {
		pinString = '<a onclick="pinPlayer_click(\''+pId+'\', '+false+')"><img src="http://i.cdn.turner.com/pgatour/.element/img/4.0/sect/leaderboard/plus_pin.gif" alt="+" title="Add to My Leaderboard" /></a>';
	} else {
		pinString = '<a onclick="unPinPlayer_click(\''+pId+'\')"><img src="http://i.cdn.turner.com/pgatour/.element/img/4.0/sect/leaderboard/minus_'+classNameComponent+'_pin.gif" alt="-" title="Remove from My Leaderboard" /></a>';
	}
	tdString = '<td id="'+destination+'Pin'+pId+'" class="tourLBPintd">'+pinString+'</td>';
	pBuff.append(tdString);
	
	//position cell
	var pos = (player.p == "--")?"&nbsp;":player.p.toUpperCase();
	tdString = '<td class="tourLBPostd">'+pos+'</td>';
	pBuff.append(tdString);
	
	//movement cell
	var arrowString = determineMovementArrow(player.m, destination);
	if(arrowString != null) {
		arrowString = '<img src="http://i.cdn.turner.com/pgatour/.element/img/4.0/sect/leaderboard/'+arrowString+'" alt="" title="" />';
	} else {
		arrowString = '';
	}
	tdString = '<td class="tourLBMovtd"><span id="'+destination+'playerMovement'+pId+'Id" class="tourPlayerMovementSpan">'+arrowString+getMovementOutputString(player.m)+'</span></td>';
	pBuff.append(tdString);
	
	//projected fedex cup/rank cell
	tdString = '<td class="tourLBRanktd '+getRankClass(player.pfcr)+'">'+getRank(player.pfcr)+'</td>';
	pBuff.append(tdString);
	
	//flag cell
	if(player.f == 'UNX') {	//UNX is an undefined country, show a blank spot for the flag
		var tdString = '<td class="tourLBFlagtd">&nbsp;</td> ';
	} else {
		var tdString = '<td class="tourLBFlagtd"><img src="http://i.cdn.turner.com/pgatour/.element/img/1.0/main/leaderboard/country_icons/'+player.f+'.gif" alt="'+player.f+'" title="'+player.f+'" /></td>';
	}
	pBuff.append(tdString);
	
	//player cell
	var playerName = player.fn + ' ' + player.ln;
	/*TODO: fix this */
	if(player.s == 'A') {	//we only want to do these checks for players in active status
		if(moreThanOneCourse()) {	//more than one course
			playerName += ' ('+getCourseCode(player.cid)+')';	//append the course code
		}
		
		if(inPreviousRound(player.rn)) {	//player is in previous round due to some delay
			playerName += ' #';	//append a #
		}
	}
	
	if(player.af) {	//amateur flag
		if(player.af == 'y') {
			playerName += ' (a)';
		}
	}
	
	var playerClass = '';
	if(totalRounds != 4) {
		playerClass= ' tourLBPlayertd'+totalRounds;
	}
	//TODO: span class
	if((typeof leaderboardVideoFeed != 'undefined') && (leaderboardVideoFeed.player_id[pId])){	// Add leaderboard video icon link if player has a video for the tournament
		if (oWGCTournaments[tId]) {
			tdString = '<td class="tourLB'+classNameComponent+'Playertd'+playerClass+'"><span class="tourLeaderboardPlayerName"><a href="http://'+window.location.hostname+'/players/'+tourConstructWGCPlayerURIPathException(pId)+'">'+playerName+'</a></span><a class="tourLeaderboardVideoIcon" href="javascript:void(0)" onclick="openLeaderboardVideo(\''+lbVideoFeedYear+'\',\''+tId+'\',\''+pId+'\')"></a></td>';
		}
		else {
			tdString = '<td class="tourLB'+classNameComponent+'Playertd'+playerClass+'"><span class="tourLeaderboardPlayerName"><a href="http://'+window.location.hostname+'/golfers/'+constructPlayerURIPath(pId)+'">'+playerName+'</a></span><a class="tourLeaderboardVideoIcon" href="javascript:void(0)" onclick="openLeaderboardVideo(\''+lbVideoFeedYear+'\',\''+tId+'\',\''+pId+'\')"></a></td>';
		}
	}
	else{
		if (oWGCTournaments[tId]) {
			tdString = '<td class="tourLB'+classNameComponent+'Playertd'+playerClass+'"><span><a href="http://'+window.location.hostname+'/players/'+tourConstructWGCPlayerURIPathException(pId)+'">'+playerName+'</a></span></td>';
		}
		else {
			tdString = '<td class="tourLB'+classNameComponent+'Playertd'+playerClass+'"><span><a href="http://'+window.location.hostname+'/golfers/'+constructPlayerURIPath(pId)+'">'+playerName+'</a></span></td>';
		}
	}
	pBuff.append(tdString);
	
	//total (to par) cell
	var tp = (player.tp == "--")?"&nbsp;":player.tp;
	tdString = '<td class="tourLBTotaltd tourLB'+classNameComponent+'CellBorderBottom_'+borderBottomBoolString + '">'+tp+'</td>';
	pBuff.append(tdString);
	
	//tee time OR thru and round score cells
	var tournRndNum = getTournamentRoundNumber();
	if(player.s != 'A') {	//non-active players
		tdString = '<td class="tourLBThrutd">&nbsp;</td><td class="tourLBRndtd">&nbsp;</td>';
	} else {	//determine if we should show tee times or score
		if(hasTeedOff(player, isFlagYes('pbp'), tournRndNum)) {
			tdString = '<td class="tourLBThrutd tourLB'+classNameComponent+'CellBorderBottom_'+borderBottomBoolString+'">'+player.t+'</td>';
			tdString += ' <td class="tourLBRndtd tourLB'+classNameComponent+'CellBorderBottom_'+borderBottomBoolString+'">'+player.crs+'</td>';
		} else {
			tdString = '<td class="tourLBTeetd tourLB'+classNameComponent+'CellBorderBottom_'+borderBottomBoolString+'" colspan="2">'+player.rs.r[player.rn-1].t+'</td>';
		}
	}
	pBuff.append(tdString);
	
	//color cell
	tdString = '<td id="nextColor'+destination+pId+'" class="tourLBColortd tourLB'+classNameComponent+'CellBorderBottom_'+borderBottomBoolString+'">';
	if(destination == 'lb') {
		tdString += '<a onclick="colorRow_click(\''+pId+'\', 1, '+false+')">color</a></td>';
	} else {
		tdString += '&nbsp;</td>';
	}
	pBuff.append(tdString);
	
	//info cell
	tdString = '<td class="tourLBInfotd tourLB'+classNameComponent+'CellBorderBottom_'+borderBottomBoolString+'"';
	if(player.s == 'A') {
		tdString += ' id="'+destination+'ShowHide'+pId+'"><a onclick="lbClickExpansion_click(\''+pId+'\', \'SC\', '+false+', \''+destination+'\')">info</a>';
	} else {
		tdString += '>&nbsp;</td>';
	}
	pBuff.append(tdString);
	
	//round score cells
	for(var i=0; i<=totalRounds-1; i++) {
		var rnd = i+1;
		var rs = (player.rs.r[i].sc == "--")?"&nbsp;":player.rs.r[i].sc;
		tdString = '<td class="tourLBRStd'+rnd+' tourLB'+classNameComponent+'CellBorderBottom_'+borderBottomBoolString+'">'+rs+'</td>';
		pBuff.append(tdString);
	}
	
	//strokes cell
	var ts = (player.rs.t == "--")?"&nbsp;":player.rs.t;
	tdString = '<td class="tourLBStrokestd tourLB'+classNameComponent+'CellBorderBottom_'+borderBottomBoolString+'">'+ts+'</td>';
	pBuff.append(tdString);
	pBuff.append('</tr>');
	
	//only get inline play-by-play row for active players
	if(player.s == 'A') {
		pBuff.append(buildInlinePBPRow(player, destination));
	}
	
	pBuff.append('</tbody></table></div>');
	
	return(pBuff.toString());
}

function buildInlinePBPRow(player, destination) {
	var inlinePbpBuff = new StringBuffer();
	var pId = player.id;
	
	//start <tr>
	inlinePbpBuff.append('<tr id="'+destination+pId+'PBPRow" class="tourPlayByPlayRow"');
	
	//if play-by-play is turned off, set the display to none
	if(!isInlinePbpOn()) {
		inlinePbpBuff.append(' style="display: none;">');
	} else {
		inlinePbpBuff.append('>');
	}
	
	inlinePbpBuff.append('<td class="tourLBPintd">&nbsp;</td><td class="tourLBPostd">&nbsp;</td><td class="tourLBMovtd">&nbsp;</td><td class="tourLBRanktd">&nbsp;</td><td class="tourLBFlagtd">&nbsp;</td>');
	inlinePbpBuff.append('<td class="tourPlayByPlayCell" colspan="30"><p>');
	
	if(player.t.indexOf('F') >= 0) {	//this player's round is over
		inlinePbpBuff.append('Round complete');
	} else {	//player is still on the course
		var pbpArr = getPlayerPBP(player);
		if(pbpArr.length > 0) {	//at least one shot
			var lastShotIndex = pbpArr.length-1;
			var holeData = getHoleData(player.cid, pbpArr[0]);
			inlinePbpBuff.append('Shot '+pbpArr[lastShotIndex][0]+': '+pbpArr[lastShotIndex][1]+' on the '+holeData[1]+'yd, Par '+holeData[0]+', '+getHoleSuffix(pbpArr[0])+' Hole');
		} else {	//no shots yet for the current hole
			var currHole = '';
			if(player.ch == '') {	//player is on first hole, which could also be 10 if they start on the back nine
				currHole = player.sh;
			} else {
				currHole = player.ch;
			}
			
			inlinePbpBuff.append('Waiting to tee off on hole '+currHole);
		}
	}
	inlinePbpBuff.append('</p></td>');
	
	//end <tr>
	inlinePbpBuff.append('</tr>');
	
	return inlinePbpBuff.toString();
}

/** lb classes **/
//ExpansionRow class
var ExpansionRow = Class.create({
	initialize: function(className, playerId, destination, type) {
		this.className = className;
		this.playerId = playerId;
		this.destination = destination;
		this.type = type;
		this.rowId = destination+playerId+'ExpRow';
		this.parentTbody = destination+'_tbody';
		this.parentRow = destination+playerId+'PBPRow';	//inline pbp on
		//this.parentRow = destination+playerId+'Row';	//inline pbp off
	},
	
	showExpansionRow: function(showHideId, isPageRefresh) {
		cancelEventBubble();
		var classNameComponent = (this.destination == "lb")?"Main":"MyLB";
		
		var expBuff = new StringBuffer();
		
		expBuff.append('<tr id="'+this.rowId+'" class="'+this.className+'"><td class="tourLBPintd">&nbsp;</td><td class="tourLBPostd">&nbsp;</td><td class="tourLBMovtd">&nbsp;</td><td class="tourLBRanktd">&nbsp;</td><td class="tourLBFlagtd">&nbsp;</td>');
		
		expBuff.append('<td class="tourExpansionCell" colspan="30"><table class="tourLB'+classNameComponent+'ExpRow" cellpadding="0" cellspacing="0" width="100%"');
		expBuff.append('<thead></thead>');
		expBuff.append('<tfoot></tfoot>');
		expBuff.append('<tbody><tr><td class="tourLastCell"><div class="tourLBExpRowTbl"><div class="tourLBExpRowNav">');
		
		//check to see if this tournament supports scorecard, pbp, or stats
		if(isFlagYes('sc')) {
			expBuff.append('<a class="tourLBExpRowItem" id="'+this.destination+this.playerId+'SC" onclick="buildSCExpTable_click(\''+this.playerId+'\', \''+this.destination+'\', '+false+')">scorecard</a>');
		}
		
		var playByPlayDisplay = '';
		/* Ticket: PGATOUR-9750. If the player is not on the primary course, do not display the play-by-play tab.  */
		if(lb.pc != pMap[this.playerId].cid){
			playByPlayDisplay = ' style="display:none"';
		}
		
		if(isFlagYes('pbp')) {
			expBuff.append('<a' + playByPlayDisplay + ' class="tourLBExpRowItem" id="'+this.destination+this.playerId+'PP" onclick="buildPPExpTable_click(\''+this.playerId+'\', \''+this.destination+'\', '+false+')">play-by-play</a>');
		}
		
		if(isFlagYes('stat')) {
			expBuff.append('<a class="tourLBExpRowItem" id="'+this.destination+this.playerId+'ST" onclick="buildSTExpTable_click(\''+this.playerId+'\', \''+this.destination+'\', '+false+')">player stats</a>');
		}
		
		if(getTournamentIsFEC()) {
			expBuff.append('<a class="tourLBExpRowItem" id="'+this.destination+this.playerId+'FC" onclick="buildFCExpTable_click(\''+this.playerId+'\', \''+this.destination+'\', '+false+')">FedExCup points</a>');
		}
		
		expBuff.append('<div id="'+this.destination+'ExpRowClose'+this.playerId+'"><div class="tourLBExpRowClose" onclick="lbClickExpansionHide_click(\''+this.playerId+'\', \''+this.type+'\', '+false+', \''+this.destination+'\')">x</div></div>');	//close (x) button
		expBuff.append('</div>');	//close nav div
		
		expBuff.append('<div class="tourLBExpRowContent"><table id="'+this.destination+this.playerId+'ExpTable" cellpadding="0" cellspacing="0" border="0"><tbody id="'+this.destination+this.playerId+'ExpTbody"></tbody></table></div>');
		expBuff.append('</div></td></tr></tbody></table></td></tr>');
		
		Element.insert(this.parentRow, {'after':expBuff.toString()});
		
		//build dynamic function name based on the type of expansion row to show (scorecared, etc) and call it
		var expTableFunction = 'build'+this.type+'ExpTable';
		window[expTableFunction](this.playerId, this.destination, isPageRefresh);
		
		document.getElementById(showHideId+this.playerId).innerHTML = '<a class="pointer" onclick="lbClickExpansionHide_click(\''+this.playerId+'\',\''+this.type+'\', '+false+', \''+this.destination+'\')">hide</a>';
	}
});

//Playoff class
var Playoff = Class.create({
	initialize: function(playoffJson) {
		if(!playoffJson || playoffJson.p == null) {	//this is in case ipl gets flipped to 'y', but there is no playoff section in the file yet
			this.holes = null;
			this.players = null;
		} else {	//normal playoff case
			this.holes = [];
			this.players = [];
			
			this.populatePlayers(playoffJson);
			this.populateHoles();
		}
	},
	
	//create list of holes in the playoff
	populateHoles: function() {
		var length = this.players.length;
		var pIndex = 0;
		var currLength = 0;
		var newLength = 0;
		
		//figure out who has the most holes, this takes care of the case where someone is eliminated from playoff play, but others continue
		for(var i=0; i<=length-1; i++) {
			newLength = this.players[i].holes.length;
			if(newLength > currLength) {
				pIndex = i;
				currLength = newLength;
			}
		}
		
		var player = this.players[pIndex];
		//get the course id so we can look up hole data (par, etc)
		this.holes.push(player.cId);
		
		//push hole numbers onto array
		var holeNumbers = [];
		for(var i=0; i<=currLength-1; i++) {
			holeNumbers.push(player.holes[i][0]);
		}
		//and then push that array onto the holes member
		this.holes.push(holeNumbers);
	},
	
	//create list of players and their data
	populatePlayers: function(playoffJson) {
		var playerJson = playoffJson.p;
		if(playerJson == null) {
			return;
		}
		
		var length = playerJson.length;
		
		for(var i=0; i<=length-1; i++) {
			var player = new PlayoffPlayer(playerJson[i]);
			
			this.players.push(player);
		}
	},
	
	//builds the tourn. playoff table and inserts into the DOM
	buildTournamentPlayoffTable: function() {
		var playoffBuff = new StringBuffer();
		var pHole = '', cHole = '', par = '';
		var holeData;
		
		playoffBuff.append('<table cellpadding="0" cellspacing="0" width="100%"><tbody class="playoff"><tr class="tourPlayoffHeader"><td class="tourPlayoffLastCell" colspan="30">PLAYOFF</td></tr>');
		
		//loop over holes and output the playoff hole number, course hole number and par for each
		for(var i=0; i<=this.holes[1].length-1; i++) {
			//hole number sequence (ex: 1, 2, 3, etc)
			pHole += '<div class="tourPlayoffItem">'+parseInt(i+1)+'</div>';
			//the hole number for the hole on the course players are playing
			cHole += '<div class="tourPlayoffItem">'+this.holes[1][i]+'</div>';
			//par for the course hole number
			holeData = getHoleData(this.holes[0], this.holes[1][i]);
			par += '<div class="tourPlayoffItem">'+holeData[0]+'</div>';
		}
		
		playoffBuff.append('<tr class="tourPlayoffCell"><td class="tourPlayoffTitle tourPlayoffHole">Playoff Hole No.</td><td class="tourPlayoffLastCell">'+pHole+'</td></tr>');
		playoffBuff.append('<tr class="tourPlayoffCellAlt"><td class="tourPlayoffTitle tourPlayoffDetail">Course Hole No.</td><td class="tourPlayoffLastCell">'+cHole+'</td></tr>');
		playoffBuff.append('<tr class="tourPlayoffCell"><td class="tourPlayoffTitle tourPlayoffDetail">PAR</td><td class="tourPlayoffLastCell">'+par+'</td></tr>');
		
		//loop over players and output their stuff
		var players = this.players;
		var pLen = players.length;
		
		for(var i=0; i<=pLen-1; i++) {
			var cellClass = (i%2 != 0)?'even':'odd';	//determine what class to give the cells based on the loop iterator
			var pStatus = players[i].status;
			
			var trWinClass = '';
			if(pStatus == 'winner') {
				trWinClass = 'tourPlayoffCellWin';
			}
			
			//output score
			var holes = players[i].holes.length;
			var sDiv = '';
			for(var j=0; j<=holes-1; j++) {
				sDiv += '<div class="tourPlayoffItem">'+players[i].holes[j][1]+'</div>';
			}
			var pRow = '<tr class="'+trWinClass+'"><td class="'+cellClass+'" rowspan="2">'+players[i].fName+' '+players[i].lName+'</td>';
			pRow += '<td class="'+cellClass+' tourPlayoffLastCell">'+sDiv+'</td>';
			pRow +='</tr>';
			
			playoffBuff.append(pRow);
			
			var shotRow = '<tr class="'+trWinClass+'"><td class="'+cellClass+'">';
			if(pStatus == 'active') {	//output shot details
				var pbpLen = players[i].pbp.length;
				//only show shot detail if there is detail to show
				if(pbpLen > 0) {
					shotRow += 'Shot '+players[i].pbp[pbpLen-1][0]+': '+players[i].pbp[pbpLen-1][1];
				} else {
					shotRow += '<br />';
				}
				shotRow += '</td></tr>';
			} else if(pStatus == 'winner') {	//output winner text
				shotRow += 'Winner</td></tr>';
			} else if(pStatus == 'eliminated' || pStatus == 'lost') {	//output eliminated text
				shotRow += 'Eliminated</td></tr>';
			} else {	//default catch
				shotRow += '&nbsp;</td></tr>';
			}
			
			playoffBuff.append(shotRow);
		}
		
		playoffBuff.append('</tbody></table>');
		
		$('lbPlayoffContent').innerHTML = playoffBuff.toString();
		$('lbPlayoffContent').setStyle({ display: 'block' });
	}
});

//PlayoffPlayer class
var PlayoffPlayer = Class.create({
	//player object that contains only what is needed for a player in a playoff round
	initialize: function(pJson) {
		this.pId = pJson.id;
		this.fName = pJson.fn;
		this.lName = pJson.ln;
		this.fInitial = pJson.fi;
		this.flag = pJson.f;
		this.cId = pJson.cid;
		this.status = pJson.s;
		this.holes = [];
		this.pbp = [];
		
		this.populateHoles(pJson);
	},
	
	//populate hole id and score
	populateHoles: function(pJson) {
		if(pJson.h.h == null) {	//sometimes this will be null before the playoff round starts
			this.holes.push(['1', '--']);	//push -- onto the hole array to fill the table cell
			return;	//then return
		}
		var holeLength = pJson.h.h.length;
		if(holeLength != null) {	//more than one hole
			for(var j=0; j<=holeLength-1; j++) {
				this.holes.push([pJson.h.h[j].id, pJson.h.h[j].s]);
				if(pJson.h.h[j].sh != null) {	//also populate play-by-play data, if it exists
					this.populatePBP(pJson, j);
				}
			}
		} else {	//only one hole so far
			this.holes.push([pJson.h.h.id, pJson.h.h.s]);
			if(pJson.h.h.sh != null) {
				this.populatePBP(pJson, null);
			}
		}
	},
	
	//shot details for most recent hole
	populatePBP: function(pJson, it) {
		if(it == null) {	//when iterator is null, there isn't an array of holes don't try to access with array index
			var pbpLength = pJson.h.h.sh.length;
			if(pbpLength != null) {	//more than one shot
				for(var i=0; i<=pbpLength-1; i++) {
					this.pbp.push([pJson.h.h.sh[i].id, pJson.h.h.sh[i].content]);
				}
			} else {	//only one shot so far
				this.pbp.push([pJson.h.h.sh.id, pJson.h.h.sh.content]);
			}
		} else {
			var pbpLength = pJson.h.h[it].sh.length;
			if(pbpLength != null) {	//more than one shot
				for(var i=0; i<=pbpLength-1; i++) {
					this.pbp.push([pJson.h.h[it].sh[i].id, pJson.h.h[it].sh[i].content]);
				}
			} else {	//only one shot so far
				this.pbp.push([pJson.h.h[it].sh.id, pJson.h.h[it].sh.content]);
			}
		}
	}
});

//StringBuffer class
var StringBuffer = Class.create({
	initialize: function() {
		this.buffer = [];
	},
	
	append: function(string) {
		this.buffer.push(string); 
	},
	
	toString: function() {
		return this.buffer.join("");
	}
});

/** cookie functions **/
//creates a cookie
function lb_createCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

//erase a cookie
function lb_eraseCookie(name) {
	lb_createCookie(name,"",-1);
}

//gets a cookie with a given name
function lb_getCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0; i<=ca.length-1; i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

//create inline play by play cookie
function lb_createPlayByPlayCookie(tOrF) {
	lb_createCookie('lb_pbp', tOrF, 7);
}

//create sort cookie
function lb_addSortToCookie(sort, direction, player) {
	if(player==null) {
		lb_createCookie('lb_sort', sort+'|'+direction, 1);
	} else {
		lb_createCookie('lb_sort', sort+'|'+direction+'|'+player, 1);
	}
}

//adds a player/type combo to the lb_ cookie
function lb_addPlayerExpToCookie(playerId, type, cookieName) {
	var current = lb_getCookie(cookieName);
	var myPlayers;
	
	if(current) {
		var pIndex = current.indexOf(playerId);
		if(pIndex >= 0) {
			var startIndex = current.indexOf('|', pIndex);
			var replaceMe = playerId+'|'+current.substring(startIndex+1, startIndex+3);
			myPlayers = current.replace(replaceMe, playerId+'|'+type);
			lb_createCookie(cookieName, myPlayers, 7);
		} else {
			myPlayers = current + ','+playerId+'|'+type;
			lb_createCookie(cookieName, myPlayers, 7);
		}
	} else {
		myPlayers = playerId+'|'+type;
		lb_createCookie(cookieName, myPlayers, 7);
	}
}

//adds a player/color combo to the lb_highlights cookie
function lb_addPlayerHighlightComboToCookie(playerId, colorIndex) {
	var current = lb_getCookie('lb_highlights');
	var prevColor = parseInt(colorIndex)-1;
	var updateCookieString;
	
	if(current) {
		if(colorIndex == 0) {
			updateCookieString = playerReplace(current, playerId, '|4');
		} else {
			var searchString = playerId+'|'+prevColor.toString();
			var playerIndex = current.indexOf(searchString);
			
			if(playerIndex>=0) {	//player already exists in the cookie
				//update the player|color combo with the new colorIndex
				updateCookieString = current.replace(searchString, playerId+'|'+colorIndex);
			} else {
				updateCookieString = current+','+playerId+'|'+colorIndex;
			}
		}
	} else {
		updateCookieString = playerId+'|'+colorIndex;
	}
	
	//set this to some crazy long timeframe, currently 10 years
	lb_createCookie('lb_highlights', updateCookieString, 3650);
}

//adds a player to the lb_myLb cookie, aka pinning
function lb_addPlayerToCookie(playerId) {
	var currentPlayers = lb_getCookie('lb_myLb');	//gets current players from cookie
	var myPlayers;	//stores new player list
	
	if (currentPlayers) {	//if there are any current players and the new player to the end of the list and update the cookie
		myPlayers = currentPlayers +","+ playerId;
	} else {
		myPlayers = playerId;
	}
	
	//set this to some crazy long timeframe, currently 10 years
	lb_createCookie('lb_myLb', myPlayers, 3650);
}

function lb_removePlayerExpFromCookie(playerId, type, cookieName) {
	var current = lb_getCookie(cookieName);
	var updateCookieString = playerReplace(current, playerId, '|'+type);
	
	lb_createCookie(cookieName, updateCookieString, 7);
}

//removes a player from the cookie
function lb_removePlayerFromCookie(playerId) {
	var current = lb_getCookie('lb_myLb');
	var updateCookieString = '';
	
	//remove player from my leaderboard cookie
	updateCookieString = playerReplace(current, playerId, '');
	lb_createCookie('lb_myLb', updateCookieString, 7);
	
	//remove from expansion row cookie
	current = lb_getCookie('myLb_exp');
	if(current) {
		var type = getExpTypeForPlayer(current, playerId);
		lb_removePlayerExpFromCookie(playerId, type, 'myLb_exp');
	}
}

//replaces a player in the cookie based on comma before or after, etc
function playerReplace(current, playerId, append) {
	var myPlayers = current;
	var playerC = playerId+append+',';	//player id with comma behind
	var cPlayer = ','+playerId+append;	//player id with comma in front
	
	if (current.indexOf(playerC) >= 0){
		myPlayers = current.replace(playerC,'');}
	else if (current.indexOf(cPlayer) >= 0){
		myPlayers = current.replace(cPlayer,'');}
	else if (current.indexOf(playerId) >= 0){
		myPlayers = current.replace(playerId+append,'');}
	return myPlayers;
}

function lb_showHideInlinePlayByPlayFromCookie() {
	var current = lb_getCookie('lb_pbp');
	
	if(current) {
		var tOrF = current == "true" ? true : false;
		showHidePBP(tOrF);
	} else {	//default to inline play by play on
		showHidePBP(true);
	}
}

function lb_sortFromCookie() {
	var current = lb_getCookie('lb_sort');
	if(current) {
		sortVals = current.split('|');
		//the value in the cookie is a string, use actual true or false
		tOrF = sortVals[1] == "true" ? true : false;
		if(!sortVals[2]) {
			lbSort(sortVals[0], tOrF);
		} else {
			lbSort(sortVals[0], tOrF, sortVals[2]);
		}
		return true;	//return true that there was a cookie for sort
	}
	//set a cookie for default sort
	lb_addSortToCookie('p', true, null);
	return false;	//no cookie previously set, call additional cookie functions in main lb function
}

//get cookie values and create expansion rows
function lb_generateExpRowsFromCookie(doMainLb) {
	var current = lb_getCookie('lb_exp');
	var currentMyLb = lb_getCookie('myLb_exp');
	var players = '';
	
	if(doMainLb == null) {
		doMainLb == true;
	}
	
	if(doMainLb) {	//sometimes we don't want to build expansion rows for the main lb, see functions.js
		if(current) {
			players = current.split(',');
			
			for(var i=0; i<=players.length-1; i++) {
				playerExpArray = players[i].split('|');
				lbClickExpansion(playerExpArray[0], playerExpArray[1], true, 'lb');
			}
		}
	}
	
	if(currentMyLb) {
		players = currentMyLb.split(',');
		
		for(var i=0; i<=players.length-1; i++) {
			playerExpArray = players[i].split('|');
			lbClickExpansion(playerExpArray[0], playerExpArray[1], true, 'myLb');
		}
	}
}

//get cookie values and highlight rows
function lb_generateRowHighlightsFromCookie() {
	var playerHighlightCombos = [];
	var playerColorArray = [];
	var current = '';
	
	//cookie set by the next color links in the lb
	current = lb_getCookie('lb_highlights');
	if(current) {
		playerHighlightCombos = current.split(',');
		for(var i=0; i<=playerHighlightCombos.length-1; i++) {
			playerColorArray = playerHighlightCombos[i].split('|');
			colorRow(playerColorArray[0], parseInt(playerColorArray[1]), true);
		}
	}
}

//gets cookie value and generates rows
function lb_generateMyLbFromCookie() {
	var myCurrentPlayers = lb_getCookie('lb_myLb');	//get cookie values
	if (myCurrentPlayers) {	//if there are any players in the cookie, loop through them and add rows to mylb
		myLbSort();
	} else {
		populateEmptyMyLb();
		return;
	}
	
	if(isMyLbEmpty()) {	//case where only players not playing this week are in the lb_myLb cookie
		populateEmptyMyLb();
	}
}

//get the expansion row type for a given player already in the cookie
function getExpTypeForPlayer(current, playerId) {
	var pIndex = current.indexOf(playerId);
	
	if(pIndex >= 0) {
		var startIndex = current.indexOf('|', pIndex);
		return current.substring(startIndex+1, startIndex+3);
	}
	return '';
}

/** determine if event is currently being played **/
function isEventLive() {
	if(lb.rs == 'In Progress') {
		return true;
	}
	return false;	//default to false
}

/** remove events (onclick) before resetting the innerHTML to avoid memory leaks **/
function removeEventHandlers() {
	for(var i in pOrderMap) {
		var pId = pOrderMap[i];
		
		var el = $('lbPin'+pId);
		if(el) {
			el.childNodes[0]['onclick'] = null;
			//purge(el);
		}
		
		el = $('nextColorlb'+pId);
		if(el) {
			el.childNodes[0]['onclick'] = null;
			//purge(el);
		}
		
		el = $('lbShowHide'+pId);
		if(el) {
			el.childNodes[0]['onclick'] = null;;
			//purge(el);
		}
		
		el = null;
	}
}

function purge(d) {
	var a = d.attributes, i, l, n;
	if (a) {
		l = a.length;
		for (i = 0; i < l; i += 1) {
			n = a[i].name;
			if (typeof d[n] === 'function') {
				d[n] = null;
			}
		}
	}
	a = d.childNodes;
	if (a) {
		l = a.length;
		for (i = 0; i < l; i += 1) {
			purge(d.childNodes[i]);
		}
	}
}

