/*

  calendar.js

  purpose: creates a simple table calendar to show events
  technique: manipulates the DOM using JavaScript
  tested: Windows XP Professional - Firefox 2, Internet Explorer 7 
  author: david t. gay (davidtgay) - reach me at gmail.com
  date: august 17, 2008

*/

var today;
var month;
var monthName;
var year;
var events;

/*

  getEvent

  given a day of the month (date),
  returns an array giving the corresponding event
  from the events array defined in the parent document.

  if there is no corresponding event,
  returns an array of one element (boolean false).

*/
function getEvent(date) {

  strYear=String(year);
  strMonth=String(month+1);
  if (month<9) {strMonth='0'+strMonth;}
  strDay=String(date);
  if (date<10) {strDay='0'+strDay;}
  strDate=strYear+'-'+strMonth+'-'+strDay;

  for (i=0; i<events.length; i++) {

    eventFound=events[i].split('|');
    if (eventFound[0]==strDate) {break;}

  }

  if (eventFound[0]!=strDate) {eventFound=new Array(false);}

  return eventFound;

}

/*

  initializeCalendar

  caches today's date as shown on the client system,
  caches values for today's month and year,
  and draws the calendar in the <div id="calendar"></div> element.

  returns true always.

*/
function initializeCalendar (){

  today=new Date()
  month=today.getMonth();
  year=today.getFullYear();

/*

  events for calendar.js

  to define events for the calendar,
  add them to this array.
  they don't need to be in order,
  but putting them in order helps
  keep the array clean.
  use this format:

  date | priority | link | title

  dates must be yyyy-mm-dd (2008-08-16).
  priorities must be top, mid, or low.
  if you don't have a link for an event, use #calendar.

*/
  events=new Array(

"2008-07-28|top|#calendar|Lytton's Birthday",
"2008-08-18|top|http://lyttonbell.com/anniversary.html|Anniversary",
"2008-08-28|mid|#calendar|Ruth's Birthday",
"2008-09-01|low|#calendar|Labor Day",
"2008-10-16|top|http://lyttonbell.com/class.html|Everyday Poetry",
"2008-10-23|top|http://lyttonbell.com/class.html|Everyday Poetry",
"2009-02-26|mid|#calendar|Reading at Luna's Cafe"

)

  return drawCalendar();

}  

/*

  prevMonth

  subtracts one from the currently shown month,
  cycles back to December of the previous year if necessary,
  and redraws the calendar to show the new month.

  returns true always.

*/
function prevMonth() {

  month--;

  if (month<0) {
    month=11;
    year--;
  }

  return drawCalendar();

}

/*

  nextMonth

  adds one to the currently shown month,
  cycles forward to January of the next year if necessary,
  and redraws the calendar to show the new month.

  returns true always.

*/
function nextMonth() {

  month++;

  if (11<month) {
    month=0;
    year++;
  }

  return drawCalendar();

}

