// 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 += ""; if (events[j][4] != 1) // not approved { 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"; }