(function($) {

    var SEL_MULT_GAMES = 0;
    // FIXME: Assign data_days and last days to defines
    var data_days = 0;
    var days_to_process = 0;
    var first_loaded = 0;
    var last_loaded = 0;
    var _TRIP_SELS = new Array();
    var _DAY_TO_DATE = new Array();
    var _DATE_TO_DAY = new Array();
    var EXCLUDE_TEAMS = new Array();
    var _STARTDISTCLEARLIST = new Array();
    var LAST_DATE;// = 1003;
    var last_visible = 0;
    var first_visible = 0;
    var disp_days = 0, disp_games = 0;
    var first_vis_day = 0;
    var day_to_load = -1;
    var gotoTeam = "";

    $.fn.gamegrid = function(json, teams, options) {
       // debug(this);
       
       for(var tm in teams) {
	  // exclude START from this list?
	  EXCLUDE_TEAMS[tm] = 0;
       }

       var opts   = $.extend({}, $.fn.gamegrid.defaults, options);
       data_days  = json.days;
       disp_days  = opts.days;
       disp_games = opts.games;
       LAST_DATE  = opts.lastdate;

       // FIXME: More clearly define/separate game info block and trip list block - what does this one handle?
       // Selectgame:
       // - Initializes: first_dist, total_dist, last_loc, trip_query, extra_stops[]
       // - Sorts: _TRIP_SELS by date
       // - Resets: grid opacity, cell size
       // - Gets last trip date
       // - Clears tripviewer
       // - Clears startdate if no start loc or games selected
       // - If start loc is loaded: add start to first dist or load distance from start (if trip is empty)
       // - Set distance to first game
       // - Loop through trip:
       //   - add game to trip (tripViewer)
       //   - First game: start query, load any days to grid before first game (starting at STARTINFO.date.mmdd) and set distances
       //                 set start game to first game and first game date
       //   - Other games: add to query or push to overflow list (extra_stops)
       //   - Not last game: add dist to next stop
       //   - Load day (if needed) and set distance for days until next stop or 3 days beyond last game (or end of season)
       // - Add map marker if only one location to load
       // - Send directions query if multiple stops

       $(this).bind("selectgame", 
		    function() {
		       
		       var first_dist = 0;
		       var total_dist = 0;
		       var last_loc = "";
		       var trip_query = "";
		       var extra_stops = new Array();
		       _TRIP_SELS.sort(function(a,b) {
					  var inta = /[0-9]+/.exec(a);
					  var intb = /[0-9]+/.exec(b);
					  return inta - intb;
				       });
		       
		       $("#gg-grid div.gg-game-cell").css("opacity", 1);
		       $("#gg-grid div.gg-game-cell-large").removeClass("gg-game-cell-large");
		       
		       var last_trip_day = parseInt($("#gg-grid div#" + _TRIP_SELS[_TRIP_SELS.length-1]).attr("day"));
		       
		       $.clearTripviewer();
		       
		       if(!STARTINFO.loc.loaded && _TRIP_SELS.length == 0) {
			  $.setStartGame(0,0,0);
		       }
		       
		       if(teamlocs["START"]) {
			  if(_TRIP_SELS.length != 0) {
			     var first_home = $("#gg-grid #" + _TRIP_SELS[0]).attr("home");
			     var start_desc = $("#startinput").attr("value");
			     first_dist = __dists["start"][first_home];
			     total_dist += first_dist;
			  } else {
			     var daytoload = _DATE_TO_DAY[STARTINFO.date.mmdd];
			     var daytoload_1 = daytoload + 1;
			     var daytoload_2 = daytoload + 2;
			     var daytoload_2 = daytoload + 3;
			     setDistanceForDay(daytoload,"start",0);
			     setDistanceForDay(daytoload + 1,"start",0);
			     setDistanceForDay(daytoload + 2,"start",0);
			     setDistanceForDay(daytoload + 3,"start",0);
			  }
		       }
		       
		       $("#gg-grid div#" + _TRIP_SELS[0]).attr("dist", first_dist);
		       
		       for(var i = 0; i < _TRIP_SELS.length; i++) {
			  var $this_game = $("#gg-grid #" + _TRIP_SELS[i]);
			  var start_loc   = $this_game.attr("home");
			  var current_day = $this_game.attr("day");
			  var current_date = _DAY_TO_DATE[current_day];
			  var next_day    = parseInt(current_day) + 1;
			  var next_trip_day = -1;
			  
			  last_loc = start_loc;
			  
			  $.addGameToTrip(current_date, _TRIP_SELS[i]);
			  
			  // First game tasks
			  if(i == 0) {
			     var start_loc_query_text = $.getStartLocQuery();
			     trip_query += "from:" + start_loc_query_text + teamlocs[start_loc].addr;
			     if(!STARTINFO.date.loaded) {
				STARTINFO.date.loaded = 1;
				STARTINFO.date.mmdd = current_date;
				$.updateStartDateDisplay();
			     } else if(STARTINFO.date.mmdd > current_date) {
				STARTINFO.date.mmdd = current_date;
				$.updateStartDateDisplay();
			     }
			     if(STARTINFO.loc.loaded) {
				var loadday = _DATE_TO_DAY[STARTINFO.date.mmdd];
				while(loadday <= current_day) {
				   if(!$("#gg-grid table#gg-day-table-" + loadday).attr("id")) {
				      loadGameday(loadday, json.gameday[loadday]);
				   }
				   setDistanceForDay(loadday, "start", 0);
				   loadday++;
				}
			     } 
			     $.setStartGame(current_date, start_loc);
			  } else if (i < 24) {
			     trip_query += " to:" + teamlocs[start_loc].addr;
			  } else {
			     extra_stops.push(start_loc);
			  }
			  
			  if(i+1 != _TRIP_SELS.length) {
			     var $next_game = $("#gg-grid #" + _TRIP_SELS[i+1]);
			     var tripnext = $next_game.attr("home");
			     total_dist += __dists[start_loc][tripnext];
			     next_trip_day = parseInt($next_game.attr("day"));
			  }				       
			  
			  while(next_day != (next_trip_day + 1) && next_day <= (last_trip_day + 3) && next_day <= _DATE_TO_DAY[LAST_DATE]) {
			     if(!$("#gg-grid table#gg-day-table-" + next_day).attr("id")) {
				loadGameday(next_day, json.gameday[next_day]);
			     }
			     setDistanceForDay(next_day, start_loc, next_day - current_day);
			     next_day++;
			  }
			  
		       }
		       var start_stop = ($.getStartLocQuery() == "") ? 0 : 1;
		       if((_TRIP_SELS.length + start_stop) == 1) {
			  if(last_loc) {
			     $.addMapMarker(last_loc, "Starting Location<br>");
			  } else {
			     $.addMapMarker("START");
			  }
		       } else {
			  $.getRoute(trip_query, extra_stops);
		       }
		    
		    });
       
       $(this).bind("startloaded", 
		    function() {
		       for(var i in _STARTDISTCLEARLIST) {
			  clearDistanceForDay(_STARTDISTCLEARLIST[i]);
		       }
		       _STARTDISTCLEARLIST.splice(0, _STARTDISTCLEARLIST.length);
		       var closest = "xxx";
		       var loaddate = STARTINFO.date.mmdd;
		       var daytoload = _DATE_TO_DAY[loaddate];
		       var daytoload_1 = daytoload + 1;
		       var daytoload_2 = daytoload + 2;
		       setDistanceForDay(daytoload,"start",0);
		       setDistanceForDay(daytoload + 1,"start",0);
		       //setDistanceForDay(daytoload + 2,"start",0);
		       closest = $("#gg-day-table-" + daytoload + " .gg-row-normal:eq(0) div.gg-game-cell").attr("home");
		       if(!closest) {
			  closest = $("#gg-day-table-" + daytoload_1 + " .gg-row-normal:eq(0) div.gg-game-cell").attr("home");
			  loaddate = _DAY_TO_DATE[daytoload_1];
		       }

		       $(this).trigger("selectgame"); // FIXME: document what this action does...
		       
		    });

       function dayJump(a_day) {
	  $(".gg-table-cell").addClass("hidden");
	  var new_date = (a_day > _DATE_TO_DAY[LAST_DATE]- 4) ? _DATE_TO_DAY[LAST_DATE] - 3: a_day;
	  first_visible = new_date;
	  for(var i = 0; i < disp_days; i++) {
	     var $daytablecell = $("#gg-day-table-cell-" + new_date);
	     if($(".gg-table-cell:eq(" + new_date + ")").children().length == 0) {
		loadGameday(new_date, json.gameday[new_date]);
	     }
	     $daytablecell.addClass("visible").removeClass("hidden");
	     last_visible = new_date;
	     new_date++;
	  }
	  setScroll();
       }

       function setScroll() {
	  if(last_visible == last_loaded) {
             $("#gg-nav-right").css("opacity", 0.5);
             $("#gg-nav-right-week").css("opacity", 0.5);
	  } else {
             $("#gg-nav-right").css("opacity", 1);
             $("#gg-nav-right-week").css("opacity", 1);
	  }
	  if(first_visible == first_loaded) {
             $("#gg-nav-left").css("opacity", 0.5);
             $("#gg-nav-left-week").css("opacity", 0.5);
	  } else {
             $("#gg-nav-left").css("opacity", 1);
             $("#gg-nav-left-week").css("opacity", 1);
	  }
	  $.updateCalendar();
       }

       function gotoNext(a_team, select) {
	  var success = 0;
	  var emptyTrip = (_TRIP_SELS.length == 0);
	  var lastDay = first_visible;
	  // FIXME: #57
          // Where should search for next game start?
	  if(!emptyTrip) {
	     lastDay = $("#" + _TRIP_SELS[_TRIP_SELS.length - 1]).attr("day");
	  }
	  first_vis_day = parseInt(lastDay);
	  $("#gg-grid").trigger("updateDay");

	  var thisdate = emptyTrip ? (first_visible): (parseInt(lastDay) + 1);

	  while (success == 0) {
	     if($("#gg-day-table-cell-" + thisdate + " .gg-game-cell[home='"+a_team+"']").length) {
		if(select == 1) {
		   $("#gg-day-table-cell-" + thisdate + " .gg-game-cell[home='"+a_team+"']").click();
		} else {
		   $("#gg-day-table-cell-" + thisdate + " .gg-game-cell[home='"+a_team+"']").mouseover();
		}
		success = 1;
	     } else {
		thisdate++;
	     }
	     if(thisdate > last_visible || success == 1) {
		first_vis_day = thisdate;
		$("#gg-grid").trigger("updateDay");
	     }
	     if(thisdate > last_loaded) {
		$.popupWindow("No more home games this season.<br/>Select an earlier date.");
		success = 1;
	     }
	  }
       }

       return this.each(function() {
			   var $this = $(this);
			   var YEAR = opts.year;
			   var past_hidden = opts.past_hidden;
			   first_visible = past_hidden;
			   days_to_process = Math.max(data_days, disp_days);
			   last_loaded = days_to_process - 1;

			   // Create grid table
			   var $grid = $("#gg-grid");
			   // Create active game row?
			   $grid.html("");
			   // Create row for day tables
			   var $daytablerow = $("<tr></tr>").attr("id", "gg-day-table-row").addClass("gg-table-row").appendTo($grid);

			   for(var d = 0; d < days_to_process; d++) {
			      var $daytablecell = $("<td></td>").attr("id", "gg-day-table-cell-" + d).addClass("gg-table-cell").appendTo($daytablerow);
			      if(d % 2 == 1) {
				 $daytablecell.addClass("gg-table-cell-odd");
			      }
			      var curr_date  = json.gameday[d].date;
			      _DAY_TO_DATE[d] = curr_date;
			      _DATE_TO_DAY[curr_date] = d;

			      // Control visibility of table
			      if((d < past_hidden) || (d >= (past_hidden + disp_days))) { 
				 $daytablecell.addClass("hidden"); 
			      } else { 
				 $daytablecell.addClass("visible"); 
				 loadGameday(d, json.gameday[d]);
				 if(d > last_visible) {
				    last_visible = d;
				 }
			      }      
			   }


			   // Add scroll LEFT - earlier
			   $("#gg-nav-left").click(function() {
						      if(first_visible > first_loaded) {
							 // Hide last visible column
							 $(".gg-table-cell:eq(" + last_visible + ")").addClass("hidden");
							 last_visible--;
							 // Show forst visible -1 column
							 first_visible--;
							 if($(".gg-table-cell:eq(" + first_visible + ")").children().length == 0) {
							    loadGameday(first_visible, json.gameday[first_visible]);
							 }
							 $(".gg-table-cell:eq(" + first_visible + ")").removeClass("hidden");
							 // Update nav markup
							 setScroll();
							 
						      }
						   });


			   // Add scroll RIGHT - later
			   $("#gg-nav-right").click(function() {
						       if(last_visible < last_loaded) {
							  // Hide first visible column
							  $("td#gg-day-table-cell-" + first_visible).addClass("hidden");
							  first_visible++;
							  // Show last visible +1 column
							  last_visible++;
							  if($("td#gg-day-table-cell-" + last_visible).children().length == 0) {
							     loadGameday(last_visible, json.gameday[last_visible]);
							  }
							  $("td#gg-day-table-cell-" + last_visible).removeClass("hidden");
							  // Update nav markup
							  setScroll();
						       }
						    });

			   // Add scroll week LEFT  - earlier 1 week
			   $("#gg-nav-left-week").click(function() { for(var i = 0; i < 7; i++) { $("#gg-nav-left").click(); } });

			   // Add scroll week RIGHT - later 1 week
			   $("#gg-nav-right-week").click(function() { for(var i =0; i < 7; i++) { $("#gg-nav-right").click(); } });

			   // Set Default Scrolls
			   setScroll();

			   // Bind events
			   // - Selecting active game
			   $("#gg-grid div.gg-game-cell").live("click", 
							       function() {
								  var game_sel = $(this).hasClass("active-game");
								  var this_id =  $(this).attr("id");
								  var this_day = $(this).attr("day");
								  var this_day_int = parseInt(this_day);
								  var parent_id = "gg-day-table-" + this_day;
								  
								  if(game_sel) {
								     var toDelete = -1;
								     var days_to_clear = 0;
								     
								     for(var i = 0; i < _TRIP_SELS.length; i++) {
									if(_TRIP_SELS[i] == this_id) {
									   toDelete = i;
									}
								     }
								     if(toDelete == 0) {
									days_to_clear = 4;
									if(_TRIP_SELS.length != 1) {
									   var next_trip_day = parseInt($("#" + _TRIP_SELS[1]).attr("day"));
									   days_to_clear = next_trip_day - this_day_int + 1;
									}
								     } else if(toDelete == _TRIP_SELS.length - 1) {
									days_to_clear = 4;
								     }
								     for(var i=0; i < days_to_clear; i++) {
									clearDistanceForDay(this_day_int+ i);
								     }
								     // Turn off this game and hide distance span
								     $(this).removeClass("active-game");
								     _TRIP_SELS.splice(toDelete,1);
								     
								  } else {
								     var $parent = $("#" + parent_id);
								     var $matches = $("div.active-game", $parent);
								     var $firstmatch = $("div.active-game:eq(0)", $parent);
								     
								     if(SEL_MULT_GAMES) {
									if($matches.length > 1) { // was if == 2
									   var toDelete = -1;
									   for(var i = 0; i < _TRIP_SELS.length; i++) {
									      if(_TRIP_SELS[i] == $firstmatch.attr("id")) {
										 toDelete = i;
									      }
									   }
									   _TRIP_SELS.splice(toDelete,1);
									   // Turn off other game in day
									   $firstmatch.removeClass("active-game");
									}									
								     } else {
									if($matches.length > 0) { // was if == 1
									   var toDelete = -1;
									   for(var i = 0; i < _TRIP_SELS.length; i++) {
									      if(_TRIP_SELS[i] == $matches.attr("id")) {
										 toDelete = i;
									      }
									   }
									   _TRIP_SELS.splice(toDelete,1);
									   // Turn off other game in day
									   $matches.removeClass("active-game");
									}
								     }
								     // turn on *this* game
								     $(this).addClass("active-game").removeClass("gg-game-cell-view");
								     _TRIP_SELS.push(this_id);    
								  }
							       	  
								  // trigger game selected event
								  $(this).trigger("selectgame");
								  
							       });
			   // - Mouseover game for info
			   $(".gg-game-cell").live("mouseover", function() { $(this).addClass("gg-game-cell-view"); } );
			   $(".gg-game-cell").live("mouseout", function() { $(this).removeClass("gg-game-cell-view"); } );

			   $(".gg-nav-cell").mouseover(function() { $(this).addClass("gg-nav-cell-hover");  }).mouseout(function() {  $(this).removeClass("gg-nav-cell-hover");  });
			   
			   // Global Event Hooks FIXME: change to live and add to top?
			   $("#gg-grid").bind("updateDay", function() {
						 dayJump(first_vis_day);
					      });
			   $("#gg-grid").bind("loadDay", function() {
						 loadGameday(day_to_load, json.gameday[day_to_load]);
					      });
			   $("#gg-grid").bind("selectNextGame", function() {
						 gotoNext(gotoTeam, 1);
					      });
			   $("#gg-grid").bind("gotoNextGame", function() {
						 gotoNext(gotoTeam, 0);
					      });
			});

    };

    // other methods?
    function clearDistanceForDay(a_day) {
       var $daytable = $("#gg-day-table-" + a_day);
       $("div.gg-game-cell .gg-cell-dist", $daytable).html("");
       // FIXME: remove large class?
    }

    function setDistanceForDay(a_day, a_start, a_days_past) {
       if(a_day > _DATE_TO_DAY[LAST_DATE]) {
	  a_day = _DATE_TO_DAY[LAST_DATE];
       }
       if(a_start == "start") {
	  _STARTDISTCLEARLIST.push(a_day);
       }
       var $daytable = $("#gg-day-table-" + a_day);
       var cells = $("div.gg-game-cell", $daytable);
       var activeGames = $("div.active-game", $daytable);
       var secondGameID = 9999;
       var first_home = a_start;

       if(activeGames.length == 2) {
	  var id0 = $(activeGames[0]).attr("id");
	  var id1 = $(activeGames[1]).attr("id");
	  var home0 = $(activeGames[0]).attr("home");
	  var home1 = $(activeGames[1]).attr("home");
	  secondGameID = (parseInt(/[0-9]+/.exec(id0)) > parseInt(/[0-9]+/.exec(id1))) ? id0 : id1; 
	  first_home        = (parseInt(/[0-9]+/.exec(id0)) > parseInt(/[0-9]+/.exec(id1))) ? home1 : home0; 
       }

       for(var i = 0; i < cells.length; i++) {
	  var $this_cell = $(cells[i]);
	  var this_id = $this_cell.attr("id");
	  var dist_el = $("div.gg-cell-dist", $this_cell);
	  var dist_str;
	  var active = $this_cell.hasClass("active-game");
	  var secondGame = (parseInt(/[0-9]+/.exec(this_id)) == parseInt(/[0-9]+/.exec(secondGameID)));
	  var end = $this_cell.attr("home");
	  var dist = (secondGame) ? Math.round(__dists[first_home][end]) : Math.round(__dists[a_start][end]);

	  // FIXME: find non-loading start date
	  // update start date on grid scroll with no games selected
	  if(isNaN(dist)) {
	     dist_str = "--- mi";
	     dist = 1;
	  } else {
	     var factor = (active) ? 0 : 0.8;
	     var opacity = 1 - factor*Math.pow((dist/3000),2);
	     dist_str = dist + " mi";
	     $this_cell.css("opacity", opacity);
	  }					
	  
	  $this_cell.parent().parent().attr("dist", dist);
	  $this_cell.attr("dist", dist);
	  dist_el[0].innerHTML = dist_str;
       }
       // ??? Cleaner way of sorting correctly?
       $(".gg-row-normal", $daytable).tsort({order: "asc", attr: "dist"});
       $(".gg-row-normal div.gg-game-cell", $daytable).removeClass("gg-game-cell-large");
       $(".gg-row-normal:lt(4) div.gg-game-cell", $daytable).addClass("gg-game-cell-large");

    }

    function loadGameday(a_gameday, a_data) {

       var curr_tdate    = a_data.tdate;
       var $daytablecell = $("#gg-grid tr td#gg-day-table-cell-" + a_gameday);
       var $daytable     = $("<table></table>").attr("id", "gg-day-table-" + a_gameday).addClass("gg-day-table");
       //if(a_gameday % 2 == 1) { $daytable.addClass("gg-day-table-odd"); }

       // Create date header
       var $header = $("<tr></tr>").attr("id", "gg-header").addClass("gg-header").appendTo($daytable);
       $("<td></td>").attr("id", "gg-header-cell-" + a_gameday).html(curr_tdate).addClass("gg-header-cell").appendTo($header);			      
       
       if(a_data.games.length == 0) {
	  var rowHTML = "<tr><td class=\"gg-no-games-cell\">No Games</td></tr>";
	  $daytable.append(rowHTML);
       }

       // Create rest of grid
       for(var g = 0; g < a_data.games.length; g++) { 
	  var ftime;
	  var min, hour, ap = "p";
	  var cellid = "game-" + a_data.games[g].id;
	  var away = a_data.games[g].a;
	  var home = a_data.games[g].h;
	  var exclude_class = (EXCLUDE_TEAMS[home] == 1) ? "gg-game-cell-exclude" : "";
	  var time = parseInt(a_data.games[g].t);
	  var matchup = "@";
	  
	  var links = new Array();
	  links.push($.getLinksHTML("tickets_sh", home, _DAY_TO_DATE[a_gameday], "Tickets"));
	  links.push($.getLinksHTML("hotel_hotels", home, _DAY_TO_DATE[a_gameday], "Hotel"));
	  //links.push($.getLinksHTML("air", home, _DAY_TO_DATE[a_gameday]));
	  //links.push($.getLinksHTML("car", home, _DAY_TO_DATE[a_gameday]));
	  
	  if(/[A-Z]+/.exec(time)) {
	     ftime = "TBA";
	  } else {
	     min = time % 100;
	     hour = (time - min) / 100;
	     if(hour < 12) { ap = "a"; }
	     if(min < 10) { min = "0" + min; }
	     if(hour > 12) { hour = hour - 12; }
	     ftime =  hour + ":" + min + ap;
	  }
	  
	  var rowHTML = ""; 
	  rowHTML +="<tr class=\"gg-row-normal\" id=\"gg-row-"+g+"\">"; //close me
	  rowHTML +="<td class=\"gg-cell-normal\" id=\"gg-cell-"+g+"\">"; // close me
	  
	  rowHTML +="<div class=\"gg-game-cell " + exclude_class + "\" id=\"" + cellid + "\" home=\"" + home + "\" away=\"" + away + "\" day=\"" + a_gameday + "\">"; //close me
	  
	  rowHTML +="<span class=\"gg-cell-matchup\">"; // close me
	  rowHTML +="<span class=\"gg-cell-away\">" + away + "</span>";
	  rowHTML +="<span class=\"gg-cell-extra\">" + matchup + "</span>";
	  rowHTML +="<span class=\"gg-cell-home\">" + home + "</span></span>"; // closed matchup
	  
	  rowHTML +="<span class=\"gg-cell-time\">" + ftime + "</span>";	  
	  rowHTML +="<div class=\"clearout\"></div>";	  
	  rowHTML +="<div class=\"gg-cell-info\">"; // close me
	  for(var k = 0; k < links.length; k++) {
	     rowHTML += links[k] + " ";
	  }
	  rowHTML += "</div>"; //closing info
	  rowHTML +="<div class=\"gg-cell-dist\"></div></div></td></tr>"; // closing game-cell, cell, row
	  
	  $daytable.append(rowHTML);

       }			      
       
       $daytablecell.append($daytable);

       // Set distance?
       if(_TRIP_SELS.length != 0) {
	  var last_game_selected = $("#" + _TRIP_SELS[_TRIP_SELS.length - 1]).attr("day");
	  var last_home = $("#" + _TRIP_SELS[_TRIP_SELS.length - 1]).attr("home");
	  var days_between = a_gameday - last_game_selected;
	  if(days_between < 4 && days_between > 0) {
	     setDistanceForDay(a_gameday, last_home, days_between);
	  }
       }

    }

    $.getClosestGame = function(a_date) {
       var day = _DATE_TO_DAY[a_date];
       var $daytablerows;
       var found = 0;
       var closest = "none";
       var a_home;
       setDistanceForDay(day, "start");
       //closest = $("#gg-day-table-" + day + " .gg-row-normal:eq(0) div.gg-game-cell").attr("home");
       $daytablerows = $("#gg-day-table-" + day + " .gg-row-normal");
       $daytablerows.each(function() {
			     a_home = $("div.gg-game-cell", $(this)).attr("home");
			     if(!EXCLUDE_TEAMS[a_home] && !found) {
				found  = 1;
				closest = a_home;
			     }
			  });
       return closest;
    }

    $.getGameCellId = function(a_gdate, a_gteam) {
       var gday = _DATE_TO_DAY[a_gdate];
       var gid = $("#gg-day-table-" + gday + " .gg-game-cell[home=" + a_gteam + "]").attr("id");
       return gid;
    }

    $.jumpToDay = function(a_date) {
       if(a_date < SEASONINFO.firstdate.mmdd) {
	  a_date = SEASONINFO.firstdate.mmdd;
       }
       first_vis_day = _DATE_TO_DAY[a_date];
       $("#gg-grid").trigger("updateDay");
    }

    $.selectNextGame = function(a_team) {
       gotoTeam = a_team;
       $("#gg-grid").trigger("selectNextGame");
    }

    $.gotoNextGame = function(a_team) {
       gotoTeam = a_team;
       $("#gg-grid").trigger("gotoNextGame");
    }

    $.loadDayInfo = function(a_date) {
       day_to_load = _DATE_TO_DAY[a_date];
       $("#gg-grid").trigger("loadDay");       
    } 

    $.preloadDays = function(a_start_date, a_num_days) {
       $("#introduction").append("preload: " + a_start_date + " " + a_num_days);
       var day;
       var first_day = _DATE_TO_DAY[a_start_date];
       for(var i = 0; i < a_num_days; i++) {
	  day = first_day + i;
	  if(!$("table#gg-day-table-" + day).attr("id")) {
	     var date = _DAY_TO_DATE[day];
	     day_to_load = day;
	     $("#gg-grid").trigger("loadDay");       
	  }
       }
    }

    $.getFirstDate = function() {
       return _DAY_TO_DATE[first_visible];
   } 

    $.setMultipleSelect = function () { SEL_MULT_GAMES = 1; }
    $.unsetMultipleSelect = function () { SEL_MULT_GAMES = 0; }

    $.selectTeam = function(a_team) {
       var team = /[A-Z]+/.exec(a_team);
       // highlight all home games
       $(".gg-game-cell[home='"+team+"']").addClass("selected-home-game");
       // highlight all away games
       $(".gg-game-cell[away='"+team+"']").addClass("selected-away-game");       
    };

    $.unSelectTeam = function(a_team) {
       var team = /[A-Z]+/.exec(a_team);
       // highlight all home games
       $(".gg-game-cell[home='"+team+"']").removeClass("selected-home-game");
       // highlight all away games
       $(".gg-game-cell[away='"+team+"']").removeClass("selected-away-game");       
    };


    // Use loadTrip to batch load games?
    $.loadTrip = function(a_trip) {
       $.clearTrip();
       for(var i = 0; i < a_trip.length; i++) {
	  if(a_trip[i]) {
	     _TRIP_SELS.push(a_trip[i]);
	     $("#" + a_trip[i]).addClass("active-game");
	  }
       }
       $("#gg-grid").trigger("selectgame");

    };

    $.clearStartLoc = function() {
       teamlocs["START"] = null;
       STARTINFO.loc.loaded = 0;
       // FIXME: clear start loc input?
       $("#tripgenerator-startlocinput").attr("value", "");
    };
    
    // Clear trip
    // Re-sort games?
    // Remove fading?
    $.clearTrip = function() {
       _TRIP_SELS.splice(0, _TRIP_SELS.length);
       $(".active-game").removeClass("active-game");       
       $(".gg-game-cell-large").removeClass("gg-game-cell-large"); 
       $(".gg-game-cell-view").removeClass("gg-game-cell-view"); 
       $(".gg-game-cell").fadeTo("fast", 1);
       $(".gg-cell-dist").html("");
       $("#gg-last-sel").empty();
       // Clear this elements loc and date attr?
       $.clearStartGame();
       $.clearTripviewer();
    };

    $.updateCalendar = function() {
       var first_date = $.getFirstDate();
       var new_day = first_date % 100;
       var new_month = (first_date - new_day) / 100;
       $("#calcon").datepicker('setDate', new Date(2010, new_month - 1, new_day));
       // if no game selected, set distance for day
       if(_TRIP_SELS.length == 0) {
	  STARTINFO.date.loaded = 1;
	  STARTINFO.date.mmdd = first_date;
	  $.updateStartDateDisplay();
	  if(STARTINFO.loc.loaded) {
	     setDistanceForDay(_DATE_TO_DAY[first_date], "start");
	     setDistanceForDay(_DATE_TO_DAY[first_date] + 1, "start");
	     setDistanceForDay(_DATE_TO_DAY[first_date] + 2, "start");
	     setDistanceForDay(_DATE_TO_DAY[first_date] + 3, "start");
	  }
       } else {
	  if(STARTINFO.loc.loaded) {
	     var curr_date = first_date;
	     var first_trip_date = STARTINFO.game.date;
	     while(first_trip_date > curr_date)  {
		setDistanceForDay(_DATE_TO_DAY[curr_date], "start");
		curr_date = _DAY_TO_DATE[_DATE_TO_DAY[curr_date]+1];
	     }
	  }
       }
    };

    $.excludeList = function(a_team) {
       if(a_team == "all") {
	  for(var t in EXCLUDE_TEAMS) {
	     EXCLUDE_TEAMS[t] = 1;
	  }
       } else if(a_team == "none") {
	  for(var t in EXCLUDE_TEAMS) {
	     EXCLUDE_TEAMS[t] = 0;
	  }
       } else {
       
	  if(EXCLUDE_TEAMS[a_team] == null) {
	     EXCLUDE_TEAMS[a_team] = 1;
	  } else {
	     if(EXCLUDE_TEAMS[a_team] == 0) {
		EXCLUDE_TEAMS[a_team] = 1;
	     } else {
		EXCLUDE_TEAMS[a_team] = 0;
	     }
	  }

       }
    };

    $.fn.gamegrid.defaults = {
       days: 5,
       games: 15,
       past_hidden: 2,
       year : 2010
    };

 })(jQuery);