// shiftcal.js
// ECMAScript Fire Department Shift Calendar
// Version 0.95 2006-02-15
// Author: Kevin P. Rice (kevin@tek4.com)
// Copyright 2001-2007 by Kevin P. Rice. All rights reserved.
//
// ECMAScript (Standard ECMA-262. Also ISO/IEC 16262.)
// http://www.ecma-international.org/
//
// Known issues:
// - focus/hide problems with schedule select list box
// - general appearance not yet refined
//
// Future Features:
//  - background image caching
//  - move legend inside of calendar table (possibly auto-locate to begin or end)
//  - cookie support to remember preferred schedule
//  - add "change schedule" button
//  - data binding to external data file
//  - make window slightly smaller to conserve desktop space
//  - change "About" from alert box to graphic window, add Freightyard.Net reference
//  - implement link (or function) for adding a schedule (possibly this could use the
//    user's cookie so new schedule only appears on user machine until audited)
//  - port to Java (security, execution speed, compactness, greater compatibility)??
//
// Version History:      
// 0.80 (02/15/01) - original JavaScript calendar
// 0.90 (03/06/01) - implemented DHTML for table update, browse buttons, schedule change
// 0.91 (04/28/03) - fixed round-up error that caused 1-day shift on leap years & year before
// 0.92 (2005-12-03) - converted HTML to XHTML 1.1
// 0.93 (2005-12-18) - W3C DOM 1.0 compatible
// 0.94 (2006-01-30) - fixed "Month++" bug that skips two months when next
// 0.95 (2007-02-15) - added OCFA schedule
//                     month has fewer days than current date
//

  // about Fire Calendar
  function about () {
    var name = "kevin";
    var domain = "tek4.com";
    alert(
      'Fire Calendar, Version 0.95\n' +
      'ECMAScript Fire Department Shift Calendar\n' +
      'Author: Kevin P. Rice <' + name + '\u0040' + domain + '>\n' +
      'Copyright 2001-2007 by Kevin P. Rice. All rights reserved.' );
  }

  //resizeTo(405,370);

  var BGBEGIN = "beige";    // background color for table frames before 1st of month
  var BGEND = "";           // background color for table frames after last of month
  var BGFRAME = "beige";    // background color for table frames
  var BGTODAY = "pink";     // background color for "today" table frame
  
  var current = new Date();
  var today = new Date();
  var useSched = 0;

  //  ================================================================
  //  Shift Schedule Data
  //  Two dimension array. Each row represents a single schedule.
  //
  //  col 1 - name of schedule
  //  col 2 - shift schedule
  //  col 3 - shift schedule offset for dayserial() = 0
  //  col 4 - colors corresponding to shift labels
  //  col 5 - shift labels
  

  // Here are all of the schedules supported. The first one is the default.
  var sched = [ 
  [ "", "ABC", 9, ["GREEN", "BLUE", "RED"], "ABC" ],
  [ "", "ABC", 9, ["GREEN", "BLUE", "RED"], "ABC" ],
  [ "", "ABC", 9, ["GREEN", "BLUE", "RED"], "ABC" ]
              ];
  
  // Constants for accessing schedule array columns
  var SNAME = 0;
  var SSCHED = 1;
  var SOFF = 2;
  var SCOLOR = 3;
  var SLABEL = 4;  

  // wait for background image to load
  //document.write( '<span id="loading">Loading...<\/span>' );
  //var bgImage = new Image();
  //bgImage.onload = createpage;
  //bgImage.src = "flames.jpg";
  //document.getElementById("loading").style.display="none";
  createpage();

  // Create main page and display today's date
  function createpage () {
  
    // write select list box
    document.write( '<span id="calSched" onclick="selSched(true);" title="Click here to select a different schedule">calSched<\/span>' );
    document.write( '<select id="calSelect" onblur="selSched(false);" onChange="SelectSched(this.value);" size="0" style="display:none" >' );
    for ( var idx = 0; idx < sched.length; ++idx)
      document.write( '<option value=' + idx + '>' + sched[idx][0] );    
    document.write( '<\/option><\/select>' );

    // begin table and write caption



    document.write( '<div><table id="calTable" width="185">' );
    document.write( '<caption id="calCaption">calCaption<\/caption>' );

    // write calendar header row with days of week
    var days = [ 'S', 'M', 'T', 'W', 'T', 'F', 'S' ]
    document.write( '<tr>' );
    for ( var idx = 0; idx < days.length; ++idx)
      document.write( '<th>' + days[idx] + '<\/th>' );
    document.write( '<\/tr>' );

    // write body of calendar
    for ( var week = 0; week < 5; ++week) {
      document.write("<tr>");
      for (var day = 0; day < 7; ++day)
        document.write("<td><\/td>");
      document.write("<\/tr>");
    }
    document.write('<tr><td><\/td><td><\/td><\/tr>');
  
    document.write('<\/table><\/div>');
    document.write('<div><span id="calLegend" title="Shift colors legend">calLegend<\/span><\/div>');

    document.write('<div style="text-align: center;">');
    //document.write('<input type="button" value="\u25c4" onclick="gomonth(-12);" title="Display previous year" \/>');
    document.write('<input type="button" value="<" onclick="gomonth(-1);" title="Display previous month" \/>');
    document.write('<input type="button" value="Today" onclick="gotoday();" title="Display current month" \/>');
    document.write('<input type="button" value=">" onclick="gomonth(1);" title="Display next month" \/>');
    //document.write('<input type="button" value="\u25ba" onclick="gomonth(12);" title="Display following year" \/>');
    document.write('<\/div>');
    //document.write('<div><span id="calAbout" onclick="about();" title="Click here for information about Fire Calendar">About Fire Calendar<\/span><\/div>');

    gotoday();
  }  

  // Display calendar
  // CALL:
  //  date - date object indicating month to display
  //  sch - schedule to use
  function displaycal (date, sch ) {

    document.getElementById("calSched").childNodes[0].nodeValue = sch[SNAME];
    document.getElementById("calCaption").childNodes[0].nodeValue = monthname(date.getMonth()) + " " + date.getFullYear();

    var elLegend = document.getElementById("calLegend");
    elLegend.innerHTML = "";
    for ( var idx=0; idx < sch[SLABEL].length; ++idx)
      elLegend.innerHTML += sch[SLABEL].charAt(idx).fontcolor(sch[SCOLOR][idx]);
    
    /* ================================================================
    update calendar table for current month
    
    LOCAL VARIABLES:
    
    bgcolor - background color for frame
    collTD - collection of all <td> tags in calendar table
    frame - loop counter for writing table cells
    monthlen - number of days in current month
    offset - day of week for first of month (0 to 6)
    serToday - serial number for today's date
    serFrame - serial number of date in frame being written
    shiftID - one character identifier for a shift
    =================================================================== */
    
    var bgcolor;
    var collTD = document.getElementById("calTable").getElementsByTagName("td");
    var frame = 0;
    var frameDate;
    var monthlen = monthdays(date);
    var offset = (date.getDay() - date.getDate() + 36) % 7;
    var serToday = dayserial(today);
    var serFrame = dayserial(date) - date.getDate() + 1;
    var shiftID;
    
    // iterate through all <td> tags in table to update calendar
    for ( var frame = 0; frame < collTD.length; ++frame) {
      // calculate date for frame
      frameDate = (frame - offset + 1);
      // test if date is in range
      if (frameDate >= 1 && frameDate <= monthlen) {
        bgcolor = (serFrame == serToday) ? BGTODAY : BGFRAME;
        shiftID = sch[SSCHED].charAt( (serFrame++ + sch[SOFF]) % sch[SSCHED].length );
        frameDate = frameDate.toString().fontcolor( sch[SCOLOR] [ sch[SLABEL].indexOf(shiftID) ] );
      // use null string for blank frames
      } else {
        bgcolor = (frameDate > monthlen) ? BGEND : BGBEGIN;
        frameDate = "";
      }
      collTD[frame].setAttribute("bgcolor", bgcolor, false);
      collTD[frame].innerHTML = frameDate;
    }

  }

  // return day of year for date
  // CALL: date - date object
  // RETURN: day of year (0 to 365) for date
  function dayofyear (date) {
    var lookup = [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 ];
    var day = lookup [date.getMonth()] + date.getDate() - 1;
    var leapyear = date.getFullYear() % 4 == 0 ? 1 : 0;
    if (date.getMonth() >= 2) day += leapyear;
    return day;
  }
  
  // return day serial number for date
  // CALL: date - date object
  // RETURN: days since Jan 1, 1980
  function dayserial (date) {
    var serial = dayofyear(date);
    serial += (date.getFullYear() - 1980) * 365;
    serial += Math.floor((date.getFullYear() - 1977) / 4);
    return serial;
  }
  
  // return number of days in month
  // CALL: date - date object
  // RETURN: number of days in month
  function monthdays (date) {
    var days = (date.getMonth() % 7 + 1) % 2 + 30;
    if (date.getMonth() == 1) days = date.getYear() % 4 ? 28 : 29;
    return days;
  }
  
  // return name of month
  function monthname (month) {
    var months = [ "January", "February", "March", "April", "May", "June",
      "July", "August", "September", "October", "November", "December" ]
    return months [month];
  }

  // go to current date
  function gotoday () {
    current = new Date();
    refresh();
  }
  
  // go month
  // CALL: offset - relative number of months
  function gomonth (offset) {
    var newMonth = current.getMonth() + offset;
    if (newMonth < 0) {
      if (current.getFullYear() > 1980) {
        newMonth += 12;
        current.setMonth(newMonth);
        current.setYear(current.getFullYear() - 1);
        refresh();
      } else alert("Cannot display dates prior to 1980");
    } else if (newMonth > 12) {
      newMonth -= 12;
      current.setMonth(newMonth);
      current.setYear(current.getFullYear() + 1);
      refresh();
    } else {
      current.setDate(1);
      current.setMonth(newMonth);
      refresh();
    }
  }
  
  // refresh calendar display
  function refresh () {
    displaycal(current, sched[useSched]);
  }
    
  // turns on/off schedule select list box
  // CALL: show - true/false
  function selSched (show) {
    document.getElementById("calSched").style.display=show ? "none" : "inline";
    document.getElementById("calSelect").style.display=show ? "inline" : "none";
    if (show) document.getElementById("calSelect").focus();
  }
  
  // new department selected
  function SelectSched (schednum) {
    useSched = schednum;
    selSched(false);
    refresh();
  }
