SonOfMaw / iRacing Online Friends

// iRacing Online Friends
// Copyright (c) 2013, Rob Kodey
//
var RK_DEBUG            = 1;
var RK_FRIENDS          = 1;    // Set to 0 to turn off online friends
var RK_SERIES_FILTER    = 0;    // Set to 0 to turn off the series drop-down filter
var RK_SET_SELECTS      = 0;    // Set to 0 to turn off the other automatic select-box selections
//
// ==UserScript==
// @name          iRacing Online Friends
// @version       1.15
// @copyright     2013, Rob Kodey
// @licence       MIT
// @namespace     http://www.kodey.com/
// @description   Highlight your Friends and Watched Drivers on the Dashboard and Series Session pages, so you know where they are driving.
// @include       https://members.iracing.com/membersite/member/*
// @grant         GM_xmlhttpRequest
// ==/UserScript==
//
// Changes:
//  TODO
//        Favorite series instead of filter?  http://members.iracing.com/jforum/posts/list/0/1973793.page
//        http://members.iracing.com/jforum/posts/list/0/1975064.page
//        Show time to upcoming session
//        http://members.iracing.com/membersite/member/GetDriverStatus?friends=1&studied=1

var RK_Version        = 1.15;
//  v1.15 04/27/2013
//        Updated to support the new iRacing Build
//  v1.14 04/08/2013
//        Increased the delay between calls, based on some code warnings coming back from iRacing (sorry!)
//  v1.13 09/10/2012
//        Re-wrote, and hopefully fixed, the Hosted Sessions support, as iRacing organizes the info very differently on that page
//  v1.12 06/09/2012
//        Wow - it's been almost a year since the last update, with 2300 installs!!  Thanks!
//        Fixed: Hosted sessions work again, and show where your friends and studied drivers are registered
//        Re-Added: Mixed-Class stats page car auto-selection - needed for Firefox
//        Fixed: Major Chrome issue by changing the core system that pulls javascript variables out of the page
//  v1.11 06/20/2011
//        Added: Support for Spectator Sessions screen
//        Added: Driver helmets
//        Added: The series filter is back, now with an all new selection dialog to let you choose what you want to see
//  v1.10 12/23/2010
//        Fixed: Detection of the Dashboard page, since iRacing's URL structure changes slightly
//        Temporarily removed: The Series filter, until iRacing brings back the Ticker settings
//  v1.9  11/07/2010
//        Fixed: The "Planner" & "Standings" dashboard widgets automatically select your active Race-Panel series
//  v1.8  11/02/2010
//        Fixed: "Hide yourself"
//        Updated: The Update check dialog, and added option to turn it off.  (Delete the cookie to turn back on)
//        Added: Series filter for Race Panel, Series Stats, and Race Planner Widget so they match your ticker series preferences
//  v1.7  10/30/2010
//        Updated: Support for Season-4 web updates from iRacing
//        Added: Friend highlighting for Races & Qualifiers to Series Sessions Page and Race Widget
//        Added: "Personal Best" stats will automatically select your active Race-Panel car
//        Added: Update check for new versions of the script
//  v1.6  06/02/2010
//        This release has nothing to do with friends - perhaps I should rename the script?
//        (Retired) Added: The "Sereis Stats" page will automatically select your active Race-Panel car
//        Added: Filter the Race Panel's "Series Selection" drop-down so that it matches your ticker series preferences
//  v1.5  05/05/2010
//        Added: support for the new iRacing site design
//        Added: support for the Join a Hosted session page
//        Added: (optional) support for past events pages: Hosted (Results), Events (Results, Replays),
//              Stats (Personal, Friends, Studied)
//        Added: options to turn on/off highlighting for any individual page
//        Fixed: minor bug in the filter that skipped querying empty sessions
//        Fixed: friend/watched matching of members with customer IDs < 10000 (aka shorter than 5 digits!)
//  v1.1  04/26/2010
//        Fixed: encoding issue for driver names with extended characters
//  v1.0  04/18/2010
//        Chrome and Opera support!
//        Driver names now link to Career Stats
//        New display formatting looks much better! (hopefully!!)
//        Where possible, the callback to retrieve the driver list is skipped, to be nice to the servers
//        Friends are now re-highlighted when iRacing refreshes panels, after a couple seconds delay
//  v0.3  04/13/2010
//        Detection of "slow widgets" on the Dashboard should be 100% reliable now
//        Getting closer to supporting Chrome, and Opera...
//  v0.2  04/11/2010
//        Fixed Dashboard detection vs. Press Room
//        session info requests are now spread out a bit, to be nice to the servers
//
//


var RK_oGlobals;
var RK_sPage;
var RK_MaxLoops         = 50;
var RK_LoopWait         = 500;
var RK_DriverWait       = 200;
var RK_Update           = 86400000;     // Check once per day
var RK_Script           = 74072;
var RK_sImages          = ".//IMG[@name]";
var RK_sLinks           = ".//A[@href]";
var RK_oRows            = new Object();
var RK_oIDs             = new Object();
var RK_oDriverTR        = new Object();
var RK_oDriverTD        = new Object();
var RK_sStyleTD         = 'padding:2px; border-bottom:2px solid #000000;';
var RK_Cookie           = 'OnlineFriendsPrefs';
var RK_SELECT           = 'PARENT_SELECT';
var RK_FILTER           = 'SeriesFilter';
var RK_FILTER_LIST      = 'SeriesFilterList';
var RK_FILTERSELECT     = 'RK_FILTER_SELECT';
var RK_ImgPop           = '<img src="http://www.kodey.com/iRacing/new_window.gif">';
var RK_Z                = 1000;
var RK_LicIcons         =
{
  1 : '/member_images/widgets/licenses/stats_licence_r.png',
  2 : '/member_images/widgets/licenses/stats_licence_d.png',
  3 : '/member_images/widgets/licenses/stats_licence_c.png',
  4 : '/member_images/widgets/licenses/stats_licence_b.png',
  5 : '/member_images/widgets/licenses/stats_licence_a.png',
  6 : '/member_images/widgets/licenses/stats_licence_p.png',
  7 : '/member_images/widgets/licenses/stats_licence_pwc.png'
}
var RK_oSessionCellLookup = { 3:1, 4:3, 5:2 };
    // var oRK_SessionDivOffset    = { 1:2, 2:2, 3:2 };  // div offset

var RK_oPrefs     =
{
  Self            : true,
  Dashboard       : true,
  Sessions        : true,
  Join            : true,
  Hosted          : true,
  Results         : true,
  Replays         : true,
  Stats           : true,
  Spectator       : true,
  SeriesResults : true,
  SeriesFilter    : false,
  SkipUpdateCheck : false,
  LastUpdate      : 0
};

