var DISPLAY_PARTY = true;
var REPUBLICAN = 0;
var DEMOCRAT = 1;
var PRIMARY_RACES = new Array();

PRIMARY_RACES[REPUBLICAN] = new Array(1797, 1798, 1799);
PRIMARY_RACES[DEMOCRAT] = new Array(1794, 1804, 1805);

var NOTHING_FOUND = "No elections meeting those criteria were found in this state. Close Races will be available after 40 percent of precincts are reporting. Races with Leading Candidates will appear only after polls close and preliminary numbers are available.";

var DEFAULT = -1;
var SENATE = 2;
var HOUSE = 3;
var CLOSE_HOUSE = 4;
var INCUMBENT_LOSING = 5;
var RANGE = 6;
var CUSTOM = 7;
var REPUBLICAN_WINNING = 8;
var DEMOCRAT_WINNING = 9;
var GOVERNOR = 10;
var STATE_HOUSE = 11;
var STATE_SENATE = 12;
var STATE = 13;

var GROUP_SIZE = 5;
var GROUP_REMAINDER = 2;

var DEFINITION_OF_CLOSE = 3;
var MINIMUM_PRECINCTS_FOR_CLOSE = 40;
var COOKIE_DELIMITER = 'ELECTION_TYPE';
var COOKIE_DELIMITER2 = 'CANDIDATE_TYPE';
var URL_DELIMITER = 'elect=';
var OVERRIDE = "override=";

var doThisState = 'NA';

var ALL = 0;
var TOP_TWO = 1;

function stateName( name, abbr )
{
  this.name = name;
  this.abbr = abbr;
}
var MONTHS = new Array ( "Jan.", "Feb.", "March", "April", "May", "June", "July", "Aug.", "Sept.", "Oct.", "Nov.", "Dec.");

var stateNames = new Array( new stateName("Select", ""),
new stateName("Alabama", "ala"),
new stateName("Alaska", "alaska"),
new stateName("Arizona", "ariz"),
new stateName("Arkansas", "ark"),
new stateName("California", "calif"),
new stateName("Colorado", "colo"),
new stateName("Connecticut", "conn"),
new stateName("Delaware", "del"),
new stateName("District of Columbia", "dc"),
new stateName("Florida", "fla"),
new stateName("Georgia", "ga"),
new stateName("Hawaii", "hawaii"),
new stateName("Idaho", "idaho"),
new stateName("Illinois", "ill"),
new stateName("Indiana", "ind"),
new stateName("Iowa", "iowa"),
new stateName("Kansas", "kans"),
new stateName("Kentucky", "ky"),
new stateName("Louisiana", "la"),
new stateName("Maine", "maine"),
new stateName("Maryland", "md"),
new stateName("Massachusetts", "mass"),
new stateName("Michigan", "mich"),
new stateName("Minnesota", "minn"),
new stateName("Mississippi", "miss"),
new stateName("Missouri", "mo"),
new stateName("Montana", "mt"),
new stateName("Nebraska", "neb"),
new stateName("Nevada", "nev"),
new stateName("New Hampshire", "nh"),
new stateName("New Jersey", "nj"),
new stateName("New Mexico", "nm"),
new stateName("New York", "ny"),
new stateName("North Carolina", "nc"),
new stateName("North Dakota", "nd"),
new stateName("Ohio", "ohio"),
new stateName("Oklahoma", "okla"),
new stateName("Oregon", "ore"),
new stateName("Pennsylvania", "pa"),
new stateName("Rhode Island", "ri"),
new stateName("South Carolina", "sc"),
new stateName("South Dakota", "sd"),
new stateName("Tennessee", "tenn"),
new stateName("Texas", "tex"),
new stateName("Utah", "utah"),
new stateName("Vermont", "vt"),
new stateName("Virginia", "va"),
new stateName("Washington", "wash"),
new stateName("West Virginia", "wva"),
new stateName("Wisconsin", "wis"),
new stateName("Wyoming", "wyo")
);

var minimum = 1;
var maximum = GROUP_SIZE;

var elections = new Array();
var candidates = new Array();
var electionsCount = 0;
var candidatesCount = 0;
var electionsRendered = 0;

