function googleCalParser(calArray, callback) {

	googleCalParser.prototype.calendars = calArray;
	googleCalParser.prototype.calEvents = new Array();
	googleCalParser.prototype.allChurchEventsArray = new Array();
	googleCalParser.prototype.loadedCalendars = 0;
	googleCalParser.prototype.callbackFunction = callback;
	googleCalParser.prototype.service;
}

googleCalParser.prototype.init = function() {
	
	// init the Google data JS client library with an error handler
	google.gdata.client.init(googleCalParser.prototype.handleGDError);
	googleCalParser.prototype.service = new google.gdata.calendar.CalendarService('gdata-js-client-samples-simple');
	googleCalParser.prototype.loadCals();
}

googleCalParser.prototype.loadCals = function() {

	
		
	for (var i=0; i<googleCalParser.prototype.calendars.length; i++) {

		var calendarName = googleCalParser.prototype.calendars[i].calName;
		var calendarURL = googleCalParser.prototype.calendars[i].url;
		
		googleCalParser.prototype.getCalendarASync(calendarURL, calendarName);
	}
}

googleCalParser.prototype.getCalendarASync = function (url, calName) {

		var query = new google.gdata.calendar.CalendarEventQuery(url);
		query.setOrderBy('starttime');
		query.setSortOrder('ascending');
		query.setFutureEvents(true);
		query.setSingleEvents(true);
	
                googleCalParser.prototype.service.getEventsFeed(query, function (data){
                
                	googleCalParser.prototype.processResults(data, calName);
                }, googleCalParser.prototype.handleGDError);
}

/**
 * Callback function for the Google data JS client library to call when an error
 * occurs during the retrieval of the feed.  Details available depend partly
 * on the web browser, but this shows a few basic examples. In the case of
 * a privileged environment using ClientLogin authentication, there may also
 * be an e.type attribute in some cases.
 *
 * @param {Error} e is an instance of an Error 
 */
googleCalParser.prototype.handleGDError = function(e) {
	document.getElementById('jsSourceFinal').setAttribute('style', 'display:none');
	if (e instanceof Error) {
		/* alert with the error line number, file and message */
		alert('Error at line ' + e.lineNumber +
		  ' in ' + e.fileName + '\n' +
		  'Message: ' + e.message);
		/* if available, output HTTP error code and status text */
		if (e.cause) {
			var status = e.cause.status;
			var statusText = e.cause.statusText;
			alert('Root cause: HTTP error ' + status + ' with status text of: ' + 
			    statusText);
		}
	} else {
		alert(e.toString());
	}
}

/**
 * Callback function for the Google data JS client library to call with a feed 
 * of events retrieved.
 *
 * Creates an unordered list of events in a human-readable form.  This list of
 * events is added into a div called 'events'.  The title for the calendar is
 * placed in a div called 'calendarTitle'
 *
 * @param {json} feedRoot is the root of the feed, containing all entries 
 */ 
googleCalParser.prototype.processResults = function(feedRoot, calName) {

	var entries = feedRoot.feed.getEntries();

	googleCalParser.prototype.calEvents[calName] = new Array();

	/* loop through each event in the feed */
	var len = entries.length;
	for (var i = 0; i < len; i++) {

		var entry = entries[i];

		// Get the calendar entry's title
		var title = entry.getTitle().getText();

		// Get the calendar entry's location
		var location = entry.getLocations()[0].getValueString(); 

		// Get the calendar entry's content
		var content = entry.getContent().getText();

		// Get the calendar entry's start time and date
		var startDateTime = null;
		var startJSDate = null;
		var times = entry.getTimes();

		if (times.length > 0) {
			startDateTime = times[0].getStartTime();
			startJSDate = startDateTime.getDate();
		} 


		var dateYear = startJSDate.getYear();
		var dateMonth = startJSDate.getMonth() + 1;
		var dateDay = startJSDate.getDate();
		var dateString = (dateMonth + "/" + dateDay);

		var timeString = "";
		if (!startDateTime.isDateOnly()) {
			var ampm = "AM";
			var hours = startJSDate.getHours();
			if (hours > 12) {
				hours = hours - 12;
				ampm = "PM";
			}

			var minutes = googleCalParser.prototype.padNumber(startJSDate.getMinutes());
			timeString = (hours + ":" + 
			minutes + ampm);
		} else {
			timeString = "";
		}

		// Add the new calendar event to an array
		var newevent = new googleCalParser.prototype.eventObject();
		newevent.eventObjectTitle = title;
		newevent.eventObjectTime = timeString;
		newevent.eventObjectDate = dateString;
		newevent.eventObjectLocation = location;
		newevent.eventObjectContent = content;
		newevent.eventObjectHours = hours;
		newevent.eventObjectMinutes = minutes;
		newevent.eventObjectAMPM = ampm;
		newevent.eventObjectYear = dateYear;
		newevent.eventObjectMonth = dateMonth;
		newevent.eventObjectDay = dateDay;
		newevent.eventObjectCalName = calName;
		newevent.setFullMonthName();
		googleCalParser.prototype.calEvents[calName].push(newevent);
	}

	googleCalParser.prototype.loadedCalendars++;

	if ( googleCalParser.prototype.loadedCalendars == cals.length ) {
		googleCalParser.prototype.createAllEventsArray();
		googleCalParser.prototype.callbackFunction(this);	
	}

}