/*

  drawCalendar

  calculates the month name, number of days in the months, and starting date;
  empties the <div id="calendar"></div> element;
  adds an "events" heading 2;
  adds a table to show the calendar;
  adds a thead element with two rows;
    top row gives links for previous and next month and shows the current month name
    bottom row is <tr class="days"> and shows the days of the week
  adds a tbody element with all the days of the month in table cells;
    the last cell in each row has <td class="last"> so the left border gets drawn
    the cell corresponding to today's date has <td class="today">
    cells corresponding to events have links with 
      <a class="top">, <a class="mid">, or <a class="low">
      according to event priority (top, mid, or low).

  returns true always.

*/
function drawCalendar() {

  var divCalendar=document.getElementById('calendar');

  days=31;

  // calculate month name
  switch(month) {
    case 0: monthName='January'; break;
    case 1: monthName='February'; (year/4==Math.floor(year/4) ? days=29 : days=28); break;
    case 2: monthName='March'; break;
    case 3: monthName='April'; days=30; break;
    case 4: monthName='May'; break;
    case 5: monthName='June'; days=30; break;
    case 6: monthName='July'; break;
    case 7: monthName='August'; break;
    case 8: monthName='September'; days=30; break;
    case 9: monthName='October'; break;
    case 10: monthName='November'; days=30; break;
    case 11: monthName='December'; break;
  }

  dateStart=new Date(year, month, 1);

  // remove all nodes
  while (0 < divCalendar.childNodes.length)
  {
    divCalendar.removeChild(divCalendar.firstChild);
  } 

  // add new nodes

  // create heading
  objH2=document.createElement('h2');

    // create text
    objText=document.createTextNode('events');
  
    // add nodes
    objH2.appendChild(objText);
    divCalendar.appendChild(objH2);

  // create table
  objTable=document.createElement('table');
  objTable.setAttribute('cellSpacing',0);
  objTable.setAttribute('summary','Columns go from Sunday to Saturday. Rows represents weeks of the month.');

    // create caption
    objCaption=document.createElement('caption');
    
      // create text
      objText=document.createTextNode('Calendar of events for the month shown.');

      // add the nodes
      objCaption.appendChild(objText);
      objTable.appendChild(objCaption);
  
    // create table head
    objThead=document.createElement('thead');

      // create table row
      objTR=document.createElement('tr');

        // create previous month cell
        objTH=document.createElement('th');

          // create link
          objA=document.createElement('a');
          objA.setAttribute('href', '#calendar');
          objA.setAttribute('title', 'previous month');
          if (objA.addEventListener){objA.addEventListener('click', prevMonth, false);} else if (objA.attachEvent) {objA.attachEvent('onclick', prevMonth);}

          // create text
          objText=document.createTextNode('<');
  
          // add nodes
          objA.appendChild(objText);
          objTH.appendChild(objA);
          objTR.appendChild(objTH);

        // create month title
        objTH=document.createElement('th');
        objTH.setAttribute('colSpan', 5);

          // create text
          objText=document.createTextNode(monthName+' '+String(year));

          // add nodes
          objTH.appendChild(objText);
          objTR.appendChild(objTH);

        // create next month cell
        objTH=document.createElement('th');

          // create link
          objA=document.createElement('a');
          objA.setAttribute('href', '#calendar');
          objA.setAttribute('title', 'next month');
          if (objA.addEventListener) {objA.addEventListener('click', nextMonth, false);} else if (objA.attachEvent) {objA.attachEvent('onclick', nextMonth);}

          // create text
          objText=document.createTextNode('>');
  
          // add nodes
          objA.appendChild(objText);
          objTH.appendChild(objA);
          objTR.appendChild(objTH);

      // add node
      objThead.appendChild(objTR);

      // create table row
      objTR=document.createElement('tr');
      objTR.setAttribute('class', 'days');
      try {objTR.className='days';} catch(objErr) {}

        // create cell
        objTH=document.createElement('th');
        objTH.setAttribute('scope', 'col');

          // create text
          objText=document.createTextNode('S');

          // add nodes
          objTH.appendChild(objText);
          objTR.appendChild(objTH);

        // create cell
        objTH=document.createElement('th');
        objTH.setAttribute('scope', 'col');

          // create text
          objText=document.createTextNode('M');

          // add nodes
          objTH.appendChild(objText);
          objTR.appendChild(objTH);

        // create cell
        objTH=document.createElement('th');
        objTH.setAttribute('scope', 'col');

          // create text
          objText=document.createTextNode('T');

          // add nodes
          objTH.appendChild(objText);
          objTR.appendChild(objTH);

        // create cell
        objTH=document.createElement('th');
        objTH.setAttribute('scope', 'col');

          // create text
          objText=document.createTextNode('W');

          // add nodes
          objTH.appendChild(objText);
          objTR.appendChild(objTH);

        // create cell
        objTH=document.createElement('th');
        objTH.setAttribute('scope', 'col');

          // create text
          objText=document.createTextNode('T');

          // add nodes
          objTH.appendChild(objText);
          objTR.appendChild(objTH);

        // create cell
        objTH=document.createElement('th');
        objTH.setAttribute('scope', 'col');

          // create text
          objText=document.createTextNode('F');

          // add nodes
          objTH.appendChild(objText);
          objTR.appendChild(objTH);

        // create cell
        objTH=document.createElement('th');
        objTH.setAttribute('scope', 'col');

          // create text
          objText=document.createTextNode('S');

          // add nodes
          objTH.appendChild(objText);
          objTR.appendChild(objTH);

      // add node
      objThead.appendChild(objTR);
      objTable.appendChild(objThead);

    // create table body
    objTbody=document.createElement('tbody');

      // create spaces on first row
      objTR=document.createElement('tr');

      for (day=0; day!=dateStart.getDay(); day++) {

        // create cell
        objTD=document.createElement('td');

          // create text
          objText=document.createTextNode(' ');

          // add node
          objTD.appendChild(objText);
          objTR.appendChild(objTD);

      }

      day=dateStart.getDay();

      for (date=1; date<=days; date) {

        for (day; day<7; day++) {

          // create cell
          objTD=document.createElement('td');
          if(day==6) {
            objTD.setAttribute('class', 'last');
            try {objTD.className='last';} catch(objErr) {}
          }

            eventFound=getEvent(date);

            if (eventFound.length==4) {

              // create link
              objA=document.createElement('a');
              objA.setAttribute('class', eventFound[1]);
              try {objA.className=eventFound[1];} catch(objErr) {}
              objA.setAttribute('href', eventFound[2]);
              objA.setAttribute('title', eventFound[3]);
              if (objA.addEventListener) {
                objA.addEventListener('mouseover', showEvent, false);
                objA.addEventListener('focus', showEvent, false);
              } else if (objA.attachEvent) {
                objA.attachEvent('onmouseover', showEvent);
                objA.attachEvent('onfocus', showEvent);
              }
              
                // create text
                objText=document.createTextNode(new String(date));

              // add nodes
              objA.appendChild(objText);
              objTD.appendChild(objA);

            } else {

              // create text
              objText=document.createTextNode(new String(date));

              // add node
              objTD.appendChild(objText);

            }

            if ((month==today.getMonth() && year==today.getFullYear()) && date==today.getDate()) {
              objTD.setAttribute('class', 'today');
              try {objTD.className='today';} catch(objErr) {}
            }

            // add node
            objTR.appendChild(objTD);

            // increment date
            date++;

            if (days<date) {break;}

        }

        if (days<date) {break;}

        // add node
        objTbody.appendChild(objTR);

        // reset day
        day=0;

        // create new row
        objTR=document.createElement('tr');

      }

      // create spaces on last row
      for (day++; day<7; day++) {

        // create cell
        objTD=document.createElement('td');
        if(day==6) {
          objTD.setAttribute('class', 'last');
          try {objTD.className='last';} catch(objErr) {}
        }

          // create text
          objText=document.createTextNode(' ');

          // add node
          objTD.appendChild(objText);
          objTR.appendChild(objTD);

      }

      // add nodes
      objTbody.appendChild(objTR);
      objTable.appendChild(objTbody);

  // add node
  divCalendar.appendChild(objTable);

  // create paragraph
  p=document.createElement('p');
  p.setAttribute('id', 'calendarEvent');

  if (month==today.getMonth() && year==today.getFullYear()) {

    // decide whether to show a default event for today's date
    eventFound=getEvent(today.getDate());

    if (eventFound.length==4) {

      strHRef=eventFound[2]
      strTitle=eventFound[3]

      // create text node
      objText=document.createTextNode(monthName + ' ' + today.getDate() + ', '  + year);
      objBR=document.createElement('br');

      // add nodes
      p.appendChild(objText);
      p.appendChild(objBR);

      // create text node
      objText=document.createTextNode(strTitle);  

      // check for a non-linked item
      if (strHRef.lastIndexOf('#calendar')==strHRef.length-9){

        // add node
        p.appendChild(objText);

      } else {

        // create link
        objA=document.createElement('a');
        objA.setAttribute('href', strHRef);
        objA.setAttribute('title', strTitle);

        // add nodes
        objA.appendChild(objText);
        p.appendChild(objA);

      }

    }

  }

  // add node
  divCalendar.appendChild(p);
  
  return true;

}