var candidateType = TOP_TWO;
var electionType = DEFAULT;
var renderedCustom = false;
var linkHtml = '';

/* PUBLIC FUNCTIONS */
var determinedRaceTypes = false;
var hasStateHouse = false;
var hasStateSenate = false;
var hasAttorneyGeneral = false;
var hasClose = false;
var hasIncumbentLosing = false;
var hasSenate = false;
var hasHouse = false;
var hasGov = false;

function hasStateHouseRace()
{
  determineRaceTypes();
  return hasStateHouse;
}

function hasStateSenateRace()
{
  determineRaceTypes();
  return hasStateSenate;
}

function hasCloseRace()
{
  determineRaceTypes();
  return hasClose;
}

function hasIncumbentLosingRace()
{
  determineRaceTypes();
  return hasIncumbentLosing;
}

function hasAttorneyGeneralRace()
{
  determineRaceTypes();
  return hasAttorneyGeneral;
}

// returns boolean indicating if there is a senate race defined
function hasSenateRace()
{
  determineRaceTypes();
  return hasSenate;
}

// returns boolean indicating if there is a house race defined
function hasHouseRace()
{
  determineRaceTypes();
  return hasHouse;
}

// returns boolean indicating if there is a gubernatorial race defined
function hasGovRace()
{
  determineRaceTypes();
  return hasGov;
}

function determineRaceTypes()
{
  if ( determinedRaceTypes == true ) return;
  for ( var count = 0; count < elections.length; count++ )
  {
    if ( ( elections[count].electionType.toLowerCase().indexOf("state") != -1) && (elections[count].name.toLowerCase().indexOf("senate") != -1 ) ) hasStateSenate = true;
    else if ( ( elections[count].electionType.toLowerCase().indexOf("state") != -1) && (elections[count].name.toLowerCase().indexOf("house") != -1 ) ) hasStateHouse = true;
    else if ( elections[count].electionType.toLowerCase().indexOf("senate") != -1 ) hasSenate = true;
    else if ( elections[count].electionType.toLowerCase().indexOf("house") != -1) hasHouse = true;
    else if ( elections[count].electionType.toLowerCase().indexOf("governor") != -1) hasGov = true;
    else if ( elections[count].electionType.toLowerCase().indexOf("statewide") != -1) hasAttorneyGeneral = true;
    else if ( elections[count].incumbentIsLosing() ) hasIncumbentLosing = true;
    else if ( elections[count].isClose() ) hasClose = true;
  }
  determinedRaceTypes = true;
}

function hasRacesWithVotes()
{
  var hasValidRaces = false;
  if (typeof elections == 'undefined') return false;
  for (var count = 0; count < elections.length; count++)
  {
    if ( ( elections[count].candidate[0].votes != '' ) || ( elections[count].candidate[0].percent != '' ) )
    {
      hasValidRaces = true;
      break;
    }
  }
  return hasValidRaces;
}

// returns number of house races
function houseRaceCount()
{
  var houseCount = 0;
  for (var count = 0; count < elections.length; count++)
    if (elections[count].electionType.toLowerCase() == 'house') houseCount++;
  return houseCount + 1;
}

// inserts commas in numbers
function insertCommas(s)
{
  var nString = '';
  var cPos = s.length;
  while (cPos > 3)
  {
    nString = ',' + s.substring(cPos - 3, cPos) + nString;
    cPos -= 3;
  }
  nString = s.substring(0, cPos) + nString;
  return nString;
}

// Write arguments (if any) into the cookie; Write election into "result" layer
function writeIt()
{
  if (writeIt.arguments.length == 1)
  { 
    what = writeIt.arguments[0];
    document.cookie = COOKIE_DELIMITER + '=' + what;
  }
  if ( writeIt.arguments.length == 2 )
  {
    what = writeIt.arguments[0];
    doThisState = writeIt.arguments[1];
  }
  var str = listElections();
  if (document.layers)
  {
    document.layers["result"].document.open();
    document.layers["result"].document.write(str);
    document.layers["result"].document.close();
  }
  else
  {
    document.all["result"].innerHTML = str;
  }
  doThisState = 'NA';
}