/**
 * Adds a leading zero to a single-digit number.  Used for displaying dates.
 */
googleCalParser.prototype.padNumber = function(num) {
	if (num <= 9) {
		return "0" + num;
	}
	return num;
}

googleCalParser.prototype.sortEvents = function(calendarArray) {

	var objectArrayIndex = calendarArray.length;

	googleCalParser.prototype.myBubbleSort(calendarArray,objectArrayIndex);
	//Use the showArray to debug sorting
        //googleCalParser.prototype.showArray('Sorted',calendarArray,objectArrayIndex);
}


googleCalParser.prototype.showArray = function(text,object,length) {
	document.write(text + ': ');
	for (var i=0; i<length; i++)
	document.write(object[i]+' <br>');
	//document.write('<BR>');
}


googleCalParser.prototype.myBubbleSort = function(arrayName,length) {
	for (var i=0; i<(length-1); i++)
	for (var j=i+1; j<length; j++)
	if (arrayName[j] < arrayName[i]) {
		var dummy = arrayName[i];
		arrayName[i] = arrayName[j];
		arrayName[j] = dummy;
	}
}

googleCalParser.prototype.eventObject = function() {
	this.eventObjectTitle = "";
	this.eventObjectTime = "";
	this.eventObjectDate = "";
	this.eventObjectLocation = "";
	this.eventObjectContent = "";
	this.eventObjectHours = 0;
	this.eventObjectMinutes = "";
	this.eventObjectAMPM = "";
	this.eventObjectYear = 0;
	this.eventObjectMonth = 0;
	this.eventObjectDay = 0;
        this.eventObjectMonthName;
        this.eventObjectCalName;
        
        this.getAttribute = function(name){
                var returnVal;
        	switch (name) {
        		case "eventTitle":     returnVal = this.eventObjectTitle; break;
        		case "eventTime":      returnVal = this.eventObjectTime; break;
        		case "eventDate":      returnVal = this.eventObjectDate; break;
        		case "eventLocation":  returnVal = this.eventObjectLocation; break;
        		case "eventDesc":      returnVal = this.eventObjectContent; break;
        		case "eventHours":     returnVal = this.eventObjectHours; break;
        		case "eventMinutes":   returnVal = this.eventObjectMinutes; break;
        		case "eventAMPM":      returnVal = this.eventObjectAMPM; break;
				case "eventYear":      returnVal = this.eventObjectYear; break;
        		case "eventMonth":     returnVal = this.eventObjectMonth; break;
        		case "eventDay":       returnVal = this.eventObjectDay; break;
        		case "eventMonthName": returnVal = this.eventObjectMonthName; break;
        		case "eventCalName":   returnVal = this.eventObjectCalName; break;
        		default:               returnVal = ""; break;
        	}
        	return returnVal;
        }
        
        
	this.setFullMonthName = function(){ 
		switch (this.eventObjectMonth) {
			case 1: this.eventObjectMonthName = "January"; break;
			case 2: this.eventObjectMonthName = "February"; break;
			case 3: this.eventObjectMonthName = "March"; break;
			case 4: this.eventObjectMonthName = "April"; break;
			case 5: this.eventObjectMonthName = "May"; break;
			case 6: this.eventObjectMonthName = "June"; break;
			case 7: this.eventObjectMonthName = "July"; break;
			case 8: this.eventObjectMonthName = "August"; break;
			case 9: this.eventObjectMonthName = "September"; break;
			case 10: this.eventObjectMonthName = "October"; break;
			case 11: this.eventObjectMonthName = "November"; break;
			case 12: this.eventObjectMonthName = "December"; break;
			default: this.eventObjectMonthName = "";
		}
	}
        
	googleCalParser.prototype.eventObject.prototype.toString = function() {
		return '' + this.eventObjectYear + googleCalParser.prototype.padNumber(this.eventObjectMonth) + googleCalParser.prototype.padNumber(this.eventObjectDay) + this.eventObjectAMPM + googleCalParser.prototype.padNumber(this.eventObjectHours) + this.eventObjectMinutes + this.eventObjectTitle;
	}
}


