// JavaScript Document
// CALENDAR type
//

var closedReqs = 0; // how many requests have been made successfully?
function detectLoadFinish() {
	closedReqs++;
	if (closedReqs >= totalCals) {
			toConsole("Data successfully loaded. Thank you!");
			closedReqs = 0;
			contractEvents();
			document.getElementById('throbber').style.display = 'none';
			document.getElementById('CalendarList').style.display = 'block';
			//alert(document.getElementById('CalendarList').innerHTML);
			//if(document.getElementById('CalendarList').innerHTML == '<img src="/images/white.gif" height="2" vspace="0" width="360">' || document.getElementById('CalendarList').innerHTML == '<br><br><br>No events match the specified criteria. Try a broader search.') document.getElementById('CalendarList').innerHTML = "<br><br><br>No events match the specified criteria. Try a broader search.";
			contractEvents();
	}
}
function toConsole(data) {
	var console = document.getElementById('console');
	console.innerHTML = data;
}

function CALENDAR(name, reqid)
{
    this.name = name;                   // must be unique and id of checkbox
    this.reqid = reqid;                 // string to send to google
    this.feedUpdateTime = "never";      // time feed was last updated per google
    this.updateTime = new Date();       // set to time of last update received
    this.updateTime.setTime(0);         // (never, so far)
    this.eventArray = new Array();      // array of events on this calendar
    this.startEventDate = new Date();   // date of first event in events array
    this.endEventDate = new Date();     // date of last event in events array
    this.xmlreq = null;                 // XML request object
    var self = this;                    // make "this" available to member function during callback (XML)

    this.insertEntry = function(e)
    {
        var title = "<no title>";
        var s;
        var c;
        var id = "<no id>";
        var orgid = "";

        // is this event cancelled?
        var tc = getNamedElements(e, "gd", "eventStatus");
        if (tc.length != 0)
        {
            var stat = tc[0].getAttribute("value");
            if (stat != null && stat == "http://schemas.google.com/g/2005#event.canceled") return;
        }
        
        // if this entry contains an "originalEvent" element,
        // record the ID of the original event.
        // This entry will over-ride the original event that matches:
        //      ID of original = recorded ID
        //      Start/end time of original = this start/end
        var oe = getNamedElements(e, "gd", "originalEvent");
        if (oe.length != 0) orgid = oe[0].getAttribute("id");

        var xid = e.getElementsByTagName("id");
        if (xid.length != 0 && xid[0].firstChild != null)  id = xid[0].firstChild.nodeValue;
        
        var tx = e.getElementsByTagName("title");
        if (tx.length != 0 && tx[0].firstChild != null) title = tx[0].firstChild.nodeValue;
        
        var auth = e.getElementsByTagName("author");
        var authname = "??";
        if (auth.length != 0 && auth[0].firstChild != null)
        {
            var nlist = auth[0].getElementsByTagName("name");
            if (nlist.length != 0 && nlist[0].firstChild != null) authname = nlist[0].firstChild.nodeValue;
        }
        
        var wx = getNamedElements(e, "gd", "where");
        var ws = "";
        if (wx.length != 0) ws = wx[0].getAttribute("valueString");
        //debugSay("wherestring = " + ws);
        
        var cx = e.getElementsByTagName("content");
        var ds = "";
        if (cx.length != 0 && cx[0].firstChild != null) ds = cx[0].firstChild.nodeValue;
        //debugSay("desc = " + ds);
		
		var ix = e.getElementsByTagName("content");
        var is = "";
        if (ix.length != 0 && ix[0].firstChild != null) is = ix[0].firstChild.nodeValue;
        //debugSay("desc = " + ds);
 
	    var wlist = getNamedElements(e, "gd", "when");
	    // debugSay("when count " + wlist.length)
	    // for each <when> entry, list it if it falls in the requested date range
	    // entry looks like <gd:when startTime="2006-07-26T18:30:00.000-04:00" endTime="2006-07-26T20:00:00.000-04:00">
       
        // debugSay("wlist len = " + wlist.length);
        var i;
        for (i=0; i<wlist.length; i++) 
        {
            // collect start and end date/time
            var ts = wlist[i].getAttribute("startTime");
            var te = wlist[i].getAttribute("endTime");
            
            // only valid if has both start and end time (per google event API)
            if (ts && te)
            {
                // Create new event
                var evt = new EVENT(title, ts, te, this.name, authname, ws, ds, id, orgid);
                if (evt.start >= this.startEventDate && evt.start <= this.endEventDate)
                {
                    // event is in range, insert into array
                    this.eventArray.push(evt);  
                }
            }
        }
    }
    
    this.loadVisibleEvents = function(sdt, edt)
    {
        if (f(this.name).checked) this.loadEvents(sdt, edt);
    }
    this.loadNotVisEvents = function(sdt, edt)
    {
        if (!f(this.name).checked) this.loadEvents(sdt, edt);
    }
    this.loadEvents = function(startDate, endDate)
    {  
        //debugSay("load for " + this.name);
        //debugSay("new start/end = " + startDate + " " + endDate);
        //debugSay("old start/end = " + this.startEventDate + " " + this.endEventDate);
		

        var bReload = false;        // set TRUE if reload needed
        
        if (startDate.getTime() < this.startEventDate.getTime() || endDate.getTime() > this.endEventDate.getTime())
        {
            this.feedUpdateTime = "never";      // new time range, force complete reload
            bReload = true;
        }
        else            // current data may be stale
        {
            var now = new Date();
            if (now.getTime() > (this.updateTime.getTime() + STALETIME) )
            {
                bReload = true;
            }
        }
 
        if (bReload)
        {
            this.forceLoadEvents(startDate, endDate);
			
        }
    }
    
    
    this.forceLoadEvents = function(startDate, endDate)
    {
        debugSay("force load for " + this.name);
        this.startEventDate.setTime( startDate.getTime() );
        this.endEventDate.setTime( endDate.getTime() );
        this.loadXMLdoc();
		toConsole("Retrieving data from database, please wait a moment...");
		document.getElementById('throbber').style.display = 'block';
		document.getElementById('CalendarList').style.display = 'none';
		
    }
    
    this.fillEventArray = function(theArray, theStart, theEnd)
    {
        if (! f(this.name).checked) return;
        var i;
        for (i in this.eventArray)
        {
            var e = this.eventArray[i];
            if (   e.start.getTime() >= theStart.getTime() 
                && e.start.getTime() <= theEnd.getTime()
                && e.show == true) theArray.push(e);
        }
    }
    
    // Note that his function requires server support for the "getcal" page.
    // Getcal is basically a passthrough, but required to exist on the server
    // because cross-domain scripting is not allowed for security reasons.
    this.loadXMLdoc = function()
    {
        var sed = this.startEventDate;
        var eed = this.endEventDate;
        
        if (sed == null || eed == null) return;
        
       // debugSay("load doc for " + this.name);
        var minstr = "&start-min=" + sed.getFullYear() + "-" + padout((sed.getMonth()+1)) + "-" + padout(sed.getDate()) + "T00:00:00";
        var maxstr = "&start-max=" + eed.getFullYear() + "-" + padout((eed.getMonth()+1))  + "-" + padout(eed.getDate()) + "T23:59:59";
        var ustr = "&utime=" + this.feedUpdateTime;
		var unique = "&nocache=" + new Date().getTime();
        var url = "/calendar/helpers/feed_gen.php" + this.reqid + minstr + maxstr + unique;
	    //debugSay(url);
        // branch for native XMLHttpRequest object
        if (window.XMLHttpRequest)
        {
            this.xmlreq = new XMLHttpRequest();
            this.xmlreq.onreadystatechange = this.processReq;
            //debugSay("about to get");
            this.xmlreq.open("GET", url, true);
          	// debugSay(" get done ");
            this.xmlreq.send(null);
          	// debugSay(" sent ");
		  	
        }
        // branch for IE/Windows ActiveX version
        else if (window.ActiveXObject) 
        {
            isIE = true;
            this.xmlreq = new ActiveXObject("Microsoft.XMLHTTP");
            if (this.xmlreq)
            {
                this.xmlreq.onreadystatechange = this.processReq;
                this.xmlreq.open("GET", url, true);
                this.xmlreq.send();
            }
	    }
    }
    
    // callback function to handle XML request for this calendar
    // note that we use "self" rather than "this" to refer to the parent object
    this.processReq = function()
    {
       //debugSay("xml callback for " + self.name);
	    if (self.xmlreq == null) return;
	    if (self.xmlreq.readyState != 4) return;			// not "loaded" so nothing to do
	    debugSay("result = " + self.xmlreq.status);
	
	    // debug - show raw return text
	    // debugSay("raw response: ");
	    // debugSay(xreq.responseText);

  	    if (self.xmlreq.status == 204) return;				// 204 == NO CONTENT
  	    if (self.xmlreq.status != 200) 
  	    {
		    // problem response
		    alert("There was a problem retrieving the XML data:\n" + self.xmlreq.statusText);
		    return;
  	    }

	    // process the returned XML
	    if (self.xmlreq.responseXML == null)  {
	       // debugSay("responseXML null");
	        return;
	        }
    	    
	    var response = self.xmlreq.responseXML.documentElement;
	    if (response == null) {
	        //debugSay("response null");
            return;
            }
            
        // set feed updated time
        var ulist = response.getElementsByTagName("updated");
        if (ulist.length != 0 && ulist[0].hasChildNodes)
        {
            self.feedUpdateTime = ulist[0].firstChild.nodeValue;
            //debugSay("update time is " + self.feedUpdateTime);
        }
    	
    	self.eventArray = new Array();                      // empty old array
	    var elist = response.getElementsByTagName("entry");
	   // debugSay("elist len = " + elist.length);

	    if (elist.length != 0)
	    {
		    // process each returned entry
		    for (i=0; i<elist.length; i++)
		    {
			    self.insertEntry(elist[i]);
		    }
	    }
    	
	    //dumpEvents();
        self.eventArray.sort(sortEvent);
        dumpEvents(self.eventArray);
        theCalendars.redisplay();
        
        //debugSay("process req completed");
        self.updateTime = new Date();
		
        //processXMLrequests();       // handle additional requests if any pending
		detectLoadFinish();
    }
}