// Create a string of all appropriate elections
function listElections()
{
  var str = '';
  electionsRendered = 0;
  var customElection = '';
  electionType = DEFAULT;
  // Display parameters may come from URL or cookie. They are contained in electionType and candidateType
  // Check the URL for overriding display parameters; set them if available
  if ( doThisState != 'NA' )
  {
    electionType = STATE;
    candidateType = ALL;
  }
  else if ( (location.href.indexOf(URL_DELIMITER) != -1) && (renderedCustom == false) )
  {
    electionType = CUSTOM;
    candidateType = ALL;
    renderedCustom = true;
    customElection = unescape( location.href.substring( location.href.indexOf(URL_DELIMITER) + URL_DELIMITER.length ) );
  }
  // ADDED PER BERKO 9/8/02
  else if (location.href.indexOf(OVERRIDE) != -1 )
  {
    electionType = unescape ( location.href.substring ( location.href.indexOf(OVERRIDE)+OVERRIDE.length ) );
  }
  // END ADD PER BERKO 9/8/02
  else
  {
    // Check cookie for display parameters; if present, set them
    if (document.cookie.indexOf(COOKIE_DELIMITER) != -1)
    {
      var startAt = document.cookie.indexOf(COOKIE_DELIMITER) + COOKIE_DELIMITER.length + 1;
      var endAt = (document.cookie.indexOf(';', startAt) == -1) ? document.cookie.length : document.cookie.indexOf(';',startAt);
      electionType = document.cookie.substring(startAt, endAt);
    }
    if (document.cookie.indexOf(COOKIE_DELIMITER2) != -1)
    {
      var startAt = document.cookie.indexOf(COOKIE_DELIMITER2) + COOKIE_DELIMITER2.length + 1;
      var endAt = (document.cookie.indexOf(';', startAt) == -1) ? document.cookie.length : document.cookie.indexOf(';',startAt);
      candidateType = document.cookie.substring(startAt, endAt);
    }
  }
  if ( (candidateType==TOP_TWO) && (!hasRacesWithVotes()) )
  {
    return NOTHING_FOUND;
  }
  str += '<NOBR><FONT FACE="Arial" SIZE="-1" COLOR="#999999">* Incumbent &nbsp; <A HREF="javascript:var pt=window.open(\'http://www.washingtonpost.com/wp-srv/onpolitics/elections/2001/results/parties.htm\',\'\',\'width=400,height=400,scrollbars=yes,resizable=yes\');">Key to Party Abbreviations</A></FONT> &nbsp; ';
  str += '<FONT FACE="Arial" SIZE="-2" COLOR="#990000"><IMG SRC="http://media.washingtonpost.com/wp-srv/images/check.gif" WIDTH="11" HEIGHT="16" BORDER="0" ALT="Declared Winner">Declared winner</FONT></NOBR>';
  // Get links for display parameters 
  str += getLinks();
  // Collect elections that meet display parameters
  for (var count = 0; count < elections.length; count++)
  {
    if ( ( electionType == SENATE ) && ( elections[count].electionType.toLowerCase() == "senate" ) )
      str += elections[count].render();
    else if ( ( electionType == HOUSE ) && ( elections[count].electionType.toLowerCase() == "house" ) )
      str += elections[count].render();
    else if ( ( electionType == STATE_HOUSE ) && ( elections[count].electionType.toLowerCase() == "state" ) && (elections[count].name.toLowerCase().indexOf("house") != -1 ) ) 
      str += elections[count].render();
    else if ( ( electionType == STATE_SENATE ) && ( elections[count].electionType.toLowerCase() == "state" ) && (elections[count].name.toLowerCase().indexOf("senate") != -1 ) )
      str += elections[count].render();
    else if ( ( electionType == GOVERNOR ) && ( elections[count].electionType.toLowerCase() == "governor" ) )
      str += elections[count].render();
    else if ( ( electionType == CLOSE_HOUSE ) && ( elections[count].isClose() ) && (elections[count].candidate[0].hasVotes()) )
      str += elections[count].render();
    else if ( (electionType == INCUMBENT_LOSING) && (elections[count].incumbentIsLosing() ) && (elections[count].candidate[0].hasVotes()) )
      str += elections[count].render();
    else if ( (electionType == RANGE) && ( elections[count].electionType.toLowerCase() == "house" ) && (count <= maximum) && (count >= minimum) )
      str += elections[count].render();
    else if ( (electionType == CUSTOM) && ( (elections[count].name == customElection) || (elections[count].id == customElection ) ))
      str += elections[count].render();
    else if ( electionType == DEFAULT )
      str += elections[count].render();
    else if ( ( electionType == REPUBLICAN_WINNING ) && (elections[count].getWinningParty() == "R") && (elections[count].candidate[0].hasVotes()) )
      str += elections[count].render();
    else if ( ( electionType == DEMOCRAT_WINNING ) && (elections[count].getWinningParty() == "D") && (elections[count].candidate[0].hasVotes()) )
      str += elections[count].render();
    else if ( ( electionType == STATE ) && ( elections[count].state == doThisState ) )
      str += elections[count].render();
  }
  // If more than five elections exist, put display parameter links below them       
  if (electionsRendered > 5) str += getLinks();
  else if ( electionsRendered == 0 ) str += NOTHING_FOUND;
  return str;
}

