// 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]...] [ ['#b7d2c1', 'Exhibits',1], ['#ccb9dd', 'Performances',1], ['#e4f0c4', 'Talks/Workshops/Films',1], ['#cbd0e7', 'Tours',1], ['#bcebeb', 'Parties/Gatherings',1], ['#dce9ff', 'Other Events',1] ]; var todayDate = new Date(); var calendarSize = 669; // 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; // This calendar has limited display dates: var DATE_BEG = new Date(2008,8,12); var DATE_END = new Date(2008,9,9); // Represents the year and month currently being displayed var Y = DATE_BEG.getFullYear(); var M = DATE_BEG.getMonth(); var firstDay; // object ID of day 1 var firstDate; // calendar date of day 1 var DAYS = Math.ceil((DATE_END.getTime()-DATE_BEG.getTime())/(1000*60*60*24)); var WEEKS = Math.ceil(DAYS/7); if (DATE_BEG.getDay() > DATE_END.getDay()) WEEKS++; // 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 //document.getElementById('cal').scrollIntoView(); } 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.onmouseup = new Function("window.location = '../programs_"+j+".php';"); 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 = "10%"; cell.align = "left"; cell.id = "prev"; cell = row.insertCell(-1); cell.width = "80%"; cell.align = "center"; cell.id = "month"; cell.style.textTransform = "uppercase"; cell = row.insertCell(-1); cell.width = "10%"; 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 < WEEKS; i++) { 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 firstDay = DATE_BEG.getDay(); firstDate = DATE_BEG.getDate(); // 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; iToday = 0; // DOES NOT WORK, SO DISABLE FOR NOW // 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; var month = aMonths[DATE_BEG.getMonth()] + ' ' + DATE_BEG.getDate() + ', ' + DATE_BEG.getFullYear() + ' - ' + aMonths[DATE_END.getMonth()] + ' ' + DATE_END.getDate() + ', ' + DATE_END.getFullYear(); 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 <= WEEKS*7; i++) // 7 days a week { var x = (i-firstDay+firstDate-1); if (x < firstDate || x > firstDate + DAYS) { document.getElementById(i).className = "calNoDay"; x = ' '; } else { document.getElementById(i).className = (iToday == x)? "calTodayDynamic": "calDayDynamic"; } if (x > aMaxDay[DATE_BEG.getMonth()]) { x -= aMaxDay[DATE_BEG.getMonth()]; } document.getElementById(i).innerHTML = before + x + middle + x + after; } if (fetch) { // Fetch data from server for this newly drawn month updateData(); } } function updateData() { for (var yy = DATE_BEG.getFullYear(); yy <= DATE_END.getFullYear(); yy++) { var mBeg = DATE_BEG.getMonth(); var mEnd = DATE_END.getMonth(); if (yy != DATE_END.getFullYear()) mEnd = 12; for (var mm = mBeg; mm <= mEnd; mm++) 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 < 2; 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 all available for (var yy = DATE_BEG.getFullYear(); yy <= DATE_END.getFullYear(); yy++) { var mBeg = DATE_BEG.getMonth(); var mEnd = DATE_END.getMonth(); if (yy != DATE_END.getFullYear()) mEnd = 12; for (var mm = mBeg; mm <= mEnd; mm++) { if (!aEvents[yy] || !aEvents[yy][mm] || aEvents[yy][mm][0] != ACQUIRED) return; } } // This helps keep track of day index when we display date ranges that span // multiple months var dayOffset = 0; // Flag this month as drawn. aEvents[Y][M][0] = CURMONTH; // is drawn for (var yy = DATE_BEG.getFullYear(); yy <= DATE_END.getFullYear(); yy++) { var mBeg = DATE_BEG.getMonth(); var mEnd = DATE_END.getMonth(); if (yy != DATE_END.getFullYear()) mEnd = 12; for (var mm = mBeg; mm <= mEnd; mm++) { var dBeg = DATE_BEG.getDate(); var dEnd = DATE_END.getDate(); if (mm != DATE_BEG.getMonth() || yy != DATE_BEG.getFullYear()) dBeg = 1; if (mm != DATE_END.getMonth() || yy != DATE_END.getFullYear()) dEnd = aMaxDay[mm]; for (var dd = dBeg; dd <= dEnd; dd++) { var events = aEvents[yy][mm][dd]; // 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 // 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(dd+firstDay-firstDate+dayOffset+1).innerHTML += html; } } // end for each day dayOffset += aMaxDay[mm]; } // end for each month } // end for each year loading(false); } /* myFunc = function(a) { alert(a); }; myFunc('hello'); function foreachday(doFunc) { for (var yy = DATE_BEG.getFullYear(); yy <= DATE_END.getFullYear(); yy++) { var mBeg = DATE_BEG.getMonth(); var mEnd = DATE_END.getMonth(); if (yy != DATE_END.getFullYear()) mEnd = 12; for (var mm = mBeg; mm <= mEnd; mm++) { var dBeg = DATE_BEG.getDate(); var dEnd = DATE_END.getDate(); if (mm != DATE_BEG.getMonth() || yy != DATE_BEG.getFullYear()) dBeg = 1; if (mm != DATE_END.getMonth() || yy != DATE_END.getFullYear()) dEnd = aMaxDay[mm]; for (var dd = dBeg; dd <= dEnd; dd++) { doFunc(yy, mm, dd); } } } } */ 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)) {//alert(month); 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); var calobj = document.getElementById("cal"); 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"; 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.top = getAbsY(calobj)+calendarSize/2 - loadobj.width/2 + "px"; loadobj.style.left = getAbsX(calobj)+calendarSize/2 - loadobj.width/2 + "px"; loadobj.style.visibility = bShow? "visible": "hidden"; } function getAbsX(elt) { return (elt.x) ? elt.x : getAbsPos(elt,"Left"); } function getAbsY(elt) { return (elt.y) ? elt.y : getAbsPos(elt,"Top"); } function getAbsPos(elt,which) { iPos = 0; while (elt != null) { iPos += elt["offset" + which]; elt = elt.offsetParent; } return iPos; } function setAbsX(o,x) { if (o.x) o.x = x; else { xOld = getAbsX(o); o.offsetLeft = o.offsetLeft - xOld + x; } } function setAbsY(o,y) { if (o.y) o.y = y; else { yOld = getAbsY(o); o.offsetTop = o.offsetTop - yOld + y; } }