var RK_oURLs        =
{
  '/membersite/member/home.do'            : 'Home',
  '/membersite/member/seriessessions.do'  : 'Sessions',
  '/membersite/member/hostedsessions.do'  : 'Join',
  '/membersite/member/hostedresults.jsp'  : 'Hosted',
  '/membersite/member/results.jsp'        : 'Results',
  '/membersite/member/replays.jsp'        : 'Replays',
  '/membersite/member/careerstats.do'     : 'Stats',
  '/membersite/member/spectator.jsp'      : 'Spectator',
  '/membersite/member/seriesstandings.do' : 'Series',
  '/membersite/member/seriesraceresults.do': 'SeriesResults'
};

var RK_sCustDisplayURL    = 'http://members.iracing.com/membersite/member/GetCustDisplayInfo';
var RK_sDriverStatusURL   = 'http://members.iracing.com/membersite/member/GetDriverStatus';
var RK_sOpenSessionURL    = 'http://members.iracing.com/membersite/member/GetOpenSessionDrivers';
var RK_sPrivateSessionURL = 'http://members.iracing.com/membersite/member/GetPrivateSessionRegistered';
var RK_sSessionDriversURL = 'http://members.iracing.com/membersite/member/GetSessionDrivers';
var RK_sSessionTimesURL   = 'http://members.iracing.com/membersite/member/GetSessionTimes';

var TM_sSeriesResultsURL  = 'http://members.iracing.com/membersite/member/SeriesRaceResults';

//////////////////////////////////////////////////

function RK_log(msg)
{
  if(console && RK_DEBUG)
  {
    console.log(msg);
  }
}

//////////////////////////////////////////////////

function RK_SetCookie(name, value, nDays)
{
  var dt          = new Date();
  dt.setDate(dt.getDate()+nDays);
  document.cookie = name+ "=" +escape(value) + ((nDays==null) ? "" : "; expires="+dt.toUTCString());
  // + "; path=/membersite"
} // RK_SetCookie

//////////////////////////////////////////////////

function RK_ReadCookies()
{
  var oCook       = new Object();
	var aCookies    = document.cookie.split('; ');
	for(var i=0; i < aCookies.length; i++)
  {
    var aNVP      = aCookies[i].split("=");
    oCook[aNVP[0]]= aNVP[1];
	}
  return(oCook);
} // RK_ReadCookies

//////////////////////////////////////////////////

function RK_SetPrefs()
{
  RK_log("RK_SetPrefs: " + RK_oPrefs.LastUpdate);
  RK_SetCookie(RK_Cookie, JSON.stringify(RK_oPrefs), 100);
} // RK_SetPrefs

//////////////////////////////////////////////////

var RK_oCookies   = RK_ReadCookies();
if(RK_oCookies[RK_Cookie])
  RK_oPrefs       = JSON.parse(unescape(RK_oCookies[RK_Cookie]));
RK_SetPrefs();

//////////////////////////////////////////////////