// Create a list of links (as pull down) with current options selected
function getLinks()
{
  var selected;
  var str = '<TABLE WIDTH="468" CELLPADDING="0" CELLSPACING="0" BORDER="0"><TR VALIGN="top"><TD><FONT FACE="Arial" SIZE="-1" COLOR="#990000"><B>Candidates</B></FONT></TD><TD><FONT FACE="Arial" SIZE="-1" COLOR="#990000"><B>Results</B></FONT></TD><TD><FONT FACE="Arial" SIZE="-1" COLOR="#990000"><B>States</B></FONT></TR><TR VALIGN="top"><TD><FORM><SELECT ONCHANGE="eval(this[selectedIndex].value)">';
  // For each link
  for (var count = 0; count < links.length; count++)
  {
    // Assume it's not selected
    selected = ' ';

    // If the parameter matches this link, select it
    if ( candidateType == links[count].ckValue ) selected = ' SELECTED ';
    if ( electionType == links[count].ckValue ) 
    {
      // "RANGE" parameter must also meet the house races in the range
      if ( (electionType== RANGE) && ( links[count].label.indexOf(minimum + '-' + maximum) != -1 ) ) selected = ' SELECTED ';
      else if (electionType != RANGE) selected = ' SELECTED ';
    }
    str += '\n<OPTION'+selected+'VALUE="'+links[count].clck+'">' + links[count].label;
    if ( (count == 1) ) str += '</SELECT></TD><TD>\n<SELECT ONCHANGE="eval(this[selectedIndex].value)">';
  }
  str += '</SELECT></FORM></TD><TD><FORM>';
  if ( ( typeof pageType != 'undefined' ) && ( pageType == 'NATIONAL_SUMMARY' ) )
  {
    var displayedStates = ';null;';
    str += '<SELECT ONCHANGE="writeIt(STATE,this[this.selectedIndex].value)">';
    str += '<OPTION VALUE="NA">All';
    for (var count = 0; count < elections.length; count++)
    {
      if ( displayedStates.indexOf(';'+elections[count].state+';') == -1 )
      {
        selected = ' ';
        if ( elections[count].state==doThisState ) selected = ' SELECTED ';
        str += '<OPTION'+selected+'VALUE="'+ elections[count].state +'">' + elections[count].state;
        displayedStates += ';' + elections[count].state + ';';
      }
    }
    str += '</SELECT>';
  }
  else
  {
    str += '<SELECT ONCHANGE="location.href=\'http://www.washingtonpost.com/wp-srv/elections/2002/general/\' + this[this.selectedIndex].value + \'.html\'">';
    for (var count = 0; count < stateNames.length; count++)
    {
      str += '<OPTION VALUE="'+ stateNames[count].abbr +'">' + stateNames[count].name;
    }
    str += '</SELECT>';
  }
  str += '</FORM></TD></TR></TABLE>';
  return str;
}

