All pastes #2077467 Raw Edit

Miscellany

public text v1 · immutable
#2077467 ·published 2011-08-20 19:02 UTC
rendered paste body
// TODO: maybe remove Term category entirely, cut down on bandwidth and array size

// TODO: constantize true/false values, 0 = false
PlayerStatus = (function() { "use strict";

  var blah = {};
  
  // constants
  var ACTUALLY_FALSE = 0; // because booleans in javascript are lies
  var ACTUALLY_TRUE = 1;  // booleans = lies
  var FIELD_SEPARATOR = "#"; // separates fields within player entries
  var ENTRY_SEPARATOR = "|"; // separates player entries
  var COUNTDOWN_INTERVAL = 3000; // delay between timer updates
  var TIMER_LENGTH = 24; // the number of characters in the timer
  var TIMER_QUANTUM = 2; // number of chars in the timer that change with each update
  var CAO_PLAYER_URL = "http://crawl.akrasiac.org/scoring/players/"; // generates link to player page
  var WEBTILES_SERVER_NAME = "CDO/Web"; // determines if a game is watchable
  var WEBTILES_URL = "https://tiles.crawl.develz.org/#watch-"; // generates link to watch webtiles games
  
  // control variables
  //is_offline = offline;
  var game_data = []; // stores table data
  var sort_category = "Player"; // default sorting column
  var countdown_timer = TIMER_LENGTH; // initial length of countdown timer
  var splash_screen = ACTUALLY_TRUE; // determines where to write "Loading games..."
  var sort_reversed = ACTUALLY_FALSE; // whether the sorting column is sorted in reverse
  var timer = null; // the timer object
  var timer_is_on = ACTUALLY_FALSE; // determines whether the timer is active
  
  blah.init = function(offline)
  {
    is_offline = offline;
    MakeRequest();
  };

  function MakeRequest()
  {
    // determines whether to show "Loading games..." in the table area or below
    if(splash_screen)
    {
      // "ajax-response" is the name of the div where the table goes
      document.getElementById("ajax-response").innerHTML = "Loading games...";
      splash_screen = ACTUALLY_FALSE; // don't use booleans, they are lies
    }
    else
    {
      // "players" is the name of the td that normally shows the number of players
      document.getElementById("players").innerHTML = "Loading games...";
    }

    // zero the array that holds all the table data
    game_data = [{Player: "Yredelemnul", Version: "4.1", XL: "28", Char: "OMTh", Place: "Zot:27", Term: " ", Idle: "0", Viewers: "2", Server: "DCO"}];
    
    if(is_offline === ACTUALLY_FALSE)
    {
      /* begin ajax shenanigans */
      xmlHttp = getXMLHttp();
   
      xmlHttp.onreadystatechange = function()
      {
        if(xmlHttp.readyState === 4)
        {
          HandleResponse(xmlHttp.responseText);
        }
      };

      xmlHttp.open("GET", "webstatus-ajax.php", true);
      xmlHttp.send(null);
    }
    else // if is_offline is true, bypass the ajax shenanigans
    {
      var fake_response;
      fake_response = "Tester1#0.2#03#JkL4#D:5#80x24#75#7#CAO|" +
                      "Tester2#0.3#04#MnO5#D:6#80x24#90#1#CDO/DGL|" +
                      "Tester3#0.4#05#PqR6#D:7#80x24#0#2#CDO/Web|" +
                      "Tester4#0.5#06#StU7#D:1#80x24#15#3#CAO|" +
                      "Tester5#0.6#07#AbC1#D:2#80x24#30#4#CDO/DGL|" +
                      "Tester6#0.7#01#DeF2#D:3#80x24#45#5#CDO/Web|" +
                      "Tester7#0.1#02#GhI3#D:4#80x24#60#6#CAO";
      //pause = setTimeout("HandleResponse(fake_response)", COUNTDOWN_INTERVAL);
      HandleResponse(fake_response);
    }
  }

  function getXMLHttp()
  {
    var xmlHttp;

    try
    {
      //Firefox, Opera 8.0+, Safari
      xmlHttp = new XMLHttpRequest();
    }
    catch(e1)
    {
      //Internet Explorer
      try
      {
        xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
      }
      catch(e2)
      {
        try
        {
          xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
        }
        catch(e3)
        {
          alert("Your browser does not support AJAX!");
          return false;
        }
      }
    }

    return xmlHttp;
  }
  /* end ajax shenanigans */

  function HandleResponse(response) // response is the string returned by ajax/php
  {
    // debug(response); // uncomment this to see the raw php output

    var split_response = [];
    split_response = response.split(ENTRY_SEPARATOR); // "|" denotes divisions between entries
    
    var game; // holds all data for a single game
    
    // populate the table data array
    for(i = 0; i < (split_response.length); i++)
    {
      game = split_response[i].split(FIELD_SEPARATOR); // "#" denotes divisions between fields
      // game object has the following properties: Player (player name), XL
      // (experience level), Char (character combo), Place (place in the dungeon),
      // Term (terminal size), Idle (idle time), Viewers (number of viewers),
      // Server (where each game is being played)
      game_data[i]={Player: game[0], Version: game[1], XL: ParseXL(game[2]), Char: game[3], Place: game[4], Term: game[5], Idle: game[6], Viewers: game[7], Server: game[8]};
    }
    
    SortData(); // put the data in order according to sort_category
    
    timer = setTimeout("Countdown()", COUNTDOWN_INTERVAL); // begin the countdown-to-refresh timer
    timer_is_on = ACTUALLY_TRUE; // note that the timer is running
  }

  // used to make the XL align and sort properly
  function ParseXL(raw_xl)
  {
    var xl;
    xl = raw_xl.slice(1);
    
    if((xl.length) === 1)
    {
      xl = "&nbsp;" + xl;
    }
    
    return xl;
  }

  // put the data in order according to the global variable sort_category
  // ugh, it's so hacky and slow, gotta redo this at some point
  // TODO: maybe generate sort strings once and then persist them
  function SortData()
  {
    var sort_keys = new Array();
    var game;
    var key;
    
    for(i = 0; i < (game_data.length); i++) // loops for each game in table
    {
      game = game_data[i];
      // example of what key might look like, for sort_category "XL":
      // 27#elliptic#git#CDO-DGL|4
      // the name of the player, version of the game, and server of the game
      // are used as a fallback sort for when the sort category returns a match
      // the player name, version, and server form the unique key
      // the 4 denotes the position in the original unsorted array
      key = FudgeNumbers(game[sort_category]) + FIELD_SEPARATOR + game["Player"] + FIELD_SEPARATOR + game["Version"] + FIELD_SEPARATOR + game["Server"] + ENTRY_SEPARATOR + i;
      
      // put all the keys in an array
      sort_keys[i] = key.toLowerCase(); // sorting is case-sensitive
    }
    
    sort_keys.sort(); // built-in javascript sorting function
    
    // sort_keys now represents the table data in order
    
    var index;
    var sorted_games = new Array();
    
    for(i = 0; i < (sort_keys.length); i++) // loop for each key
    {
      // retrieve the location of the current element in the unsorted array
      index = sort_keys[i].split(ENTRY_SEPARATOR)[1];
      sorted_games[i] = game_data[index]; // move the element
    }
    
    game_data = sorted_games; // reassign the table data array
    
    // this condition is only true when the user has sorted a column in reverse
    // and then allows the table to refresh automatically
    if(sort_reversed)
    {
      game_data = game_data.reverse();
    }
    
    CreateTable(); // create the table!
  }

  // the sorting algorithm can't be numeric, so "Idle" property (which is saved as
  // raw seconds) has to be fudged to make it sort properly
  function FudgeNumbers(value)
  {
    if(sort_category === "Idle")
    {
      switch(value.length)
      {
      case 1:
        value = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" + value;
        break;
      case 2:
        value = "&nbsp;&nbsp;&nbsp;&nbsp;" + value;
        break;
      case 3:
        value = "&nbsp;&nbsp;&nbsp;" + value;
        break;
      case 4:
        value = "&nbsp;&nbsp;" + value;
        break;
      case 5:
        value = "&nbsp;" + value;
        break;
      default:
        break;
      }
    }
    
    return value;
  }

  // create the html table that holds all the table data
  function CreateTable()
  {
    // "data-table" is what is overwritten when the countdown timer elapses
    var table_string = "<table id='container-table'><tr><td colspan=2><table id='data-table'><tr>";
    
    // get the names of the categories, defined as object properties
    game = game_data[0]; 
    
    for(i in game)
    {    
      if(i === sort_category) // visual cue for the current sort category
      {
        table_string += "<th class='sort' onmousedown='return false;' onselectstart='return false;' onclick='PlayerStatus.SortCategories(\"" + i + "\")'>" + i + "</th>";
      }
      else if(!(i === "Term")) // we don't want to see the term category
      {
        // "onmousedown='return false;' onselectstart='return false;'
        // keep the header text from being highlightable
        table_string += "<th onmousedown='return false;' onselectstart='return false;' onclick='PlayerStatus.SortCategories(\"" + i + "\")'>" + i + "</th>";
      }
    }
    
    table_string += "</tr>";
    
    game = "";
    var player_name;
    
    // put the data into the table
    for(i = 0; i < (game_data.length); i++)
    {
      game = game_data[i];
      
      if(i % 2 === 0)
      {
        table_string += "<tr class='norm'>"; // even rows are darker
      }
      else
      {
        table_string += "<tr class='alt'>"; // odd rows are lighter
      }
      
      for(j in game)
      {
        // we want the player's name to be a link to their scoring page
        if(j === "Player")
        {
          player_name = game[j];
          // link to their CAO scoring page
          table_string += "<td>" + player_name.link(CAO_PLAYER_URL + player_name.toLowerCase() + ".html") + "</td>";
        }
        // the idle string is stored as seconds, we want to put it in ##:## format
        else if(j === "Idle")
        {
          table_string += "<td>" + convertIdle(game[j]) + "</td>";
        }
        // if playing webtiles, we want to add a link to watch the game
        else if(j === "Viewers" && game["Server"] === WEBTILES_SERVER_NAME)
        {
          table_string += "<td>" + game[j] + " (" + "Watch".link(WEBTILES_URL + player_name.toLowerCase()) + ")</td>";
        }
        // we don't want to see the term column
        else if(!(j === "Term"))
        {
          table_string += "<td>" + game[j] + "</td>";
        }
      }
      
      table_string += "</tr>";
    }
    
    // close off the table and add the lower table
    // clicking this lower table reloads the upper table
    // two fields: "players" shows the number of games being played,
    // "timer" shows the countdown timer
    table_string += "</table></td></tr><tr id='status-row' onclick='ReloadTable();' onmousedown='return false;' onselectstart='return false;'><td id='players'>" + (game_data.length) + " game" + (game_data.length===1 ? "" : "s") + " in progress</td><td id='timer'></td></tr></table>";
    
    // push the defined table to the page
    document.getElementById('ajax-response').innerHTML = table_string;
    
    // begin to update the countdown timer
    DrawCountdownTimer();
  }

  // if the column to be sorted is the column that is already sorted,
  // re-sort it in the opposite order.
  blah.SortCategories = function(new_category)
  {
    // new_category is the category the user has clicked
    // sort_category is the caregory currently determining sort order
    if(sort_category === new_category)
    {
      // sort_reversed is our global variable to determine the sort order
      if(sort_reversed)
      {
        sort_reversed = ACTUALLY_FALSE; // booleans are lies
      }
      else
      {
        sort_reversed = ACTUALLY_TRUE; // lies
      }
      
      game_data = game_data.reverse(); // don't need to bother re-sorting
      CreateTable();
    }
    else
    {
      sort_reversed = ACTUALLY_FALSE; // lies
      sort_category = new_category;
      SortData(); // proprietary sort method called "terrible slowsort"
    }
  };

  // idle time is passed as pure seconds, we want it in 00:00 format
  function convertIdle(seconds)
  {
    var idle_string = "";
    
    // if less than a minute, draw it as 00:xx or 00:0x
    if(seconds < 60)
    {
      if(seconds < 10)
      {
        idle_string = "00:0" + seconds;
      }
      else
      {
        idle_string = "00:" + seconds;
      }
    }
    else
    {
      minutes = Math.floor(seconds/60);
      seconds = seconds % 60;
      
      if(seconds < 10)
      {
        seconds = "0" + seconds;
      }
      
      if(minutes < 10)
      {
        minutes = "0" + minutes;
      }
    
      idle_string = minutes + ":" + seconds;
    }
    
    return idle_string;
  }

  // timer is redrawn every COUNTDOWN_INTERVAL ms
  function DrawCountdownTimer()
  {
    var timer_string = "<span class='blue'>"; // colors text blue
    
    // if TIMER_QUANTUM = 2, every COUNTDOWN_INTERVAL the timer will have
    // two fewer = signs
    for(i = 0; i < countdown_timer; i+=TIMER_QUANTUM)
    {
      for(j = 0; j < TIMER_QUANTUM; j++)
      {
        timer_string += "=";
      }
    }
    
    timer_string += "</span>";
    
    if(countdown_timer <= TIMER_LENGTH - TIMER_QUANTUM)
    {
      timer_string += "<span class='purple'>--</span>";
    }
    
    if(countdown_timer <= TIMER_LENGTH - (TIMER_QUANTUM * 2))
    {
      timer_string += "<span class='grey'>";
    
      for(i = countdown_timer; i < TIMER_LENGTH - TIMER_QUANTUM; i+=TIMER_QUANTUM)
      {
        for(j = 0; j < TIMER_QUANTUM; j++)
        {
          timer_string += "-";
        }
      }
      
      timer_string += "</span>";
    }

    document.getElementById('timer').innerHTML = timer_string;
  }

  function Countdown()
  {
    timer_is_on = ACTUALLY_FALSE;

    if(countdown_timer > 1)
    {
      countdown_timer -= TIMER_QUANTUM;
      DrawCountdownTimer();
      timer = setTimeout("Countdown()", COUNTDOWN_INTERVAL);
      timer_is_on = ACTUALLY_TRUE;
    }
    else
    {
      ReloadTable();
    }
  }

  function debug(message)
  {
    document.getElementById('debug').innerHTML = message;
  }

  function debugDate()
  {
    var d = new Date();
    document.getElementById('debug').innerHTML = d;
  }

  function ReloadTable()
  {
    countdown_timer = TIMER_LENGTH;
    
    if(timer_is_on)
    {
      clearTimeout(timer);
    }
    
    MakeRequest();
  }
  
  return blah;
})();