/**
 * JavaScript Calendar
 * 
 * @author David Miles
 * @created 09/18/2010
 * @revised 09/23/2010
 */
 
/**
 * Load event
 */
window.onload = function(){
  // Add click events to the anchor tags
  document.getElementById("next").onclick = updateCalendar;
  document.getElementById("prev").onclick = updateCalendar;
 
  // Load the calendar
  loadCalendar();
};
 
/**
 * Click event handler for updating the calendar
 *
 * @return boolean
 */
function updateCalendar(){
 
  // This regular expression will take the URL we're using
  // this anchor and parse the hash in the format M-YYYY
  var regex = new RegExp("#([0-9]{1,2})-([0-9]{4})$");
  var results = regex.exec(this.href);
 
  // Load a new calendar using the results of the regular
  // expressions.
  loadCalendar(results[1], results[2]);
 
  // Don't actually follow the link
  return false;
 
}
 
/**
 * Load a calendar
 */
function loadCalendar(month, year){
 
  // Declarations
  var calendar = new Calendar(month, year);
 
  // Fix for going to the next/previous years
  var nextMonth = calendar.getMonth() == 11 ? 0  : calendar.getMonth() + 1;
  var prevMonth = calendar.getMonth() == 0  ? 11 : calendar.getMonth() - 1;
  var nextYear  = calendar.getMonth() == 11 ? calendar.getYear() + 1 : calendar.getYear();
  var prevYear  = calendar.getMonth() == 0  ? calendar.getYear() - 1 : calendar.getYear();
 
  // Load into page
  document.getElementById("title").innerHTML = calendar.getMonthName() + ", " + calendar.getYear();
  document.getElementById("calendar").innerHTML = calendar;
  document.getElementById("next").href = "#" + nextMonth + "-" + nextYear;
  document.getElementById("prev").href = "#" + prevMonth + "-" + prevYear;
 
}
 
/**
 * Calendar class
 *
 * Builds a table-based calendar when constructed with a month and year
 */
function Calendar(month, year){
 
  // Private Fields {{{
 
  var _month = 0;
  var _year  = 0;
  var _currentDate = new Date();
 
  // }}}
 
  // Constructor {{{
 
  // If the month and year were not passed in, we must set defaults.
  // Otherwise, use the arguments passed in
  _month = month === undefined ? _currentDate.getMonth()    : parseInt(month);
  _year  = year  === undefined ? _currentDate.getFullYear() : parseInt(year);
 
  // To get the first day of the month, create a new Date object
  // and set the year and month to our private field values. Then,
  // set the day of the month to the first
  var _firstDay = new Date();
  _firstDay.setYear(_year);
  _firstDay.setMonth(_month);
  _firstDay.setDate(1);
 
  // A month may start in the middle of the week, and we will need
  // to compensate for this. We store a reference to the day of the
  // week to figure this out.
  var _emptyDays = _firstDay.getDay();
 
  // To get the last day of the month, clone the first day and
  // increment the month, then set the hours to a negative number
  var _lastDay = _firstDay;
  _lastDay.setMonth(_month + 1);
  _lastDay.setHours(-1);
 
  // This will be used as a comparison string to figure out which day
  // is today. If we don't store the current day, month, and year, then
  // we run the risk of Sept. 21 showing up as today for Oct. 21
  var _today = _currentDate.getDate() + "-"
             + _currentDate.getMonth() + "-"
             + _currentDate.getFullYear();
 
  // }}}
 
  // Public Methods {{{
 
  /**
   * Get current month name
   * 
   * @param int i Month index (0-11)
   * @return string
   */
  this.getMonthName = function(){
    var months = new Array("January", "February", "March", "April", "May"
                         , "June", "July", "August", "September", "October"
                         , "November", "December");
    return months[_month];
  }
 
  /**
   * Get the current year
   * 
   * @return int
   */
  this.getYear = function(){
    return _year;
  }
 
  /**
   * Get the current month
   * 
   * @return int
   */
  this.getMonth = function(){
    return _month;
  }
 
  /**
   * Convert to string
   * 
   * @return string
   */
  this.toString = function(){
 
    // Declarations
    var output = "";
    var isToday = "";
    var daysOfMonth = _emptyDays + _lastDay.getDate();
    var daysLeft = daysOfMonth % 7 != 0 ? 7 - (daysOfMonth % 7) : 0;
 
    // Start a table
    output += "<table><tr>";
 
    // Create day-of-the-week headers
    for(var j = 0; j < 7; j++){
      output += '<th>' + getDay(j).substring(0, 3) + '</th>';
    }
 
    // Start a new row
    output += "</tr><tr>";
 
    // If the beginning of the week is not the first day of the 
    // week, compensate by creating an empty column header
    if(_emptyDays > 0){
      output += '<th colspan="' + _emptyDays + '">&nbsp;</th>';
    }
 
    // Now, loop through the days of the month
    for(var i = _emptyDays, day = 1; i < daysOfMonth; i++, day++){
 
      // Is this the end of the week?
      if(i % 7 == 0){
        output += "</tr><tr>";
      }
 
      // Is this the current day?
      isToday = (day + "-" + _month + "-" + _year) == _today ? "today" : "";
 
      // Add to output buffer
      output += '<td id="' + isToday + '">' + day + '</td>';
 
    }
 
    // Complete the table by adding the remaining cells to the end
    if(daysLeft > 0){
      output += '<th colspan="' + daysLeft + '">&nbsp;</th>';
    }
 
    // End our table
    output += "</tr></table>";
 
    return output;
  }
 
  // }}}
 
  // Private Methods (((
 
  /**
   * Get day's string
   * 
   * @param int i Day index (0-6)
   * @return string
   */
  getDay = function(i){
    var days = new Array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday"
                       , "Friday", "Saturday");
    return days[i];
  }
 
  // }}}
 
}
// }}}