function setCandidates(style)
{
  document.cookie = COOKIE_DELIMITER2 + '=' + style;
}

// OBJECTS
// Election object contains election-specific information and methods for manipulating election-specific info
function Election(name)
{
  this.name = name;
  this.id = 0;
  this.electionType = "";
  this.candidate = new Array();
  this.candidateCount = 0;
  this.precinctsReporting = 0;
  this.note = "";
  this.pollsAreClosed = false;
  this.source = 'AP';
  this.pollClosingTime = 'NA';
  this.timestamp = 'NA';
  this.state = 'NA';
  this.articles = new Array();
  this.isClose = _isClose;
  this.incumbentIsLosing = _incumbentIsLosing;
  this.addCandidate = _addCandidate;
  this.aC = _addCandidate;
  this.render = _renderElection;
  this.getWinningParty = _getWinningParty;
  this.addArticle = _addArticle;
  this.renderArticles = _renderArticles;
}

// Candidate object contains candidate-specific information and display method
function Candidate(name, party, votes, percent)
{
  this.name = name;
  this.party = party;
  this.percent = percent;
  this.votes = votes;
  this.isWinner = false;
  this.render = _renderCandidate;
  this.hasVotes = _hasVotes;
  if (Candidate.arguments.length == 5) this.isWinner = Candidate.arguments[4];
}

// Article object contains article-specific information and display method
function Article(headline, byline, source, url)
{
  this.headline = headline;
  this.byline = byline;
  this.source = source;
  this.url = url;
  this.render = _renderArticle;
}

function _hasVotes()
{
  var voteAsString = this.votes;
  while (voteAsString.indexOf(",") != -1)
    voteAsString = voteAsString.substring(0, voteAsString.indexOf(",")) +
                   voteAsString.substring(voteAsString.indexOf(",") +1, voteAsString.length);
  if ( ( parseInt(voteAsString) > 0 ) || (this.isWinner == true) ) return true;
  else return false;                   
}

// DisplayLink object contains info necessary for writing links and detecting which links are currently selected
function DisplayLink(c, l, v)
{
  this.clck = c;
  this.label = l;
  this.ckValue = v;
}

function _addArticle(headline, byline, source, url)
{
  this.articles[this.articles.length] = new Article(headline, byline, source, url)
}

function _renderArticle()
{
  var buffer = '<FONT FACE="Verdana,Arial" SIZE="-2">';
  if (this.url != '') buffer += '<A HREF="'+this.url+'">';
  if (this.headline != '') buffer += '<B>' + this.headline + '</B>';
  if (this.url != '') buffer += '</A> ';
  if (this.byline != '') buffer += this.byline;
  if (this.source != '') buffer += '<I> ('+this.source+')</I> ';
  buffer += '</B> ';
  return buffer;
}

function _renderArticles()
{
  var buffer = '';
  for (var count = 0; count < this.articles.length; count++)
  {
    buffer += this.articles[count].render();
    if ( count < this.articles.length - 1 ) buffer += '<BR>';
  }
  if (this.articles.length > 0) buffer += '<BR><BR>';
  return buffer;
}

function _getWinningParty()
{
  if (this.candidate.length > 0) return this.candidate[0].party.toUpperCase();
  else return "";
}

function _incumbentIsLosing()
{
  var incumbentIsLosing = false;
  if ( this.candidate[0].name.indexOf("*") == -1)
  {
    for ( var count = 1; count < this.candidate.length; count++ ) 
    {
      if ( this.candidate[count].name.indexOf("*") != -1) incumbentIsLosing = true;
    }
  }
  return incumbentIsLosing;
}

function _isClose()
{
  var isClose = false;
  if ( this.candidate.length > 1 )
  {
    if ( ( this.candidate[0].percent - this.candidate[1].percent <= DEFINITION_OF_CLOSE ) 
       &&( this.precinctsReporting >= MINIMUM_PRECINCTS_FOR_CLOSE )
       )
    {
      isClose = true;
    }
  }
  return isClose;
}