function RK_xpath(query, context)
{
  if(!context) context = document;
	return document.evaluate(query, context, null,	XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
}

//////////////////////////////////////////////////

function RK_GetDriverName(oDriver)
{
  var name          = decodeURIComponent(oDriver.displayname || oDriver.dn || oDriver.name);
  name              = name.replace(/\+/g,' ');
  RK_log("RK_GetDriverName: " + name);
  RK_log("RK_GetDriverName driver: " + JSON.stringify(oDriver));
  var oHelmet       = oDriver.helmet || oDriver;
  var sClass        = RK_oIDs[oDriver.custid] || '';
  var sColors       = (oHelmet.c1 || oHelmet.helmcolor1) + ',' + (oHelmet.c2 || oHelmet.helmcolor2) + ',' + (oHelmet.c3 || oHelmet.helmcolor3);
  var sPattern      = oHelmet.p   || oHelmet.helmpattern;
  var sLicense      = oHelmet.ll  || oHelmet.maxlicenselevel;
  var src           = RK_oGlobals.simStatus ? ( 'http://127.0.0.1:32034/helmet.png?size=5&pat='+sPattern+'&lic='+sLicense+'&colors='+sColors) :
                                                '/membersite/images/default/helmet/size_5/helmet.png';
  return( '<div style="padding:0px; text-align:left; height:22px; overflow:hidden;" class="'+sClass+
          '"> <img style="vertical-align: middle; padding:1px;" src="'+src+
          '"> <a class="stats_table_link" href="CareerStats.do?custid='+oDriver.custid+'">'+name+'</a></div>');
} // RK_GetDriverName

//////////////////////////////////////////////////

function RK_InsertDriver(oDriver, cell, oConfig)
{
  // RK_log("RK_InsertDriver");
  if(!RK_oDriverTR[oConfig.name])
  {
    var newTR   = document.createElement('tr');
    while(cell.tagName != 'TD' && cell.parentNode) cell = cell.parentNode;
    cell.parentNode.parentNode.insertBefore(newTR, cell.parentNode.nextSibling);
    newTR.innerHTML  = '<td style="'+RK_sStyleTD+'" colspan="'+oConfig.nSpanL+'"></td><td style="'+RK_sStyleTD+'" colspan="'+oConfig.nSpanR+'"></td>';
    RK_oDriverTD[oConfig.name]  = RK_xpath('.//TD[last()]', newTR).snapshotItem(0);
    RK_oDriverTR[oConfig.name]  = newTR;
  }

  var sPos      = oDriver.pos ? oDriver.pos+'.' : '';
  RK_oDriverTD[oConfig.name].previousSibling.innerHTML  += '<div style="padding:4px 0px 0px 0px; text-align:right; height:18px;">'+sPos+'</div>';
  RK_oDriverTD[oConfig.name].innerHTML                  += RK_GetDriverName(oDriver);
} // RK_InsertDriver

//////////////////////////////////////////////////

function RK_CheckMatch(oDriver, session, oConfig)
{
  if(RK_oIDs[oDriver.custid])
  {
    var tr        = RK_oRows[session];
    var cell      = RK_xpath(RK_sImages, tr).snapshotItem(0) || RK_xpath(RK_sLinks, tr).snapshotItem(0);
    RK_InsertDriver(oDriver, cell, oConfig);
  }
} // RK_CheckMatch

//////////////////////////////////////////////////

function RK_GetDriverInfo(url, delay, oConfig)
{
  window.setTimeout(function()
  {
    RK_log("RK_GetDriverInfo: " + url);
    GM_xmlhttpRequest(
    {
      method: 'GET',
      url: url,
      onload: function(res)
      {
        // RK_log("RK_GetDriverInfo: " + res.responseText);
        var oDrivers = JSON.parse(res.responseText);
        // console.log(JSON.stringify(oDrivers));
        var session;
        if(oDrivers.rows)
        {
          session   = oDrivers.subsessionid;
          oDrivers  = oDrivers.rows;
        }
        else
        {
          session   = oDrivers[0].subsessionid;
        }

        RK_oDriverTR[oConfig.name]  = null;
        RK_oDriverTD[oConfig.name]  = null;
        for(var i=0; i < oDrivers.length; i++)
        {
          oDrivers[i].pos = i+1;
          RK_CheckMatch(oDrivers[i], session, oConfig);
        }
      }
    });
  }, delay);

} // RK_GetDriverInfo

//////////////////////////////////////////////////

function RK_AddClickPref(ele, sName, fnOverride)
{
  ele.style.cursor      = 'pointer';

  var fnToggle          = function()
  {
    RK_oPrefs[sName]    = (RK_oPrefs[sName] ? false : true);
    RK_SetPrefs();
    document.location.reload();
  };

  if(fnOverride) fnToggle = fnOverride;

  ele.addEventListener('click', fnToggle, false);
} // RK_AddClickPref

//////////////////////////////////////////////////

function RK_WriteFriendPref(sName, oParent, sClass)
{
  var oDiv              = document.createElement('div');
  oDiv.className        = sClass;
  oDiv.style.cssText    = 'text-align:right; color:#646464; font-weight:bold; padding:2px;';
  RK_AddClickPref(oDiv, sName);
  oParent.appendChild(oDiv);
  oDiv.innerHTML        = '<span class="stats_table_text" style="padding:2px;">' + (RK_oPrefs[sName] ? 'Hide ' : 'Show ') + (sName == 'Self' ? 'yourself' : 'friends') + '</span>';
} // RK_WriteFriendPref

//////////////////////////////////////////////////

function RK_ProcessDrivers(xRows, oConfig)
{
  RK_log('RK_ProcessDrivers: ' + oConfig.name + ': ' +xRows.snapshotLength);
  xRows.snapshotItem(2).id = 'RK_'+oConfig.name;

  var xLastRow;

  for(var i = 0; i < xRows.snapshotLength; i++)
  {
    var oRow    = xRows.snapshotItem(i);
    var sess    = '';

    // Look for helmet images
    var xImages = RK_xpath(RK_sImages, oRow);
    //RK_log('RK_ProcessDrivers: helmet images: ' + xImages.snapshotLength);
    for(var j = 0; (!sess && j < xImages.snapshotLength); j++)
    {
      //RK_log('RK_ProcessDrivers: helmet name: ' + xImages.snapshotItem(j).name);
      var aMatches = xImages.snapshotItem(j).name.match(/(\d+)/);
      if(aMatches)
      {
        sess    = aMatches[1];
      }
    }

    // Then, if not found, look for results links
    var xLinks  = RK_xpath(RK_sLinks, oRow);
    for(var j = 0; (!sess && j < xLinks.snapshotLength); j++)
    {
      //RK_log('RK_ProcessDrivers: results link: ' + xLinks.snapshotItem(j).href);
      var aMatches = unescape(xLinks.snapshotItem(j).href).match(/launchEventResults?\((\d+),\s*\d+\)/i);
      if(aMatches)
      {
        sess    = aMatches[1];
      }
    }

    //RK_log('RK_ProcessDrivers: session: ' + sess);
    if(sess && RK_oPrefs[RK_sPage])
    {
      RK_oRows[sess] = oRow;
      var reZero  = oConfig.reZero || /[>(]\s*0\/\d+\s*[)<]/;
      if(!(oRow.innerHTML.match(reZero)))
      {
        var url = oConfig.url + '?requestindex='+(i+1)+'&subsessionid='+sess;
        RK_GetDriverInfo(url, i*RK_DriverWait, oConfig);
      }
      else if(RK_DEBUG)
      {
        oRow.childNodes[0].innerHTML += 'skipped';
      }
    }
    
    if(oRow.childNodes.length > 1)
    {
      xLastRow  = oRow;
    }
  }

  var tr                  = document.createElement('tr');
  var tdL                 = document.createElement('td');
  // tdL.style.cssText     = RK_sStyleTD;
  tdL.colSpan             = oConfig.nSpanL;
  tr.appendChild(tdL);
  var tdR                 = document.createElement('td');
  tdR.style.cssText       = RK_sStyleTD;
  tdR.colSpan             = oConfig.nSpanR;
  tr.appendChild(tdR);
  xLastRow.parentNode.insertBefore(tr, xLastRow.nextSibling);

  if(RK_oPrefs[RK_sPage]) RK_WriteFriendPref('Self',    tdR, '');
  RK_WriteFriendPref(RK_sPage,  tdR, 'stats_friend_tr');
  // var oSelfDiv            = document.createElement('div');
  // oSelfDiv.style.cssText  = 'text-align:right; color:#646464; font-weight:bold;';
  // oSelfDiv.addEventListener('click', function()
  // {
    // RK_oPrefs.Self        = !RK_oPrefs.Self
    // RK_SetPrefs();
    // document.location.reload();
  // }, false);
  // tdR.appendChild(oSelfDiv);
  // oSelfDiv.innerHTML      = '<span class="stats_table_text" style="cursor:pointer;">'+(RK_oPrefs.Self ? 'Hide' : 'Show') + ' yourself</span>';

} // RK_ProcessDrivers


//////////////////////////////////////////////////

function RK_ProcessHosted(xRows, oConfig)
{
  RK_log('RK_ProcessHosted: ' + oConfig.name + ': ' +xRows.snapshotLength);
  xRows.snapshotItem(2).id = 'RK_'+oConfig.name;

  RK_log("RK_ProcessHosted: Getting hosted sessions");
  GM_xmlhttpRequest(
  {
    method:   'GET',
    url:      'http://members.iracing.com/membersite/member/GetHostedSessions?ts=0',
    // headers:  { "Content-Type": "application/x-www-form-urlencoded" },
    onload:   function(res)
    {
      RK_log("RK_ProcessHosted: done");
      var tmp1      = res.responseText.replace(/\+/g," ");
      eval("var aHosted="+tmp1+";");

      var oHosted   = {};
      for(var i = 0; i < aHosted.all.length; i++)
      { // cnovert the array into a hash
        oHosted[aHosted.all[i].privatesessionid] = aHosted.all[i];
      }

      var xLastRow;

      for(var i = 0; i < xRows.snapshotLength; i++)
      {
        var oRow      = xRows.snapshotItem(i);
        var hosted    = null;

        // Look for helmet to get the session
        var xImages   = RK_xpath(RK_sImages, oRow);
        for(var j = 0; (j < xImages.snapshotLength); j++)
        {
          RK_log('RK_ProcessHosted: helmet name: ' + xImages.snapshotItem(j).name);
          var aMatches = xImages.snapshotItem(j).name.match(/(\d+)/);
          if(aMatches && !hosted)
          {
            hosted    = oHosted[aMatches[1]];
          }
        }

        if(hosted)
        {
          var pvtid   = hosted.privatesessionid;
          var sess    = hosted.subsessionid;


          RK_log('RK_ProcessHosted: '+i+' session: '+sess+', name: '+hosted.sessionname);
          if(sess && RK_oPrefs[RK_sPage])
          {
            RK_oRows[sess] = oRow;
            var reZero  = oConfig.reZero || /[>(]\s*0\/\d+\s*[)<]/;
            if(!(oRow.innerHTML.match(reZero)))
            {
              var url = oConfig.url + '?requestindex='+(i+1)+'&pvtid='+pvtid+'&ssid='+sess;
              RK_GetDriverInfo(url, i*RK_DriverWait, oConfig);
            }
            else if(RK_DEBUG)
            {
              oRow.childNodes[0].innerHTML += 'skipped';
            }
          }
          
          if(oRow.childNodes.length > 1)
          {
            xLastRow  = oRow;
          }
        }
      }

      var tr                  = document.createElement('tr');
      var tdL                 = document.createElement('td');
      // tdL.style.cssText     = RK_sStyleTD;
      tdL.colSpan             = oConfig.nSpanL;
      tr.appendChild(tdL);
      var tdR                 = document.createElement('td');
      tdR.style.cssText       = RK_sStyleTD;
      tdR.colSpan             = oConfig.nSpanR;
      tr.appendChild(tdR);
      xLastRow.parentNode.insertBefore(tr, xLastRow.nextSibling);

      if(RK_oPrefs[RK_sPage]) RK_WriteFriendPref('Self',    tdR, '');
      RK_WriteFriendPref(RK_sPage,  tdR, 'stats_friend_tr');
    }
  });
} // RK_ProcessHosted


//////////////////////////////////////////////////

function RK_GetRegistered(func)
{
  var url         = RK_sDriverStatusURL;
  RK_log("RK_GetRegistered: " + url);
  GM_xmlhttpRequest(
  {
    method:   'POST',
    url:      url,
    data:     'friends=1&studied=1&onlineOnly=1',
    headers:  { "Content-Type": "application/x-www-form-urlencoded" },
    onload:   function(res)
    {
      RK_log("RK_GetRegistered: done");
      var oDrivers = JSON.parse(res.responseText);

      var oDrvSess  = new Object();
      for(var i = 0; i < oDrivers.fsRacers.length; i++)
      {
        var oDriver = oDrivers.fsRacers[i];
        if(oDriver.sessionId)
        {
          // oDriver.sessionId = '12681297';
          RK_log('RK_GetRegistered: ' + oDriver.name + ", Session: " + oDriver.sessionId + ", Status: " + oDriver.subSessionStatus);
          if(!oDrvSess[oDriver.sessionId])
            oDrvSess[oDriver.sessionId] = new Array();
          oDrvSess[oDriver.sessionId].push(oDriver);
          // if(oDriver.subSessionStatus != 'subses_running')
          // {
            // alert('RK_GetRegistered: ' + oDriver.name + ", Session: " + oDriver.sessionId + ", Status: " + oDriver.subSessionStatus);
          // }
        }
      }

      func(oDrivers, oDrvSess);
    }
  });
} // RK_GetRegistered

//////////////////////////////////////////////////

function RK_RaceRegistered(xRows, oConfig)
{
  RK_log('RK_RaceRegistered: ' + oConfig.name + ': ' +xRows.snapshotLength);
  xRows.snapshotItem(2).id = 'RK_'+oConfig.name;

  RK_GetRegistered(function(oDrivers, oDrvSess)
  {
    var xLastRow;

    for(var i = 0; i < xRows.snapshotLength; i++)
    {
      var oRow    = xRows.snapshotItem(i);
      var sess    = '';
      var cell;
      var xDivs   = RK_xpath('.//DIV[@id]', oRow);
      for(var j = 0; (!sess && j < xDivs.snapshotLength); j++)
      {
        var aMatches = xDivs.snapshotItem(j).id.match(/^(\d{7,})$/);
        if(aMatches)
        {
          cell    = xDivs.snapshotItem(j);
          sess    = aMatches[1];
          RK_log('RK_RaceRegistered: ' + sess);
        }
      }

      if(sess && RK_oPrefs[RK_sPage])
      {
        RK_oDriverTR[oConfig.name]  = null;
        RK_oDriverTD[oConfig.name]  = null;
        var aDrivers        = oDrvSess[sess] || new Array();
        for(var j = 0; j < aDrivers.length; j++)
        {
          var oDriver       = aDrivers[j];
          RK_InsertDriver(oDriver, cell, oConfig);
        }
      }
      
      if(oRow.childNodes.length > 1)
      {
        xLastRow  = oRow;
      }
    }

    var tr                  = document.createElement('tr');
    var tdL                 = document.createElement('td');
    tdL.colSpan             = oConfig.nSpanL;
    tr.appendChild(tdL);
    var tdR                 = document.createElement('td');
    tdR.style.cssText       = RK_sStyleTD;
    tdR.colSpan             = oConfig.nSpanR;
    tr.appendChild(tdR);
    xLastRow.parentNode.insertBefore(tr, xLastRow.nextSibling);

    if(RK_oPrefs[RK_sPage]) RK_WriteFriendPref('Self',    tdR, '');
    RK_WriteFriendPref(RK_sPage,  tdR, 'stats_friend_tr');
  });
} // RK_RaceRegistered

//////////////////////////////////////////////////

function RK_WaitForElement(oConfig, fn, min, count)
{
  if(!count)  count = 1;
  if(!min)    min   = 2;
  RK_log('RK_WaitForElement: ' + count + ': ' + oConfig.name);

  var mutex   = document.getElementById('RK_'+oConfig.name);
  if(!mutex)
  {
    var xRes  = RK_xpath('//TABLE[@'+oConfig.type+'="'+oConfig.name+'"]//TR');
    RK_log('RK_WaitForElement: ' + xRes.snapshotLength + ': ' + oConfig.name);
    if(xRes.snapshotLength > min)
    {
      fn(xRes, oConfig);
    }
    else if(count <= RK_MaxLoops)
    {
      window.setTimeout(function() { RK_WaitForElement(oConfig, fn, min, count+1); }, RK_LoopWait);
    }
  }
} // RK_WaitForElement

//////////////////////////////////////////////////

function RK_GetParentSelect(oOption)
{
  if(oOption && oOption.tagName.toLowerCase() == 'option')
  {
    var oSelect = oOption;
    do
    {
      oSelect   = oSelect.parentNode;
    } while(oSelect.tagName.toLowerCase() != 'select' && oSelect.parentNode);
    return(oSelect);
  }
} // RK_GetParentSelect

//////////////////////////////////////////////////

function RK_GetDropDown(xContainer)
{
  RK_log('RK_GetDropDown');
  var oOptions                  = new Object();
  if(xContainer.snapshotLength)
  {
    var xGroups                 = RK_xpath('.//OPTGROUP', xContainer.snapshotItem(0));
    for(var i=0; i< xGroups.snapshotLength; i++)
    {
      var oGroup                = xGroups.snapshotItem(i);
      oGroup.style.color        = '#808080';
      oGroup.style.fontStyle    = 'italic';
      // oGroup.style.fontWeight   = 'normal';
      RK_log('RK_GetDropDown: Group: ' + oGroup.label + ':' + oGroup.style.fontWeight);
    }

    var xOptions                = RK_xpath('.//OPTION', xContainer.snapshotItem(0));
    for(var i=0; i< xOptions.snapshotLength; i++)
    {
      var oOption               = xOptions.snapshotItem(i);
      if(oOption.disabled)
      {
        oOption.style.color     = '#808080';
        oOption.style.fontStyle = 'normal';
        // oGroup.style.fontWeight = 'normal';
      }
      else
      {
        oOption.style.color     = '#000000';
        oOption.style.fontStyle = 'normal';
        // oGroup.style.fontWeight = 'bold';
      }
      oOptions[oOption.value]   = oOption;
      oOptions[RK_SELECT]       = RK_GetParentSelect(oOption);
      // RK_log('RK_GetDropDown: Option: ' + oOption.value + ':' + oOption.style.fontWeight);
    }
  }
  return(oOptions);
} // RK_GetDropDown

//////////////////////////////////////////////////

function RK_RemoveOption(oOption)
{
  if(oOption && oOption.parentNode)
  {
    // RK_log('RK_RemoveOption: Removing: ' + oOption.text);
    RK_GetParentSelect(oOption).remove(oOption.index);
  }
} // RK_RemoveOption

//////////////////////////////////////////////////

function RK_GetSeriesSelection(oSeries)
{
  var html          = '';
  var td            = '<td style="vertical-align:middle; padding:2px; white-space:nowrap;">';
  var aFilters      = RK_oPrefs[RK_FILTER_LIST] || (new Array());
  if(oSeries)
  {
    html           += td+'<input type="checkbox" name="'+RK_FILTERSELECT+'" value="'+oSeries.seriesid+'" style="margin:4px; vertical-align:middle;" '+(aFilters[oSeries.seriesid] ? '' : 'checked="true"')+'>';
    html           += '<img src="'+(RK_LicIcons[oSeries.serieslicgroupid]||'')+'" style="vertical-align:middle;"></td>';
    html           += td+'<div style="width:60px; height:22px; background:url(/'+unescape(oSeries.whatshotimg)+');">&nbsp;</div></td>';
    html           += td+oSeries.seriesname.replace(/( race)? series/i,'')+/*' : '+oSeries.seriesid+' : '+oSeries.i+' : '+oSeries.licgroupid+' : '+oSeries.serieslicgroupid+*/'</td>';
  }
  else html        += '<td></td><td></td><td></td>';
  return(html);
} // RK_GetSeriesSelection

//////////////////////////////////////////////////

function RK_ShowSeriesFilter()
{
  RK_log('RK_ShowSeriesFilter');
  var aSeries         = RK_oGlobals.SeasonListing;
  if(aSeries)
  {
    var id              = 'RK_SERIES_FILTER';
    var id_toggle       = id+'_TOGGLE';
    var div             = document.createElement('div');
    div.id              = id;
    div.className       = 'stats_friend_tr';
    div.style.cssText   = 'position:absolute; left:100px; top:100px; border:2px solid #000000; z-index:'+(RK_Z++)+'; padding:3px; text-align:left;';
    var html            = '';
    html               += '<div style="text-align:center;">The following Series are currently displayed.  <b>Uncheck</b> a series to hide it from the Series List.</div>';
    html               += '<div style="text-align:center;">Note: If iRacing adds new series, they will not be hidden by default.</div>';

    var oSeriesTable    = new Object();
    for(var i=0; i<aSeries.length; i++)
    {
      var oSeries       = aSeries[i];
      oSeries.i         = i;
      if(oSeries.active)
      {
        if(!(oSeriesTable[oSeries.category])) oSeriesTable[oSeries.category] = new Array();
        oSeriesTable[oSeries.category].push(oSeries);
      }
    }

    function sortFunc(a,b)
    {
      var ret;
      ret               = a.serieslicgroupid - b.serieslicgroupid;
      if(!ret) ret      = a.licgroupid - b.licgroupid;
      if(!ret) ret      = a.seriesname.toLowerCase() > b.seriesname.toLowerCase();
      return(ret);
    }

    oSeriesTable[1].sort(sortFunc);
    oSeriesTable[2].sort(sortFunc);

    var max             = oSeriesTable[1].length > oSeriesTable[2].length ? oSeriesTable[1].length : oSeriesTable[2].length;
    // html               += '<div style="overflow:auto; height:300px; border:1px solid black;"><table border="0" cellspacing="0" cellpadding="0" style="margin:10px;">';
    html               += '<div><table border="0" cellspacing="0" cellpadding="0" style="margin:10px;">';
    for(var i=0; i<max; i++)
    {
      html             += '<tr>';
      html             += '<td style="width:10px;"></td>';
      html             += RK_GetSeriesSelection(oSeriesTable[1][i]);
      html             += '<td style="width:20px;"></td>';
      html             += RK_GetSeriesSelection(oSeriesTable[2][i]);
      html             += '<td style="width:10px;"></td>';
      html             += '</tr>';
    }
    html               += '</table></div>';

    // html               += '<div>Please visit the <a target="_blank" href="http://userscripts.org/scripts/show/'+RK_Script+'">UserScript page '+RK_ImgPop+'</a> to upgrade.</div>';
    // html               += '<div id="'+whatsnewid+'"></div>';
    // html               += '<br><div>Or, visit the <a target="_blank" href="http://members.iracing.com/jforum/posts/list/1275006.page">iRacing Forum thread '+RK_ImgPop+'</a> for more info.</div>';
    html               += '<div style="text-align:center;"><input type="checkbox" id="RK_FILTER_TOGGLE" style="margin:10px 4px; vertical-align:middle;" '+(RK_oPrefs[RK_FILTER] ? 'checked="true"' : '')+'>Turn on the filter (Uncheck this to show all series)</div>';
    html               += '<div style="text-align:center;"><a id="'+id_toggle+'" onclick="return(false);" href="" style="margin-right:10px;">Save</a>|';
    html               += '<a href="" onclick="javascript:document.getElementById(\''+id+'\').style.display=\'none\'; return(false);" style="margin-left:10px;">Cancel</a></div><br>';
    div.innerHTML       = html;
    var child           = document.body.firstChild;
    if(child)
    {
      child.parentNode.insertBefore(div, child);
    }
    RK_AddClickPref(document.getElementById(id_toggle), RK_FILTER, function()
    {
      var aChecks       = document.getElementsByName(RK_FILTERSELECT);
      var oFilter       = {};
      for(var i=0; i<aChecks.length; i++)
      {
        if(!(aChecks[i].checked))
          oFilter[aChecks[i].value] = 1;
      }
      RK_oPrefs[RK_FILTER_LIST] = oFilter;
      RK_oPrefs[RK_FILTER]      = document.getElementById('RK_FILTER_TOGGLE').checked ? true : false;
      RK_SetPrefs();
      document.location.reload();
    });
  }
} // RK_ShowSeriesFilter

//////////////////////////////////////////////////

function RK_Addfilter(oObj)
{
  if(oObj)
  {
    var oSelect                 = oObj[RK_SELECT];
    if(oSelect)
    {
      var oDiv                  = document.createElement('div');
      oDiv.style.position       = oSelect.style.position;
      oDiv.style.left           = oSelect.style.left;
      oDiv.style.top            = oSelect.style.top;
      // oDiv.style.width          = (parseInt(oSelect.style.width) + 16) + 'px';
      oSelect.style.position    = '';
      oSelect.style.left        = '';
      oSelect.style.top         = '';
      oSelect.style.width       = (parseInt(oSelect.style.width) - 22) + 'px';

      var oFilter               = document.createElement('span');
      // oFilter.className         = '';
      oFilter.innerHTML         = '&nbsp;<a href="" onclick="return(false);" class="racingpanel_link" style="font-weight:'+(RK_oPrefs[RK_FILTER]&&0?'bold':'normal')+'">Filter</a>';
      RK_AddClickPref(oFilter, RK_FILTER, RK_ShowSeriesFilter);

      oSelect.parentNode.insertBefore(oDiv, oSelect.nextSibling);
      oDiv.appendChild(oSelect);
      oDiv.appendChild(oFilter);
    }
  }
} // RK_Addfilter

//////////////////////////////////////////////////

function RK_SeriesFilter()
{
  RK_log('RK_SeriesFilter: ' + RK_oPrefs[RK_FILTER]);

  var aSeries         = RK_oGlobals.SeasonListing;
  if(aSeries)
  {
    // var oRacingSeries = RK_GetDropDown(RK_xpath('.//DIV[@id="racingpanel_inner"]'));
    var oRacingSeries = RK_GetDropDown(RK_xpath('.//DIV[@id="racingpanel_series"]'));
    var oSeriesSelect = RK_GetDropDown(RK_xpath('.//SELECT[@id="series_select"]'));

    if(RK_oPrefs[RK_FILTER])
    {
      var oSeriesList   = new Object();
      for(var i=0; i<aSeries.length; i++)
      {
        if(!(oSeriesList[aSeries[i].seriesid]) || !(oSeriesList[aSeries[i].seriesid].active))
          oSeriesList[aSeries[i].seriesid] = aSeries[i];
      }

      var oExclude      = RK_oPrefs[RK_FILTER_LIST];
      for(var i in oExclude)
      {
        var oExclude  = oSeriesList[i];
        if(oExclude)
        {
          if(oRacingSeries) RK_RemoveOption(oRacingSeries[oExclude.seasonid]);
          if(oSeriesSelect) RK_RemoveOption(oSeriesSelect[oExclude.seriesid]);
        }
      }

      // Now, remove the old inactive series
      for(var nSeries in oSeriesList)
      {
        var oSeries   = oSeriesList[nSeries];
        if(!oSeries.active)
        {
          if(oSeriesSelect) RK_RemoveOption(oSeriesSelect[oSeries.seriesid]);
        }
      }
    }

    RK_Addfilter(oRacingSeries);
    RK_Addfilter(oSeriesSelect);
  }
} // RK_SeriesFilter

//////////////////////////////////////////////////

function RK_Select(xOptions, sValue, sFunction)
{
  RK_log('RK_Select: '+sValue+' : '+xOptions.snapshotLength);
  if(xOptions.snapshotLength)
  {
    // var aOptions    = xSelect.snapshotItem(0).childNodes;
    for(var i=0; i < xOptions.snapshotLength; i++)
    {
      var oOption   = xOptions.snapshotItem(i);
      if(oOption.value == sValue && !oOption.selected)
      {
        RK_GetParentSelect(oOption).selectedIndex = oOption.index;
        document.location.href = sFunction;
      }
    }
  }
} // RK_Select

//////////////////////////////////////////////////

function RK_GetObject(aArray, key, value)
{
  RK_log('RK_GetObject');
  for(var i = 0; i < aArray.length; i++)
  {
    if(aArray[i][key] == value)
    {
      RK_log('RK_GetObject: '+aArray[i][key]);
      return(aArray[i]);
    }
  }
} // RK_GetObject

//////////////////////////////////////////////////

function RK_SetSelectBoxes()
{
  var aTables = RK_oSelectPages[RK_sPage];
  if(aTables)
  {
    for(var i=0; i < aTables.length; i++)
    {
      RK_WaitForElement(aTables[i],
      function(xRes, oConfig)
      {
        RK_log('RK_SetSelectBoxes');

        if(RK_sPage == 'Stats')
        RK_Select(RK_xpath('.//DIV[@id="careerTabContent"]/*/SELECT//OPTION'),  // Personal stats car selection
                  RK_oGlobals['racingpaneldata.car'].id,
                  'javascript:CareerTab.getPersonalBests('+RK_oGlobals['racingpaneldata.car'].id+','+RK_oGlobals.MemBean.custid+')');
        if(RK_sPage == 'Series')
        RK_Select(RK_xpath('.//SELECT[@id="class_select"]//OPTION'),            // Series Stats
                  RK_GetObject(RK_oGlobals.CarClassListing, 'name', RK_oGlobals['racingpaneldata.car'].name).id,
                  'javascript:ajax_filter(0,active_stat)');
        // RK_Select(RK_xpath('.//SELECT[@id="series_select"]//OPTION'),           // Planner Widget
                  // RK_oGlobals.racingpaneldata.series.seriesid,
                  // 'javascript:eventPlannerWidget.update_seriesid()');
        // RK_Select(RK_xpath('.//SELECT[@id="standings_series_select"]//OPTION'), // Standing Widget
                  // RK_oGlobals.racingpaneldata.series.seriesid,
                  // 'javascript:regionStandingsWidget.handleSeriesSelect()');
      }, 1);
    }
    window.setTimeout(function() { RK_OnlineFriends(); }, 5000);
  }
} // RK_SetSelectBoxes

//////////////////////////////////////////////////

/*
function RK_MixedClassStats()
{
  if(RK_oGlobals.racingpaneldata)
  {
    RK_log('RK_MixedClassStats');
    var aNames      = RK_oGlobals.racingpaneldata.car.name.split(/\s+/);
    var nMax        = 0;
    var xSelect     = RK_xpath('.//SELECT[@id="class_select"]');
    if(xSelect.snapshotLength)
    {
      var aOptions  = xSelect.snapshotItem(0).childNodes;
      for(var i=0; i < aOptions.length; i++)
      {
        var nCount  = 0;
        for(var j=0; j < aNames.length; j++)
        {
          if(aOptions[i].textContent.match(new RegExp(aNames[j],'i')))
          {
            nCount++;
          }
        }
        if(nCount > nMax && !(aOptions[i].selected))
        {
          nMax      = nCount;
          RK_log('RK_MixedClassStats: Activating: '+aOptions[i].text);
          aOptions[i].selected  = true;
        }
      }
      if(nMax > 0)
      {
        var xGo     = RK_xpath('.//A[@href="javascript:ajax_filter(0,active_stat)"]');
        if(xGo.snapshotLength)
        {
          var oGo   = xGo.snapshotItem(0);
          document.location.href = oGo.href;
        }
      }
    }
  }
} // RK_MixedClassStats
*/

//////////////////////////////////////////////////

function RK_OnlineFriends()
{
  RK_log('RK_OnlineFriends');
  var aTables = RK_oFriendPages[RK_sPage];
  if(aTables)
  {
    for(var i=0; i < aTables.length; i++)
    {
      RK_WaitForElement(aTables[i],
      function(xRes, oConfig)
      {
        var fn  = oConfig.func || RK_ProcessDrivers;
        fn(xRes, oConfig);
        RK_RegisteredFriends();
      });
    }
    window.setTimeout(function() { RK_OnlineFriends(); }, 5000);
  }
} // RK_OnlineFriends

//////////////////////////////////////////////////

function RK_ProcessRegistered(oDrivers, oDrvSess)
{
  RK_log('RK_ProcessRegistered');

  url = RK_sSessionTimesURL;
  RK_log("RK_ProcessRegistered: " + url);
  GM_xmlhttpRequest(
  {
    method:   'POST',
    url:      url,
    headers:  { "Content-Type": "application/x-www-form-urlencoded" },
    onload:   function(res)
    {
      RK_log("RK_ProcessRegistered: done");
      var oSessions   = JSON.parse(res.responseText);
      RK_log(oSessions);

      var xCell       = RK_xpath('//TABLE[@id="sessions_table"]/TBODY/TR/TD');

      for(var i = 0; i < oSessions.d.r.length; i++)
      {
        var oSession  = oSessions.d.r[i];
        if(oSession[1] > 0)
        {
          RK_log('RK_ProcessRegistered: ' + oSession[1] + " : " + oSession[8]);
          var aDrivers        = oDrvSess[oSession[1]] || new Array();
          for(var j = 0; j < aDrivers.length; j++)
          {
            var oDriver       = aDrivers[j];
            RK_log(oDriver);
            var nCell         = RK_oSessionCellLookup[oSession[8]];
            var oCell         = xCell.snapshotItem(nCell);
            if(oCell)
            {
              RK_log(oCell);
              var oDiv        = RK_xpath('.//DIV[@class="sessiontype_contain"]', oCell).snapshotItem(0);
              // var oDiv        = RK_xpath('.//DIV[@class="sessiontype_contain"]//DIV', oCell).snapshotItem(oDivOffset[nCell]++);
              if(oDiv)
              {
                RK_log(oDiv);
                RK_log(oDriver);
                var div       = document.createElement('div');
                div.id        = 'RK_ProcessRegistered';
                // div.cssText   = 'padding:2px; text-align:left;';
                // div.className = '';
                div.innerHTML = RK_GetDriverName(oDriver);
                oDiv.appendChild(div);
              }
            }
          }
        }
      }
    }
  });
} // RK_ProcessRegistered

//////////////////////////////////////////////////

function RK_RegisteredFriends()
{
  RK_log('RK_RegisteredFriends');
  if(RK_sPage == 'Sessions' && !document.getElementById('RK_RegisteredFriends'))
  {
    window.setTimeout(function()
    {
      RK_GetRegistered(RK_ProcessRegistered);
    }, RK_LoopWait);
  }
} // RK_RegisteredFriends

//////////////////////////////////////////////////

function RK_Begin()
{
  RK_log('RK_Begin');

  for(var id in RK_oGlobals.FriendsListing)
  {
    RK_oIDs[id]   = 'stats_friend_tr';
  }
  for(var id in RK_oGlobals.WatchedListing)
  {
    RK_oIDs[id]   = 'stats_watched_tr';
  }
  if(RK_oPrefs.Self && RK_oGlobals.MemBean)
    RK_oIDs[RK_oGlobals.MemBean.custid] = 'stats_self_tr';

  if(RK_SERIES_FILTER)  RK_SeriesFilter();
  // var RK_MIXED_CLASS      = 0;    // (RETIRED) Set to 0 to turn off the automatic Mixed-Class car selection
  // if(RK_MIXED_CLASS)    RK_MixedClassStats();
  if(RK_SET_SELECTS)    RK_SetSelectBoxes();
  if(RK_FRIENDS)        RK_OnlineFriends();
} // RK_Begin

//////////////////////////////////////////////////

function RK_UpdateCheck()
{ // Based on:  http://userscripts.org/scripts/show/20145
  if(!(RK_oPrefs.SkipUpdateCheck))
  {
    RK_log('RK_UpdateCheck');
    var now             = new Date().getTime();
    var last            = RK_oPrefs.LastUpdate || 0;
    var id              = 'RK_UPDATE';
    var id_toggle       = id+'_TOGGLE';
    var whatsnewid      = 'RK_UPDATE_WHATS_NEW';
    if((last + RK_Update) <= now || RK_DEBUG)
    {
      try
      {
        RK_oPrefs.LastUpdate  = now;
        RK_SetPrefs();

        GM_xmlhttpRequest(
        {
          method: 'GET',
          url: 'http://userscripts.org/scripts/source/'+RK_Script+'.meta.js?'+now,
          headers: {'Cache-Control': 'no-cache'},
          onload: function(resp)
          {
            var res             = resp.responseText;
            if(res)
            {
              var aVer            = res.match(/@version\s*(.*?)\s*$/m);
              var aName           = res.match(/@name\s*(.*?)\s*$/m);

              RK_log('RK_UpdateCheck: Site  Ver: '+aVer);
              RK_log('RK_UpdateCheck: Local Ver: '+RK_Version);
              if((aVer && aVer[1] != RK_Version) || RK_DEBUG)
              {
                var div           = document.createElement('div');
                div.id            = id;
                div.className     = 'stats_friend_tr';
                div.style.cssText = 'position:absolute; left:200px; top:380px; border:2px solid #000000; z-index:'+(RK_Z++)+'; padding:3px; text-align:left;';
                var html          = '';
                html             += '<div>There is a new version of "<b>iRacing Online Friends</b>".</div>';
                html             += '<div>Please visit the <a target="_blank" href="http://userscripts.org/scripts/show/'+RK_Script+'">UserScript page '+RK_ImgPop+'</a> to upgrade.</div>';
                html             += '<div id="'+whatsnewid+'"></div>';
                html             += '<br><div>Or, visit the <a target="_blank" href="http://members.iracing.com/jforum/posts/list/1275006.page">iRacing Forum thread '+RK_ImgPop+'</a> for more info.</div>';
                html             += '<br><a id="'+id_toggle+'" onclick="return(false);" href="">Turn off update checks</a>';
                html             += '<div style="text-align:right; float:right;"><a href="" onclick="javascript:document.getElementById(\''+id+'\').style.display=\'none\'; return(false);">Close [X]</a></div>';
                div.innerHTML     = html;
                var child         = document.body.firstChild;
                if(child)
                {
                  child.parentNode.insertBefore(div, child);
                }
                RK_AddClickPref(document.getElementById(id_toggle), 'SkipUpdateCheck');
              }
            }

            GM_xmlhttpRequest(
            {
              method: 'GET',
              url: 'http://www.kodey.com/iRacing/iracing_online_friends.html',
              headers: {'Cache-Control': 'no-cache'},
              onload: function(resp)
              {
                var msg = resp.responseText;
                var div = document.getElementById(whatsnewid);
                
                if(div && msg)
                {
                  div.innerHTML = msg;
                }
              }
            });
          }
        });
      }
      catch (err){}
    }
  }
} // RK_UpdateCheck

//////////////////////////////////////////////////

var RK_oGlobalElem = document.createElement('div');
RK_oGlobalElem.id = "RK_oGlobalElem";
RK_oGlobalElem.style.visibility = "hidden";
RK_oGlobalElem.style.display = "none";
document.body.appendChild(RK_oGlobalElem);

//////////////////////////////////////////////////

function RK_WaitForGlobals(func, count)
{
  RK_log('RK_WaitForGlobals');
  if(!count) count = 1;
  var ret = RK_oGlobalElem.innerHTML;
  if(!ret)
  {
    if(count < RK_MaxLoops)
      window.setTimeout(function()
      {
        RK_WaitForGlobals(func, count+1);
      }, RK_LoopWait);
  }
  else
  {
    RK_oGlobals = JSON.parse(ret);
    RK_log(RK_oGlobals);
    func();
  }
} // RK_WaitForGlobals

//////////////////////////////////////////////////

function RK_SetGlobal(name, val)
{
  document.location.href = 'javascript:void('+name+'='+JSON.stringify(val)+')';
} // RK_SetGlobal

//////////////////////////////////////////////////

function RK_GetGlobals(aVars, func)
{
  var js = 'javascript:(function(){var x={};var a=function(v){x[v]=eval(v)};';
  for(var i=0; i < aVars.length; i++)
  {
    var name = aVars[i];
    js += 'a("'+name+'");';
  }
  js += 'document.getElementById("RK_oGlobalElem").innerHTML=JSON.stringify(x);void(0);})()';
  RK_log('RK_GetGlobals: '+js.length+': '+js);
  document.location.href = js;
  RK_WaitForGlobals(func);
} // RK_GetGlobals

//////////////////////////////////////////////////

var RK_oSelectPages =
{
  Stats :
  [
    { name: 'personalBestsTable',     type: 'id' }
  ],
  Series :
  [
    { name: 'stats_series_table',     type: 'class' }
  ]
};

var RK_oFriendPages =
{
  Dashboard :
  [
    { name: 'WHPracticeTable',        type: 'id',     nSpanL: 2, nSpanR: 5, url: RK_sOpenSessionURL },
    { name: 'WHEventsTable',          type: 'id',     nSpanL: 4, nSpanR: 5, url: RK_sCustDisplayURL },
    { name: 'WHRaceTable',            type: 'id',     nSpanL: 2, nSpanR: 5, url: RK_sOpenSessionURL,    func: RK_RaceRegistered }
  ],
  Sessions :
  [
    { name: 'openpractice_table',     type: 'id',     nSpanL: 1, nSpanR: 3, url: RK_sOpenSessionURL }
  ],
  Join :
  [
    { name: 'hosted_sessions_current',type: 'id',     nSpanL: 9, nSpanR: 6, url: RK_sPrivateSessionURL, func: RK_ProcessHosted }
  ],
  Hosted :
  [
    { name: 'results_table',          type: 'class',  nSpanL: 9, nSpanR: 4, url: RK_sCustDisplayURL }
  ],
  Results :
  [
    { name: 'Results-Table',          type: 'id',  nSpanL: 10, nSpanR: 4, url: RK_sCustDisplayURL }
  ],
  SeriesResults:
  [
      { name: 'series_results_table', type: 'class', nSpanL:  4, nSpanR: 4, url: RK_sCustDisplayURL }
  ],
  Spectator :
  [
    { name: 'spectator_table',        type: 'class',  nSpanL: 3, nSpanR: 5, url: RK_sSessionDriversURL, reZero: /[>(]\s*0\s*[)<]/ }
  ],
  Replays :
  [
    { name: 'replays_data_table',     type: 'class',  nSpanL: 10, nSpanR: 8, url: RK_sCustDisplayURL }
  ],
  Stats :
  [
    { name: 'last10_table',           type: 'class',  nSpanL: 7, nSpanR: 5, url: RK_sCustDisplayURL }
  ]
};  // RK_oFriendPages

//////////////////////////////////////////////////

RK_sPage            = RK_oURLs[document.location.pathname.toLowerCase()];

if(RK_sPage == 'Home')
  if(document.title.match(/Dashboard/i))        RK_sPage  = 'Dashboard';
// if(RK_sPage == 'Stats')
// {
  // if(document.location.href.match(/friendid=/i))        RK_sPage  = 'Friends'
  // else if(document.location.href.match(/watchedid=/i))  RK_sPage  = 'Studied'
  // else if(document.location.href.match(/custid=/i))     RK_sPage  = 'Drivers'
  // else                                                  RK_sPage  = 'Personal'
// }
RK_log('RK_sPage: ' + RK_sPage);

// RK_SetGlobal('ajaxUpdateManager.freqs.whpractice', 300);
RK_GetGlobals([ 'MemBean', 'FriendsListing', 'WatchedListing', 'racingpaneldata.car', 'SeasonListing', 'simStatus', 'CarClassListing'], RK_Begin);
RK_UpdateCheck();

//@ sourceURL=OnlineFriends.js