googleCalParser.prototype.createAllEventsArray = function() {

	for (var i=0; i<googleCalParser.prototype.calendars.length; i++) {

	        var calendarName = googleCalParser.prototype.calendars[i].calName;
	        var selectEventsArray = googleCalParser.prototype.calEvents[calendarName];
	        
	        for (var j=0; j<selectEventsArray.length; j++) {
	        	googleCalParser.prototype.allChurchEventsArray.push(selectEventsArray[j]);
	        }
	}
	
	googleCalParser.prototype.sortEvents(googleCalParser.prototype.allChurchEventsArray);
}

googleCalParser.prototype.getAllEvents = function() {

        return googleCalParser.prototype.allChurchEventsArray;

}


googleCalParser.prototype.getEventsByCalendar = function(calendarName) {

        return googleCalParser.prototype.calEvents[calendarName];
}

googleCalParser.prototype.getEventsByCalendars = function(calendarNameArray) {

        var selectEventsArray = new Array();

	for (var i=0; i<googleCalParser.prototype.calendars.length; i++) {
	        selectEventsArray.push(googleCalParser.prototype.calEvents[googleCalParser.prototype.calendars[i].calName]);
	}
       
        googleCalParser.prototype.sortEvents(selectEventsArray);
        return selectEventsArray;
}