function _renderCandidate()
{
  var str = '';
  str += '<TD WIDTH="15">';
  if (this.isWinner) str += '<IMG SRC="http://media.washingtonpost.com/wp-srv/images/check.gif" WIDTH="11" HEIGHT="16" BORDER="0" ALT="Declared Winner">';
  else str += '<SPACER TYPE="block" WIDTH="15" HEIGHT="1">';
  str += '</TD>';
  str += '<TD WIDTH="300"><FONT FACE="Verdana,Arial" SIZE="-1">';
  str += this.name;
  str += '</FONT></TD>';
  if (DISPLAY_PARTY) str += '<TD WIDTH="41"><FONT FACE="Verdana,Arial" SIZE="-1">&nbsp;' + this.party + '</FONT></TD>';
  else str += '<TD WIDTH="41"><SPACER TYPE="block" WIDTH="41" HEIGHT="1"></TD>';
  str += '<TD WIDTH="76" ALIGN="right"><FONT FACE="Verdana,Arial" SIZE="-1">';
  str += this.votes;
  str += '</FONT></TD><TD WIDTH="36" ALIGN="right"><FONT FACE="Verdana,Arial" SIZE="-1">';
  str += this.percent;
  str += '&nbsp;</FONT></TD>';
  return str;
}

function _renderElection()
{
  if( this.name.indexOf ("State House") != -1 ) leadingCandNum = 3;
  else leadingCandNum = 2;
  if( ( thisNode.indexOf('metro') == -1 ) &&
      ( ( this.electionType == 'State' ) ||
        ( this.electionType == 'Statewide' )
      )
    )
  {
    return ""; 
  }
  var proceed = false;
  if ( typeof RACE_PARTY != "undefined" )
  {
    for ( var count = 0; count < PRIMARY_RACES[RACE_PARTY].length; count++ )
    {
      if ( this.id ==  PRIMARY_RACES[RACE_PARTY][count] )
      {
        proceed = true;
        break;
      }
    }
  }
  else proceed = true;
  if ( proceed == false ) return "";
  var pattern = /,/ig;
  var otherPercent = 0;
  var otherVotes = 0;
  var otherCandidateCount = 0;
  var str = '';
  electionsRendered++;
  str += '<TABLE WIDTH="468" CELLPADDING="1" CELLSPACING="0" BORDER="0">';
  if (this.pollsAreClosed) 
    str += '<TR><TD COLSPAN="5"><FONT FACE="Arial" SIZE="-2" COLOR="#999999">Precincts reporting: ' + this.precinctsReporting + ' percent </FONT></TD></TR>';
  else if (this.pollClosingTime != 'NA')
  {
    str += '<TR><TD COLSPAN="5" HEIGHT="10"><FONT FACE="Arial" SIZE="-2" COLOR="#999999">First polls close at: ';
    str += (this.pollClosingTime.getHours() > 12 ? this.pollClosingTime.getHours() - 12 : (this.pollClosingTime.getHours()==0?'12':this.pollClosingTime.getHours()) );
    str += ':';
    str += (this.pollClosingTime.getMinutes() < 10 ? '0' + this.pollClosingTime.getMinutes() : this.pollClosingTime.getMinutes());
    str += (this.pollClosingTime.getHours() > 12 ? 'pm' : 'am' );
    str += ' EST</FONT></TD></TR>';
  }
  else
    str += '<TR><TD COLSPAN="5" HEIGHT="10"><SPACER TYPE="block" WIDTH="1" HEIGHT="10"></TD></TR>';
  str += '<TR BGCOLOR="#333333"><TD COLSPAN="2"><FONT FACE="Verdana,Arial" SIZE="-1" COLOR="#FFFFFF"><B>&nbsp;';
  str += this.name;
  str += '</B></FONT></TD>';
  str += '<TD><FONT FACE="Verdana,Arial" SIZE="-1" COLOR="#FFFFFF">'+(DISPLAY_PARTY ? 'Party' : '<SPACER TYPE="block" WIDTH="1" HEIGHT="1">')+'</FONT></TD><TD ALIGN="right"><FONT FACE="Verdana,Arial" SIZE="-1" COLOR="#FFFFFF">Votes</FONT></TD><TD ALIGN="right"><FONT FACE="Verdana,Arial" SIZE="-1" COLOR="#FFFFFF">%&nbsp;</FONT></TD>';
  str += '</TR>';
  var myCandCount = 0;
  var rCount = 0;
  for (var count = 0; count < this.candidate.length; count++)
  {
    if ( (candidateType == TOP_TWO) && (myCandCount >= leadingCandNum) )
    {
      otherPercent += parseInt(this.candidate[count].percent);
      var tVal = this.candidate[count].votes.replace(pattern,"");
      otherVotes += parseInt(this.candidate[count].votes.replace(pattern,""));
      otherCandidateCount++;
    }
    else
    {
      color = (count % 2 == 0) ? 'CCCCCC' : 'FFFFFF';
      str += '<TR BGCOLOR="#'+color+'">';
      str += this.candidate[count].render();
      str += '</TR>';
      if ( (count < this.candidate.length - 1) 
        && ( this.candidate[count].votes != this.candidate[count + 1].votes ) 
         ) myCandCount++;
      rCount++;
    }
  }
  if ( (candidateType == TOP_TWO) && ( (otherPercent > 0) || (otherVotes > 0) ) )
  {
    color = (rCount % 2 == 0) ? "CCCCCC" : "FFFFFF";
    str += '<TR BGCOLOR="#'+color+'"><TD WIDTH="15"><SPACER TYPE="block" WIDTH="15" HEIGHT="1"></TD>';
    str += '<TD WIDTH="300"><FONT FACE="Verdana,Arial" SIZE="-1">Other ('+otherCandidateCount+' candidate'+((otherCandidateCount==1) ? '' : 's')+')';
    str += '</FONT></TD><TD><FONT FACE="Verdana,Arial" SIZE="-1">&nbsp;';
    str += '</FONT></TD><TD ALIGN="right"><FONT FACE="Verdana,Arial" SIZE="-1">' + insertCommas(otherVotes + '');
    str += '</FONT></TD><TD ALIGN="right"><FONT FACE="Verdana,Arial" SIZE="-1">';
    str += otherPercent;
    str += '&nbsp;</FONT></TD>';
    str += '</TR>';
  }
  if ( this.candidate.length < 2 )
  {
    str += '<TR BGCOLOR="#FFFFFF"><TD WIDTH="15"><SPACER TYPE="block" WIDTH="15" HEIGHT="1"></TD><TD COLSPAN="4"><FONT FACE="Verdana,Arial" SIZE="-1">Uncontested</FONT></TD></TR>';
  }
  if ( this.note != '' )
  {
    str += '<TR><TD COLSPAN="5" ALIGN="left"><FONT FACE="Arial" SIZE="-1" COLOR="#333333">'+this.note+'</FONT></TD></TR>';
  }
  str += '<TR><TD COLSPAN="3" ALIGN="left"><FONT FACE="Arial" SIZE="-2" COLOR="#999999">';
  str += (this.timestamp == 'NA' ? '': 'Updated: '+ (this.timestamp.getMonth()+2) + '/' + this.timestamp.getDate() + '/2002, ' + (this.timestamp.getHours() > 12 ? this.timestamp.getHours() - 12 : (this.timestamp.getHours() == 0 ? 12 : this.timestamp.getHours()))+':'+(this.timestamp.getMinutes() < 10 ? '0'+this.timestamp.getMinutes(): this.timestamp.getMinutes()) + ' ' + (this.timestamp.getHours() > 12 ? 'pm': 'am') )+' EST</FONT></TD>';
  str += '<TD COLSPAN="2" ALIGN="right"><FONT FACE="Arial" SIZE="-2" COLOR="#999999">Source: ';
  str += this.source;
  str += '</FONT></TD></TR>';
  str += '<TR><TD COLSPAN="5">'+this.renderArticles()+'</TD></TR>';
  str += '<TR><TD COLSPAN="5" HEIGHT="36"><SPACER TYPE="block" WIDTH="1" HEIGHT="36"></TD></TR>';
  str += '</TABLE>';
  return str;
}

function _addCandidate(c)
{
  this.candidate[this.candidateCount++] = c;
}