// EVENT type
//
function EVENT(title, start, end, calname, authname, where, desc, id, orgID)
{
    // debugSay("new evt " + title + " s: " + start + " e: " + end);

    // constructor - setup members
    this.title = title;
    this.start = parseDatime(start);
    this.end = parseDatime(end);
    this.calname = calname;
    this.authname = authname;
    this.whereStr = where;
    this.descStr = desc;
    this.ID = id.replace(/[^a-zA-Z 0-9]+/g,'');
    this.orgID = orgID;
    this.show = true;               // set to false if this (original) event overridden by specialized one
    
    // function members - display
    this.showStart = function() {return (Weekdays[this.start.getDay()] + " " + Months[this.start.getMonth()] + " " + this.start.getDate() + " " + this.start.getHours() + ":" + padout(this.start.getMinutes()));}
    this.month = this.start.getMonth();
	this.StartDate = longDate(this.start); 
	this.shortStartDate = shortDate(this.start);
	this.hours = this.start.getHours();
	if (this.hours>=13) { this.hours-=12; var meridian = "pm"; } else { var meridian = "am" };
		this.StartTime = this.hours + ":" + padout(this.start.getMinutes()) + meridian; 
			if (this.hours == 0) this.StartTime = ' ';
			if (this.StartTime == '12:00am') this.StartTime = 'Noon';
    this.showTitle = function() {return this.title;}    
    this.Duration = (this.end - this.start)/3600000;
		if (this.Duration <= 1) { 
			this.Duration += " hr"; 
			} else { 
			if (this.Duration >= 24) { 
				this.Duration = "&nbsp;";
				} else { 
				this.Duration += " hrs"; 
				};
			};
		
		
    this.getAuthname = function() { return this.authname; }
    this.getWhere = function() { return this.whereStr; }
    this.getDesc = function() { return this.descStr; }
    this.dump = function() { debugSay("[evt] " + this.start + " " + this.end + " " + this.title + " id=" + this.ID + " orgid= " + this.orgID); 
		}
}

function sortEvent(e1, e2) { return (e1.start - e2.start); }

function dumpEvents(theArray)
{
   // debugSay("event array len = " + theArray.length);

    var i;
    for (i in theArray)
    {
        theArray[i].dump();
    }
}


// Parse a date/time from Google
//
// format is "2006-07-26T18:30:00.000-04:00"
//
function parseDatime(str)
{
    var theDate = new Date();
    
    if (str == null) return;
    
    theDate.setFullYear(str.substring(0, 4), str.substring(5, 7) - 1, str.substring(8, 10) );
    
    if (str.length < 21) theDate.setHours(0,0,0,0);
    else theDate.setHours(str.substring(11, 13), str.substring(14, 16), str.substring(17, 19), str.substring(20, 22) );
    // ignore timezone for now
    
    return theDate;
}