googleCalParser.prototype.getSpecificHTMLforEvents = function(idName, numEvents, eventsArray, htmlArray, toolTipClassName) {

	// idName is the id of the DIV you want this appended to
	// numEvents is the number of events you want to display
	// eventsArray is the array of event objects you are passing in
	// htmlArray is an array of options and the order that you would like to get back for each event.
	// htmlArray options: eventTitle, eventLocation, eventDesc, eventDate, eventMonthName, eventMonthNbr, eventDay, eventDateBlock, eventTime, eventHours, eventMinutes, eventAMPM, eventDateTime

	var container = document.getElementById(idName);
	
	if (numEvents > eventsArray.length) {
		numEvents = eventsArray.length;
	}
	
	for (i=0;i<(numEvents);i++)
	{
		// Create all of the html children to be used:
		
		var toolTipClass = document.createElement("p");
		toolTipClass.className = "toolTipClass";
		toolTipClass.style.display = "none";
		toolTipClass.appendChild(document.createTextNode(eventsArray[i].getAttribute(toolTipClassName)));
		
		var eventTitle = document.createElement("p");
		eventTitle.className = "eventTitle";
		eventTitle.appendChild(document.createTextNode(eventsArray[i].eventObjectTitle));

		var eventLocation = document.createElement("p");
		eventLocation.className = "eventLocation";
		eventLocation.appendChild(document.createTextNode(eventsArray[i].eventObjectLocation));

		var eventDesc = document.createElement("p");
		eventDesc.className = "eventDesc";
		eventDesc.innerHTML = eventsArray[i].eventObjectContent;

		var eventDate = document.createElement("p");
		eventDate.className = "eventDate";
		eventDate.appendChild(document.createTextNode(eventsArray[i].eventObjectDate));

		var eventMonthName = document.createElement("p");
		eventMonthName.className = "eventMonthName";
		eventMonthName.appendChild(document.createTextNode(eventsArray[i].eventObjectMonthName));

		var eventMonthNbr = document.createElement("p");
		eventMonthNbr.className = "eventMonthNbr";
		eventMonthNbr.appendChild(document.createTextNode(eventsArray[i].eventObjectMonth));

		var eventDay = document.createElement("p");
		eventDay.className = "eventDay";
		eventDay.appendChild(document.createTextNode(eventsArray[i].eventObjectDay));

		var fullMonth = eventsArray[i].eventObjectMonthName;
		var shortMonth = fullMonth.substring(0,3);
		var upperMonth = shortMonth.toUpperCase();
		
		var eventMonthShortName = document.createElement("p");
		eventMonthShortName.className = "eventMonthShortName";
		eventMonthShortName.appendChild(document.createTextNode(upperMonth));

		var eventDateBlock = document.createElement("div");
		eventDateBlock.className = "eventDateBlock";
                eventDateBlock.appendChild(eventMonthShortName);
                eventDateBlock.appendChild(eventDay);
				
		var eventYear = document.createElement("p");
		eventYear.className = "eventYear";
		eventYear.appendChild(document.createTextNode(eventsArray[i].eventObjectYear));

		var eventTime = document.createElement("p");
		eventTime.className = "eventTime";
		eventTime.appendChild(document.createTextNode(eventsArray[i].eventObjectTime));

		var eventHours = document.createElement("p");
		eventHours.className = "eventHours";
		eventHours.appendChild(document.createTextNode(eventsArray[i].eventObjectHours));

		var eventMinutes = document.createElement("p");
		eventMinutes.className = "eventMinutes";
		eventMinutes.appendChild(document.createTextNode(eventsArray[i].eventObjectMinutes));

		var eventAMPM = document.createElement("p");
		eventAMPM.className = "eventAMPM";
		eventAMPM.appendChild(document.createTextNode(eventsArray[i].eventObjectAMPM));

		var eventDateTime = document.createElement("p");
		eventDateTime.className = "eventDateTime";
		if (eventsArray[i].eventObjectTime != "") {
			eventDateTime.appendChild(document.createTextNode(eventsArray[i].eventObjectMonthName + " "  + eventsArray[i].eventObjectDay + " @ " + eventsArray[i].eventObjectTime));
		} else {
			eventDateTime.appendChild(document.createTextNode(eventsArray[i].eventObjectMonthName + " "  + eventsArray[i].eventObjectDay));
		}


		//Create Basic HTML structure for every event
		var div = document.createElement("div");
		div.className = "event";
		if((i % 2) == 1) {
			div.className = div.className + " event-odd";
		} else {
			div.className = div.className + " event-even";
		}
		if (i == 0) {
			div.className = div.className + " event-first";
		}
		if (i == (numEvents - 1)) {
			div.className = div.className + " event-last";
		}
		div.style.cursor = "default";
	
		// Loop through the list of 
		for (j=0;j<htmlArray.length;j++)
		{
			switch (htmlArray[j]) {
			      case "eventTitle":     div.appendChild(eventTitle);     break;
			      case "eventLocation":  div.appendChild(eventLocation);  break;
			      case "eventDesc":      div.appendChild(eventDesc);      break;
			      case "eventDate":      div.appendChild(eventDate);      break;
				  case "eventYear":      div.appendChild(eventYear);      break;
			      case "eventMonthName": div.appendChild(eventMonthName); break;
			      case "eventMonthNbr":  div.appendChild(eventMonthNbr);  break;
			      case "eventDay":       div.appendChild(eventDay);       break;
			      case "eventDateBlock": div.appendChild(eventDateBlock); break;
			      case "eventTime":      div.appendChild(eventTime);      break;
			      case "eventHours":     div.appendChild(eventHours);     break;
			      case "eventMinutes":   div.appendChild(eventMinutes);   break;
			      case "eventAMPM":      div.appendChild(eventAMPM);      break;
			      case "eventDateTime":  div.appendChild(eventDateTime);  break;
			}
		}
		div.appendChild(toolTipClass);
		
		container.appendChild(div);
	}
}



googleCalParser.prototype.getHTMLforEvents = function(idName, numEvents, eventsArray, toolTipClassName) {

	// idName is the id of the DIV you want this appended to
	// numEvents is the number of events you want to display
	// eventsArray is the array of event objects you are passing in
	// This method will simply use a default value of:
	// eventTitle, eventLocation, eventDateTime, eventDesc


	var htmlArray = ['eventTitle', 'eventLocation', 'eventDateTime', 'eventDesc' ];
	googleCalParser.prototype.getSpecificHTMLforEvents(idName, numEvents, eventsArray, htmlArray, toolTipClassName);

}