/*

  showEvent(eventIn)

  triggered by a mouseover or focus event in the calendar,
  causes the calendarEvent paragraph element
  to show the date and event.

  if the event has a link other than #calendar,
  the calendarEvent paragraph contains the link.

  returns true always.

*/
function showEvent(eventIn) {

  var strTitle;
  var strHRef;
  var strDateIn;
  var p=document.getElementById('calendarEvent');

  if (eventIn.target) {
    strTitle=eventIn.target.title;
    strHRef=eventIn.target.href;
    strDateIn=eventIn.target.firstChild.nodeValue;
  } else if (eventIn.srcElement) {
    strTitle=eventIn.srcElement.title;
    strHRef=eventIn.srcElement.href;
    strDateIn=eventIn.srcElement.firstChild.nodeValue;
  }

  // clear paragraph
  hideEvent();

  // create text node
  objText=document.createTextNode(monthName + ' ' + strDateIn + ', '  + year);
  objBR=document.createElement('br');

  // add nodes
  p.appendChild(objText);
  p.appendChild(objBR);

  // create text node
  objText=document.createTextNode(strTitle);  

  // check for a non-linked item
  if (strHRef.lastIndexOf('#calendar')==strHRef.length-9){

    // add node
    p.appendChild(objText);

  } else {

    // create link
    objA=document.createElement('a');
    objA.setAttribute('href', strHRef);
    objA.setAttribute('title', strTitle);

    // add nodes
    objA.appendChild(objText);
    p.appendChild(objA);
  }

  return true;

}

/*

  hideEvent

  clears the contents of the calendarEvent paragraph.

  returns true always.

*/
function hideEvent() {

  var p=document.getElementById('calendarEvent');

  // remove all nodes
  while (0 < p.childNodes.length)
  {
    p.removeChild(p.firstChild);
  } 

  return true;

}
