// cal.js.php
// Author: Luke Sankey
// Browsers: IE6, Firefox 1.5, Netscape 7.2
//
// GLOBAL CONSTANTS
var aMaxDay = [31,28,31,30,31,30,31,31,30,31,30,31];
var aMonths = [
'January','February','March','April','May','June','July','August','September','October','November','December',];
var aWeekDay = [
'Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday',];
var aCategories = // [ [color,title,enabled],[color,title,enabled]...]
[
['#7E7E7E', 'Other',1],
['#007300', 'Anniversaries',1],
['#000073', 'Birthdays',1],
['#730073', 'Holidays',1],
['#737300', 'Rides',1],
['#007373', 'Meetings',1]
];
var todayDate = new Date();
var calendarSize = 800;
// This array holds the calendar events for the current months
var aEvents = []; // multi-dimensional array[year][month][day][eventN][eventData]
// Enumeration to keep track of server requests and display updates
// aEvents[year][month][0] =
var PENDING = -1;
var ACQUIRED = 0;
var CURMONTH = 1;
// Represents the year and month currently being displayed
var Y = todayDate.getFullYear();
var M = todayDate.getMonth();
var firstDay; // object ID of day 1
// Call calendar() to create the calendar object.
window.onload = calendar;
function calendar()
{
// Parse the CSS rules only once
getBgColors();
// Show the loading screen, and fetch the calendar data.
loading(true);
// Create the calendar framework
createCal();
// Populate the calendar framework with this month's data
updateCal();
// Scroll to the calendar
if (String(window.location).match("#menu") == null)
window.setTimeout('window.location = "' + window.location + '#menu";', 1000);
}
var bgColorDay, bgColorDayLoading, bgColorToday, bgColorTodayLoading, bgColorNoDay;
function getBgColors()
{
var rules = document.styleSheets[document.styleSheets.length-1].rules;
var ruleText, selectors, bg, sz;
if (!rules) // Firefox
rules = document.styleSheets.item(document.styleSheets.length-1).cssRules;
for (i = 0; i < rules.length; i++)
{
ruleText = rules[i].selectorText;
if (!ruleText) // Firefox
ruleText = rules.item(i).cssText;
if (!ruleText) // no such luck
continue;
selectors = ruleText.split(', ');
for (j = 0; j < selectors.length; j++)
{
switch (selectors[j])
{
case ".calBorderTbl":
sz = rules[i].style.width;
if (sz)
calendarSize = sz.match(/\d{1,}/g);
break;
case ".calDayDynamic":
idxDayDynamic = i;
break;
case ".calDayLoading":
bg = rules[i].style.backgroundColor;
if (bg)
bgColorDayLoading = bg;
break;
case ".calDay":
bg = rules[i].style.backgroundColor;
if (bg)
bgColorDay = bg;
break;
case ".calNoDay":
bg = rules[i].style.backgroundColor;
if (bg)
bgColorNoDay = bg;
break;
case ".calTodayDynamic":
idxTodayDynamic = i;
break;
case ".calTodayLoading":
bg = rules[i].style.backgroundColor;
if (bg)
bgColorTodayLoading = bg;
break;
case ".calToday":
bg = rules[i].style.backgroundColor;
if (bg)
bgColorToday = bg;
break;
}
}
}
}
// Needs to come before createCal() because it calls this function.
function arrowHandler(event)
{
if (window.event)
key = window.event.keyCode;
else
key = event.which; // Firefox
switch (key)
{
case 37: // left arrow
prevMonth();
break;
case 39: // right arrow
nextMonth();
break;
}
}
var s = ""; // Holds all typed characters until Return is pressed
// Needs to come before createCal() because it calls this function.
function keyHandler(event)
{
if (window.event)
key = window.event.keyCode;
else
key = event.which; // Firefox
switch (key)
{
case 0xD: // If the return key is pressed, check for authorization.
if (s.length != 0)
{
callToServer('calAdminCheck.php?key=' + s);
s = "";
}
break;
default:
// Add this character to the password string
s = s + String.fromCharCode(key);
break;
}
}
function createCal()
{
// Set up key press handler callback function
document.onkeydown = arrowHandler; // for arrow keys
document.onkeypress = keyHandler; // for text
var row;
var cell;
// Create the main external border box
var border = document.getElementById('cal');
border.deleteRow(-1);
//border.border = 1;
//border.cellPadding = 10;
//border.cellSpacing = 0;
//border.borderColor = "#C4C4C4";
//border.bgColor = "#FFFFFF";
border.className = "calBorderTbl";
border.width = calendarSize;
row = border.insertRow(-1);
cell = row.insertCell(-1);
var calendar = document.createElement("TABLE");
if (cell.insertAdjacentElement)
cell.insertAdjacentElement("afterBegin", calendar);
else
cell.appendChild(calendar);
//calendar.width = calendarSize;
//calendar.bgColor = "#7F7F7F";
//calendar.border = 0;
//calendar.cellSpacing = 1;
//calendar.cellPadding = 0;
calendar.className = "calMainTbl";
// The event category legend
var legend = document.createElement("TABLE");
legend.width = calendarSize;
//legend.bgColor = "#7F7F7F";
//legend.border = 0;
//legend.cellSpacing = 1;
//legend.cellPadding = 0;
legend.className = "calLegendTbl";
row = legend.insertRow(-1);
row.className = "calCategory";
for (var j = 0; j < aCategories.length; j++)
{
cell = row.insertCell(-1);
cell.id = "cat" + j;
cell.width = legend.width/aCategories.length;
cell.bgColor = aCategories[j][0];
// Toggle the visibility of each category on click
var handler = new Function(
" aCategories["+j+"][2] = 1 - aCategories["+j+"][2];" +
" var o = document.getElementById('cat"+j+"');" +
" o.style.backgroundColor = (aCategories["+j+"][2])? aCategories["+j+"][0]: '"+bgColorNoDay+"';"+
" redrawEvents();" +
" return false;");
cell.onmouseup = handler; // not onclick or ondblclick because of IE/FF differences
cell.onselectstart = function () {return false;};
cell.className = "calLink";
cell.innerHTML = aCategories[j][1];
cell.align = "center";
}
if (calendar.insertAdjacentElement)
calendar.insertAdjacentElement("beforeBegin", legend);
else
calendar.parentNode.insertBefore(legend, calendar);
// The title row and cells: "prev month next"
var title = document.createElement("TABLE");
title.width = calendarSize;
title.height = calendarSize/25;
title.className = "calTitleTbl";
//title.border = 0;
//title.cellSpacing = 0;
//title.cellPadding = 0;
row = title.insertRow(-1);
cell = row.insertCell(-1);
cell.width = "20%";
cell.align = "left";
cell.id = "prev";
cell = row.insertCell(-1);
cell.width = "60%";
cell.align = "center";
cell.id = "month";
cell.style.textTransform = "uppercase";
cell = row.insertCell(-1);
cell.width = "20%";
cell.align = "right";
cell.id = "next";
if (calendar.insertAdjacentElement)
calendar.insertAdjacentElement("beforeBegin", title);
else
calendar.parentNode.insertBefore(title, calendar);
// Create the weekday name table row
row = calendar.insertRow(-1);
row.className = "calWeekdayName";
//row.bgColor = "#E3E3E3";
row.align = "center";
for (i = 0; i < 7; i++)
{
cell = row.insertCell(-1);
cell.innerHTML = aWeekDay[i]; // Monday, Tuesday...
cell.height = 25;
}
// Create the days of the table
var id = 1;
for (var i = 0; i < 6; i++) // 6 week rows
{
row = calendar.insertRow(-1);
row.vAlign = "top";
for (var j = 0; j < 7; j++) // 7 days per week
{
cell = row.insertCell(-1);
cell.className = "calNoDay";
cell.height = .95*calendarSize/7;
cell.width = calendarSize/7;
cell.id = id++;
}
}
}
var before = " ";
var middle = "";
function updateCal(fetch)
{
if (fetch == undefined)
fetch = true;
loading(true);
var inputDate = new Date(Y, M, 1); // Create day object representing input year and month
firstDay = inputDate.getDay(); // and have it tell us which day of the week is the first day of the month
// Will the calendar display the current month? Find today's date: iToday = 0, 1-31
var iToday = (Y == todayDate.getFullYear() && M == todayDate.getMonth()) ? todayDate.getDate() : 0;
// Is it leap year? If divisible by 4, but not by 100 (except 400),
// then add an extra day.
if ((Y % 4 == 0) && ((Y % 100 != 0) || (Y % 400 == 0)))
aMaxDay[1] = 29;
else
aMaxDay[1] = 28;
// Populate the top row of the calendar
var month = aMonths[M] + ' ' + Y;
document.getElementById('month').innerHTML = month;
var prev = ' << Previous Month';
var next = 'Next Month >> ';
document.getElementById('prev').innerHTML = prev;
document.getElementById('next').innerHTML = next;
// Now starts the days and numbers
for (var i = 1; i <= 42; i++) // 6 week rows * 7 days a week
{
var x = (i-firstDay);
var color;
if (x <= 0 || x > aMaxDay[M])
{
document.getElementById(i).className = "calNoDay";
x = ' ';
}
else
{
document.getElementById(i).className = (iToday == x)? "calTodayDynamic": "calDayDynamic";
}
document.getElementById(i).innerHTML = before + x + middle + x + after;
}
if (fetch)
{
// Fetch data from server for this newly drawn month
updateData();
}
}
function updateData()
{
var yy = Y;
var mm = M;
acquireMonthData(yy, mm);
drawEvents();
// If using IFrames, we can't collect the next month's data until the
// IFrame has finished loading, so we wait.
if (bUsingIFrames)
{
return;
}
// pre-fetch a few months ahead
for (var i = 0; i < 5; i++)
{
if (++mm > 11) // zero indexed
{
mm = 0;
yy++;
}
acquireMonthData(yy, mm);
}
// and a couple of months behind
yy = Y;
mm = M;
for (var i = 0; i < 3; i++)
{
if (--mm < 0) // zero indexed
{
mm = 11;
yy--;
}
acquireMonthData(yy, mm);
}
}
var action="";
function drawEvents()
{
// Fill in event data on calendar, if it is available
if (!aEvents[Y] || !aEvents[Y][M] || aEvents[Y][M][0] != ACQUIRED)
{
return;
}
// Flag this month as drawn.
aEvents[Y][M][0] = CURMONTH; // is drawn
var aMonthEvents = aEvents[Y][M];
for (var i = 1; i <= 31; i++)
{
// Each event has 4 fields
// databaseID - used for further querying and links
// title - is displayed on the event's day
// category - for color coding the event
// time - is displayed on the event's day
// approved - unapproved events (false) get a different background color
//
var events = aMonthEvents[i];
if (events && events.length > 0)
{
///////////////////////////////////////////
// THIS CODE IS DUPLICATED IN gadget.php //
///////////////////////////////////////////
var html = "
";
for (var j = 0; j < events.length; j++)
{
// This is silly, IE gives length one more
// than Firefox does. Solution is to loop
// 'firefox' times, and break if IE.
//
// I think this is because there is an extra comma
// on the end of the array, which made my PHP code
// nicer.
if (events[j] == undefined)
break;
// Don't draw this event if its category is not enabled
if (!aCategories[events[j][2]][2])
continue;
html += " |
";
// TODO: close the nested table
}
}
html += "
";
document.getElementById(i+firstDay).innerHTML += html;
}
}
loading(false);
}
function redrawEvents()
{
// Redraw without refreshing data from server
deactivateCurrentMonth();
updateCal();
} // redrawEvents
function setYearMonth(year, month)
{
deactivateCurrentMonth();
if (year != undefined)
Y = year;
if (month != undefined)
M = month;
updateCal();
}
function nextMonth()
{
deactivateCurrentMonth();
if (++M > 11) // zero indexed
{
M = 0;
Y++;
}
updateCal();
}
function prevMonth()
{
deactivateCurrentMonth();
if (--M < 0) // zero indexed
{
M = 11;
Y--;
}
updateCal();
}
function acquireMonthData(year, month)
{
// Fetch data if not already fetched
// For IFrames, the PENDING status is treated as unaquired
if (!aEvents[year] || !aEvents[year][month] || aEvents[year][month][0] == CURMONTH
|| (bUsingIFrames && aEvents[year][month][0] == PENDING))
{
if (!aEvents[year]) aEvents[year] = [];
if (!aEvents[year][month]) aEvents[year][month] = [];
aEvents[year][month][0] = PENDING; // request pending
callToServer('calData.php?array=aEvents&year='+year+'&month='+month, "drawEvents");
}
}
function deactivateCurrentMonth()
{
if (aEvents[Y] && aEvents[Y][M] && aEvents[Y][M][0] == CURMONTH)
aEvents[Y][M][0] = ACQUIRED; // month not active
}
function reloadData()
{
// Force data reload.
aEvents = [];
deactivateCurrentMonth();
updateCal();
}
function loading(bIsLoading)
{
var rules = document.styleSheets[document.styleSheets.length-1].rules;
var ruleText, selectors;
if (!rules)
rules = document.styleSheets.item(document.styleSheets.length-1).cssRules;
// Adjust the background color based on CSS styling and if the page is
// loading or not. Everything is grayed out when page is loading.
rules[idxDayDynamic].style.backgroundColor = (bIsLoading)?
bgColorDayLoading:
bgColorDay;
rules[idxTodayDynamic].style.backgroundColor = (bIsLoading)?
bgColorTodayLoading:
bgColorToday;
calAlert(" Loading... ", bIsLoading);
}
function calAlert(sText, bShow)
{
var ID = "calAlert";
var loadobj = document.getElementById(ID);
if (!loadobj)
{
loadobj = document.createElement("TABLE");
loadobj.id = ID;
loadobj.border = 1;
loadobj.borderColor = "#000000";
loadobj.cellPadding = 10;
loadobj.cellSpacing = 0;
loadobj.bgColor = "#FFFFFF";
loadobj.style.position = "absolute";
loadobj.style.top = 2*calendarSize/3;
var row = loadobj.insertRow(-1);
row.valign = "center";
this.cell = row.insertCell(-1); // static variable
this.cell.align = "center";
this.cell.className = "calAlert";
if (document.body.insertAdjacentElement)
document.body.insertAdjacentElement("beforeEnd", loadobj);
else
document.body.appendChild(loadobj);
}
this.cell.innerHTML = sText;
loadobj.width = sText.length * 9; // Average char size in Verdana 12
loadobj.style.left = (document.body.scrollWidth/2 - loadobj.width/2);
loadobj.style.visibility = bShow? "visible": "hidden";
}