googleCalParser.prototype.applyToolTip = function(idName, elementClassName) {

	var classNodes = googleCalParser.prototype.getElementsByClassName(idName, elementClassName);
	
	var toolTips = googleCalParser.prototype.getElementsByClassName(idName, 'toolTipClass');

	for (i=0;i<(classNodes.length);i++)
	{
		var content = toolTips[i].firstChild.nodeValue;
		if (content !== "") {
			// I KNOW this is horrible code, but I have yet to figure out how to convert the variable i to a static nbr. toString(), valueOf() don't work.
			//  and what happens is that unless i hardcode this, I always see only the most recent content - whatever the highest value of variable i is.
			switch (i) {
				case 0: classNodes[0].onmouseover = function() {tooltip.show(toolTips[0].firstChild.nodeValue); }; break;
				case 1: classNodes[1].onmouseover = function() {tooltip.show(toolTips[1].firstChild.nodeValue); }; break;
				case 2: classNodes[2].onmouseover = function() {tooltip.show(toolTips[2].firstChild.nodeValue); }; break;
				case 3: classNodes[3].onmouseover = function() {tooltip.show(toolTips[3].firstChild.nodeValue); }; break;
				case 4: classNodes[4].onmouseover = function() {tooltip.show(toolTips[4].firstChild.nodeValue); }; break;
				case 5: classNodes[5].onmouseover = function() {tooltip.show(toolTips[5].firstChild.nodeValue); }; break;
				case 6: classNodes[6].onmouseover = function() {tooltip.show(toolTips[6].firstChild.nodeValue); }; break;
				case 7: classNodes[7].onmouseover = function() {tooltip.show(toolTips[7].firstChild.nodeValue); }; break;
				case 8: classNodes[8].onmouseover = function() {tooltip.show(toolTips[8].firstChild.nodeValue); }; break;
				case 9: classNodes[9].onmouseover = function() {tooltip.show(toolTips[9].firstChild.nodeValue); }; break;
				case 10: classNodes[10].onmouseover = function() {tooltip.show(toolTips[10].firstChild.nodeValue); }; break;
				case 11: classNodes[11].onmouseover = function() {tooltip.show(toolTips[11].firstChild.nodeValue); }; break;
				case 12: classNodes[12].onmouseover = function() {tooltip.show(toolTips[12].firstChild.nodeValue); }; break;
				case 13: classNodes[13].onmouseover = function() {tooltip.show(toolTips[13].firstChild.nodeValue); }; break;
				case 14: classNodes[14].onmouseover = function() {tooltip.show(toolTips[14].firstChild.nodeValue); }; break;
				case 15: classNodes[15].onmouseover = function() {tooltip.show(toolTips[15].firstChild.nodeValue); }; break;
				case 16: classNodes[16].onmouseover = function() {tooltip.show(toolTips[16].firstChild.nodeValue); }; break;
				case 17: classNodes[17].onmouseover = function() {tooltip.show(toolTips[17].firstChild.nodeValue); }; break;
				case 18: classNodes[18].onmouseover = function() {tooltip.show(toolTips[18].firstChild.nodeValue); }; break;
				case 19: classNodes[19].onmouseover = function() {tooltip.show(toolTips[19].firstChild.nodeValue); }; break;
				case 20: classNodes[20].onmouseover = function() {tooltip.show(toolTips[20].firstChild.nodeValue); }; break;
				case 21: classNodes[21].onmouseover = function() {tooltip.show(toolTips[21].firstChild.nodeValue); }; break;
				case 22: classNodes[22].onmouseover = function() {tooltip.show(toolTips[22].firstChild.nodeValue); }; break;
				case 23: classNodes[23].onmouseover = function() {tooltip.show(toolTips[23].firstChild.nodeValue); }; break;
				case 24: classNodes[24].onmouseover = function() {tooltip.show(toolTips[24].firstChild.nodeValue); }; break;
				case 25: classNodes[25].onmouseover = function() {tooltip.show(toolTips[25].firstChild.nodeValue); }; break;
				case 26: classNodes[26].onmouseover = function() {tooltip.show(toolTips[26].firstChild.nodeValue); }; break;
				case 27: classNodes[27].onmouseover = function() {tooltip.show(toolTips[27].firstChild.nodeValue); }; break;
				case 28: classNodes[28].onmouseover = function() {tooltip.show(toolTips[28].firstChild.nodeValue); }; break;
				case 29: classNodes[29].onmouseover = function() {tooltip.show(toolTips[29].firstChild.nodeValue); }; break;
				case 30: classNodes[20].onmouseover = function() {tooltip.show(toolTips[30].firstChild.nodeValue); }; break;				
				default: classNodes[i].onmouseover = function() {tooltip.show("'Details not available at this time...'"); };;
			} 
			//classNodes[i].onmouseover = function() {tooltip.show(toolTips[i].firstChild.nodeValue); };
			classNodes[i].onmouseout  = function() {tooltip.hide(); };
		}
	}

}


googleCalParser.prototype.getElementsByClassName = function(idName, classname) {

      var node = document.getElementById(idName);

      var a = [];

      var re = new RegExp('\\b' + classname + '\\b');

      var els = node.getElementsByTagName("*");

      for(var i=0,j=els.length; i<j; i++)

      if(re.test(els[i].className))a.push(els[i]);

      return a;
}
