//--------------------
// global variables
//--------------------
var oCalDoc;
var oCalendarFrame;
var oFrameContent;
var oCalendarTable;
var oAgendaWrapper;

var sImportedCalendarId = 'imported-calendar';

var iCurrentMonth;
var iCurrentYear;
var iCurrentDay;

var iCurrentView;

var aDaynames = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
var aMonthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

var aNodeTypes = ['',
			'element',
			'attribute',
			'text',
			'CDATA section',
			'entity reference',
			'entity',
			'processing instruction',
			'comment',
			'document',
			'document type',
			'document fragment',
			'notation'
			];
/*
    * Node.ELEMENT_NODE == 1
    * Node.ATTRIBUTE_NODE == 2
    * Node.TEXT_NODE == 3
    * Node.CDATA_SECTION_NODE == 4
    * Node.ENTITY_REFERENCE_NODE == 5
    * Node.ENTITY_NODE == 6
    * Node.PROCESSING_INSTRUCTION_NODE == 7
    * Node.COMMENT_NODE == 8
    * Node.DOCUMENT_NODE == 9
    * Node.DOCUMENT_TYPE_NODE == 10
    * Node.DOCUMENT_FRAGMENT_NODE == 11
    * Node.NOTATION_NODE == 12
*/


//====================
function calendarMorphInit()
//====================
{
//alert('function calendarMorphInit()');

		if (!document.getElementById) return alert("document.getElementById not supported");
		//if (!document.importNode) return alert("document.importNode not supported");

	//alert('iCurrentMonth = ' + iCurrentMonth + '\n' + 'iCurrentYear = ' + iCurrentYear + '\n' + 'iCurrentDay = ' + iCurrentDay + '\n');
	//alert('iframe month = ' + oCalDoc.getElementById('NCCurrentMonth').value);
	
	//--------------------
	// set context pointers
	//--------------------
	var oFrameContainer = document.getElementById('NCAL');
		if (!oFrameContainer) return;	// alert("Cannot find oFrameContainer 'NCAL'");
	
	oCalendarFrame = document.getElementById('NCALiframe');	
		if (!oCalendarFrame) return alert('Cannot find iframe "NCALiframe"');
	addEvent(oCalendarFrame, 'load', calendarMorphInit);

	oCalDoc = oCalendarFrame.contentDocument;

	iCurrentMonth = oCalDoc.getElementById('NCCurrentMonth').value;
	iCurrentYear = oCalDoc.getElementById('NCCurrentYear').value;
	iCurrentDay = oCalDoc.getElementById('NCCurrentDay').value;

	//--------------------
	// disable unwanted tooltip script
	//--------------------
	var aDocs = [document, oCalDoc];
	var rSearch = new RegExp('wz_tooltip[.]js$');
	var sSrc;

	for (var iDoc=0; iDoc < aDocs.length; iDoc++)
	{
		var aScripts = aDocs[iDoc].getElementsByTagName('script');
		for (var iScript=0; iScript < aScripts.length; iScript++)
		{
			sSrc = aScripts[iScript].getAttribute('src');
				if (rSearch.test(sSrc))
				{
					aScripts[iScript].parentNode.removeChild(aScripts[iScript]);
					break;
				}
		}
	}

	//--------------------
	// set the current view and branch
	//--------------------

	// get the current view
	var oNCCurrentView = oCalDoc.getElementById('NCCurrentView');
		if (!oNCCurrentView) return alert('Cannot find iframe element "NCCurrentView"');
	
	switch (oNCCurrentView.value)
	{
		case 'calendars/month':
			calendarMorphInitCalendar();
		break;

		case 'calendars/week':
			calendarMorphInitAgenda();
		break;
		
		case 'calendars/detail':
			calendarMorphInitDetail();
		break;
		
		default:
			alert("Cannot recognize NCCurrentView '" + oNCCurrentView.value + "'");
	}

	resetIframeHeight();
}


//====================
function calendarMorphInitDetail()
//====================
{
	document.body.setAttribute('id', 'detail-view');

	//--------------------
	// morph the search bar
	//--------------------
	morphSearchBar('HIGHLIGHTS');

}


//====================
function calendarMorphInitAgenda()
//====================
{
	document.body.setAttribute('id', 'agenda-view');
	oCalDoc.body.setAttribute('id', 'agenda-view');

	//--------------------
	// point to the old calendar table
	//--------------------
	oCalendarTable = oCalDoc.getElementById('NCALcontent');
		if (!oCalendarTable) return alert("Cannot find frame block 'NCALcontent'");

// debug
//var oTextarea = document.createElement('textarea');
//oTextarea.value = oCalendarTable.innerHTML;
//document.body.appendChild(oTextarea);

	//--------------------
	// morph the agenda header
	//--------------------
	morphAgendaHeading();

	//--------------------
	// morph the search bar
	//--------------------
	morphSearchBar('HIGHLIGHTS');

	//--------------------
	// modify the agenda markup
	//--------------------
	morphAgendaDays();
}


//====================
function morphAgendaHeading()
//====================
{
//alert('function morphAgendaHeading()');

	var sHTML = '';
	//sHTML += '\n						<div id="calendar-navigation">';
	sHTML += '\n';
	sHTML += '\n							<ul id="view-choices">';
	sHTML += '\n								<li id="agenda-view">';
	sHTML += '\n									<a href="#" onclick="loadNCalendarWeek()">Agenda View</a>';
	sHTML += '\n								</li>';
	sHTML += '\n								<li id="calendar-view" class="selected">';
	sHTML += '\n									<a href="#" onclick="loadNCalendarMonth(0)">Calendar View</a>';
	sHTML += '\n								</li>';
	sHTML += '\n							</ul>';
	sHTML += '\n';
	sHTML += '\n							<div id="next-prev-month">';
	sHTML += '\n								<h2 id="date-range"></h2>';
//	sHTML += '\n								<h2><span>September 30</span> to <span>October 12, 2010</span></h2>';
	sHTML += '\n								<a id="previous-two-weeks" href="#" onclick="loadNCalendarWeek(-2)"><span>«</span> Previous two weeks</a>';
	sHTML += '\n								<a id="next-two-weeks" href="#" onclick="loadNCalendarWeek(+2)">Next two weeks <span>»</span></a>';
	sHTML += '\n							</div>';
	//sHTML += '\n						</div><!-- #calendar-navigation -->';
	sHTML += '\n';
	
	//alert(sHTML);
	
	var aEls = oCalDoc.getElementsByTagName('ul');
	for (var iEl=0; iEl < aEls.length; iEl++)
	{
			if (aEls[iEl].className && aEls[iEl].className == 'NCALweekLinkBar')
			{
				var oOldNav = aEls[iEl];
				break;
			}
	}

	var oDiv = oCalDoc.createElement('div');
	oDiv.setAttribute('id', 'calendar-navigation');
	oDiv.innerHTML = sHTML;

	var oContainer = oOldNav.parentNode;
	oContainer.removeChild(oOldNav);
	oContainer.appendChild(oDiv);
	
	//--------------------
	// assign events to new heading
	//--------------------
/*
	// agenda & calendar views
	oLink = oCalDoc.getElementById('agenda-view');
	oLink.onclick = function()
	{
		loadNCalendarWeek();
	};

	oLink = oCalDoc.getElementById('calendar-view');
	oLink.onclick = function()
	{
		loadNCalendarMonth(0);
	};

	// previous | next month
	oLink = oCalDoc.getElementById('previous-two-weeks');
	oLink.onclick = function()
	{
		loadNCalendarWeek(-2);
	};
	
	oLink = oCalDoc.getElementById('next-two-weeks');
	oLink.onclick = function()
	{
		loadNCalendarWeek(+2);
	};
*/
	//--------------------
	// insert date range
	//--------------------
	// dates are found in the format 'Monday, August 2'

	// find first date
	var rSearch = new RegExp('\\bNCALweekDayTitle\\b');
	var aDivs = oCalDoc.getElementsByTagName('div');
	var sClass;
	var sFirstDate;
	var sLastDate;
	var oTextNode;
	//var sMsg = '';
	for (var iDiv=0; iDiv < aDivs.length; iDiv++)
	{
		sClass = aDivs[iDiv].className;
			if (rSearch.test(sClass))				//sClass > '' && 
			{
				//sMsg += iDiv + ') ' + sClass + ' - ' + aDivs[iDiv].textContent + '\n';
					if (!sFirstDate)
					{
						sFirstDate = getTextContent(aDivs[iDiv]);
					}
					else
					{
						sLastDate = getTextContent(aDivs[iDiv]);
					}
			}
			else if (sClass == 'NCALweekAgendaWrapper')
			{
				oAgendaWrapper = aDivs[iDiv];
			}
	}
	//alert(sMsg);
	
	var sSpan1;
	var sInterspan;
	var sSpan2;
	var oSpan;
	
	// 1 = Month, 2 = day
	var rWordSplitter = /\W+/;
	var aFirstDateNodes = sFirstDate.split(rWordSplitter);
	var aLastDateNodes = sLastDate.split(rWordSplitter);

	oLink = oCalDoc.getElementById('date-range');
	
		// same month - August 1-14, 2010
		if (aFirstDateNodes[2] == aLastDateNodes[2])
		{
			sInterspan = aFirstDateNodes[2] + ' ' + aFirstDateNodes[3] + ' to ' + aLastDateNodes[3] + ', ' + iCurrentYear;
		}
		// different years - December 31, 2010 to January 13, 2011
		else if (aFirstDateNodes[2] == 'December')
		{
			sSpan1 = aFirstDateNodes[2] + ' ' + aFirstDateNodes[3] + ', ' + iCurrentYear;
			sInterspan = ' to ';
			sSpan2 = aLastDateNodes[2] + ' ' + aLastDateNodes[3] + ', ' + (iCurrentYear+1);
		}
		// different months - August 30 to September 13, 2010
		else
		{
			sSpan1 = aFirstDateNodes[2] + ' ' + aFirstDateNodes[3];
			sInterspan = ' to ';
			sSpan2 = aLastDateNodes[2] + ' ' + aLastDateNodes[3] + ', ' + iCurrentYear;
		}

		if (sSpan1)
		{
			oSpan = oCalDoc.createElement('span');
			oTextNode = oCalDoc.createTextNode(sSpan1);
			oSpan.appendChild(oTextNode);
			oLink.appendChild(oSpan);
		}
		if (sInterspan)
		{
			oTextNode = oCalDoc.createTextNode(sInterspan);
			oLink.appendChild(oTextNode);
		}
		if (sSpan2)
		{
			oSpan = oCalDoc.createElement('span');
			oTextNode = oCalDoc.createTextNode(sSpan2);
			oSpan.appendChild(oTextNode);
			oLink.appendChild(oSpan);
		}

	//--------------------
	// insert event blocks inside date blocks
	//--------------------
	var oDayTitle;
	var oOldDaysEvents;
	var oNewDaysEvents;
	var sOldDate;
	var oPara;

	oDayTitle = findNextChildElement(oAgendaWrapper);
	
	while (oDayTitle)
	{
		// put date into wrapper
		sOldDate = getTextContent(oDayTitle);	// Monday, August 2 --> Monday | 1 | November
		aDateNodes = sOldDate.split(rWordSplitter);
		// delete old date
		oDayTitle.innerHTML = '';
		
		var oDiv = oCalDoc.createElement('div');
		oDiv = oDayTitle.appendChild(oDiv);

		oDiv.className = 'DayTitleBlock';

		oPara = oCalDoc.createElement('p');
		setTextContent(oPara, aDateNodes[1]);
		oPara.className = 'day-name';
		oDiv.appendChild(oPara);
		
		oPara = oCalDoc.createElement('p');
		setTextContent(oPara, aDateNodes[3]);
		oPara.className = 'day-number';
		oDiv.appendChild(oPara);
		
		oPara = oCalDoc.createElement('p');
		setTextContent(oPara, aDateNodes[2]);
		oPara.className = 'month-name';
		oDiv.appendChild(oPara);

		
		// move events into day title
		oOldDaysEvents = findNextChildElement(oAgendaWrapper, oDayTitle);
//alert('oOldDaysEvents = ' + oOldDaysEvents.nodeName + ':\n' + oOldDaysEvents.innerHTML);
		oNewDaysEvents = oOldDaysEvents.cloneNode(true);
		oAgendaWrapper.removeChild(oOldDaysEvents);
		oDayTitle.appendChild(oNewDaysEvents);
		
		oDayTitle = findNextChildElement(oAgendaWrapper, oDayTitle);
//alert('oDayTitle = ' + oDayTitle.nodeName + ':\n' + oDayTitle.innerHTML);
	}
	
	//alert('done');	
}


//====================
function morphAgendaDays()
//====================
{
}


//====================
function calendarMorphInitCalendar()
//====================
{
////alert('function calendarMorphInitCalendar()');
	document.body.setAttribute('id', 'calendar-view');
	oCalDoc.body.setAttribute('id', 'calendar-view');

	//--------------------
	// point to the old calendar table
	//--------------------
	oCalendarTable = oCalDoc.getElementById('NCALmonth');
		if (!oCalendarTable) return alert("Cannot find frame table 'NCALmonth'");

// debug
//var oTextarea = document.createElement('textarea');
//oTextarea.value = oCalendarTable.innerHTML;
//document.body.appendChild(oTextarea);

	//--------------------
	// morph the calendar header
	//--------------------
	morphCalendarHeading();

	//--------------------
	// morph the search bar
	//--------------------
	morphSearchBar();

	//--------------------
	// insert day name after each day number
	//--------------------
	morphCalendarDays();
}


//====================
function morphCalendarDays()
//====================
{
	// insert day name after each day number
/*
BEFORE:
<td class="NCALmonthDay">
	<span class='NCALmonthDate'>
		<a href='javascript:void(0);' onclick='loadNCalendarWeekSetDay(2010,7,26)'>26</a>
	</span>

AFTER:
<td class="NCALmonthDay">
	<span class='NCALmonthDate'>
		<a href='javascript:void(0);' onclick='loadNCalendarWeekSetDay(2010,7,26)'>26
			<span class="day-name">Tuesday</span>
		</a>
	</span>

*/
	var aTableBody = oCalendarTable.getElementsByTagName('tbody');
	var aTableCells = aTableBody[0].getElementsByTagName('td');

	var iDayname = 0;
	
	for (var iTC=0; iTC < aTableCells.length; iTC++)
	{
		var oParentSpan = findFirstChildWithTagName(aTableCells[iTC], 'SPAN')
			if (!oParentSpan) continue;

		var oChildNode = findFirstChildWithTagName(oParentSpan, 'A')
			if (!oChildNode) continue;

		// add day-name as the last child-node within the A

		iDayname = iTC % 7;
		var oText = oCalDoc.createTextNode(aDaynames[iDayname]);
		var oSpan = oCalDoc.createElement('div');
		oSpan.appendChild(oText);
		oSpan.className = 'day-name';
		oSpan = oChildNode.appendChild(oSpan);
		
		//--------------------
		// convert event markup
		//--------------------
		/*
			Overcome the obstacle of inconsistent markup of events by replacing it with this structure:
			<li>
				<a class="NCALmonthEvntTitle">EVENT TITLE</a>
				<div class="tooltip">
					<p class="NCALmonthEvntLocation">LOCATION</p>
					<p class="NCALmonthEvntDateTime">DATE-TIME</p>
					<p class="NCALmonthEvntShortDesc">DESCRIPTION</p>
				</div>
			</li>
		*/
		var aLIs = aTableCells[iTC].getElementsByTagName('li');

		for (var iLI=0; iLI < aLIs.length; iLI++)
		{
			var oNewEventLink = oCalDoc.createElement('a');
			oNewEventLink.onmouseover = calendarToolTip;
			oNewEventLink.onmouseout = calendarToolTipErase;

			var oNewTooltip = oCalDoc.createElement('div');
			oNewTooltip.className = 'CalendarToolTip';

			var aLinks = aLIs[iLI].getElementsByTagName('a');
			oNewEventLink.onclick = aLinks[0].onclick;
			
			// get all the event divs within the event item
			var aDivs = aLIs[iLI].getElementsByTagName('div');

			// replace each div with a span, ensuring a wrapping space around each @-sign
			for (var iDiv=0; iDiv < aDivs.length; iDiv++)
			{
				var oDiv = aDivs[iDiv];
				var sText = getTextContent(oDiv);
				var oText = oCalDoc.createTextNode(sText);

					if (oDiv.className == 'NCALmonthEvntTitle')
					{
						sText = sText.replace('@', ' @ ');
						oNewEventLink.appendChild(oText);
						oNewEventLink.className = oDiv.className;
					}
					else
					{
						var oParagraph = oCalDoc.createElement('p');
						oParagraph.appendChild(oText);
						oParagraph.className = oDiv.className;
						oNewTooltip.appendChild(oParagraph);
					}
				}
				
				// remove old content
				removeChildren(aLIs[iLI]);
				
				// add new children
				aLIs[iLI].appendChild(oNewEventLink);
				aLIs[iLI].appendChild(oNewTooltip);
		}
	}

	//--------------------
	// replace the tooltip behavior
	//--------------------
	var aAnchors = getElementsByClassName('NCALmonthEvent', 'a', oCalendarTable);
	
	for (var i=0; i < aAnchors.length; i++)
	{
		aAnchors[i].removeAttribute('onmouseover');
		aAnchors[i].removeAttribute('onmouseout');

		aAnchors[i].onmouseover = calendarToolTip;
		aAnchors[i].onmouseout = calendarToolTipErase;
	}
}


//====================
function morphSearchBar(sDefaultTab)
//====================
{
//alert('function morphSearchBar()');

	var oSearchInput = oCalDoc.getElementById('search_text');
		if (!oSearchInput) return alert("Cannot find search_text");	
	
	//--------------------
	// remove inline prompt
	//--------------------
	oSearchInput.setAttribute('value', '');
	
	var oContainer = oSearchInput.parentNode;
	
	//--------------------
	// insert Search label
	//--------------------
	var oLabel = oCalDoc.createElement("label");
	oLabel.setAttribute('id', 'search_text_label');
	oLabel.setAttribute('for', 'search_text');
	
	var oText = oCalDoc.createTextNode('Search');
	oLabel.appendChild(oText);
	
	oContainer.insertBefore(oLabel, oSearchInput);

	//--------------------
	// hide text nodes
	//--------------------
	var oSibling = oSearchInput;
	while (oSibling)
	{
//alert('oSibling.nodeType = ' + oSibling.nodeType);
			if (aNodeTypes[oSibling.nodeType] == 'element')
			{
				var sId = oSibling.getAttribute('id');
					if (sId && sId == 'tabs')
					{
						break;
					}
			}
		var oNext = oSibling.nextSibling;
			if (aNodeTypes[oSibling.nodeType] == 'text')
			{
				//alert(oSibling.nodeValue);
				oContainer.removeChild(oSibling);
				//	if (oSibling.style) oSibling.style.display = 'hidden';
			}
			else if (oSibling.nodeName == 'BUTTON' && oSibling.getAttribute('id') == '' && getTextContent(oSibling) == 'Go')
			{
				oSibling.setAttribute('id', 'search_text_button');
			}
		oSibling = oNext;
	}

	//--------------------
	// assign behaviors to tabs nav menu
	//--------------------
	var oTabsMenu = oCalDoc.getElementById('tabs');
		if (!oTabsMenu) return alert('Cannot find tabs menu "tabs"');
	var aItems = oTabsMenu.getElementsByTagName('LI');
		if (aItems.length == 0) return alert('No items found in tabs menu');

	for (var iItem=0; iItem < aItems.length; iItem++)
	{
		var oLink = findNextChildElement(aItems[iItem]);
		var oDefaultLink = null;
		oLink.onclick = activateTabMenu;
			if (oLink.id == sDefaultTab)
			{
				oLink.parentNode.className += ' selected';
				var oTabBlock = oCalDoc.getElementById('tabs-' + (iItem + 1));
				oTabBlock.className += ' selected';
			}
	}
//		if (oDefaultLink) simulateClick(oDefaultLink);
	
	//--------------------
	// recombobulate features tab (remove table markup)
	//--------------------
	// delete first cell
	// move contents of third cell to 2nd
	// delete 3rd cell
	
	var oTabBlock = oCalDoc.getElementById('tabs-1');
	var aTables = oTabBlock.getElementsByTagName('TABLE');
	var aCells = aTables[0].getElementsByTagName('TD');

	// move contents of cell 2 to cell 1
	oNode = aCells[2].firstChild;

	// copy nodes from cells
	while (oNode)
	{
		while (oNode && aNodeTypes[oNode.nodeType] != 'element')
		{
			oNode = oNode.nextSibling;
		}
			// no more elements in this cell?
			if (oNode)
			{
				oClone = oNode.cloneNode(true);
				aCells[1].appendChild(oClone);

				oNode = oNode.nextSibling;
			}
	}

	// remove inline dimensioning
	aCells[1].removeAttribute('width');

	var aImages = aCells[1].getElementsByTagName('IMG');
	var rWidth = new RegExp('\\b' + 'width:\\w*\d+px;' + '\\b', 'g');
	var rHeight = new RegExp('\\b' + 'height:\\w*\d+px;' + '\\b', 'g');

	for (var iImage=0; iImage < aImages.length; iImage++)
	{
		aImages[iImage].removeAttribute('width');
		aImages[iImage].removeAttribute('height');

		sStyle = aImages[iImage].getAttribute('style');
			if (sStyle)
			{
				sStyle = sStyle.replace(rWidth, 'max-width: 326px;');
				sStyle = sStyle.replace(rHeight, 'max-height: 200px;');
				aImages[iImage].setAttribute('style', sStyle);
			}
	}

	// delete cell 2
	aCells[2].parentNode.removeChild(aCells[2]);

	// delete cell 0
	aCells[0].parentNode.removeChild(aCells[0]);


	//--------------------
	// recombobulate tabs markup
	//--------------------
	var aTabBlocks = ['tabs-2', 'tabs-3', 'tabs-4'];

	for (var iBlock=0; iBlock < aTabBlocks.length; iBlock++)
	{
		tabsTableToList(aTabBlocks[iBlock]);
	}
	
	//--------------------
	// make search bar visible
	//--------------------
	oContainer.id = 'search-bar';
}


//====================
function tabsTableToList(sTabBlockId)
//====================
// build a list of inputs & labels from a tag soup of inputs & plain text in table cells
{
	var oTabBlock = oCalDoc.getElementById(sTabBlockId);
	var aTables = oTabBlock.getElementsByTagName('TABLE');
	var aCells = aTables[0].getElementsByTagName('TD');

	var oItem;
	var oInput;
	var oNode;
	var oText;
	
	var oList = oCalDoc.createElement('ul');
	oList.className = 'tabs-input';

	for (var iCell=0; iCell < aCells.length; iCell++)
	{
		// find the first input in the cell
		oNode = aCells[iCell].firstChild;

		while (oNode)
		{
			while (oNode && (oNode.nodeName != 'INPUT'))
			{
				//alert(oNode.nodeType + ' : ' + oNode.nodeName);
				oNode = oNode.nextSibling;

					// no more elements in this cell?
					if (!oNode) break;
			}
				if (!oNode) break;
				//alert(oNode.nodeType + ' : ' + oNode.nodeName);
	
			oItem = oCalDoc.createElement('li');
			oInput = oNode.cloneNode(false);
			oItem.appendChild(oInput);

			// get text node after INPUT
			oNode = oNode.nextSibling;
			oText = oNode.cloneNode(false);

			var oLabel = oCalDoc.createElement('label');
			oLabel.setAttribute('for', oInput.id);
			oLabel.appendChild(oText);
			
			oItem.appendChild(oLabel);
			oList.appendChild(oItem);
		}
	}	
	
	// insert list
	aTables[0].parentNode.insertBefore(oList, aTables[0]);
	
	// delete table
	aTables[0].parentNode.removeChild(aTables[0]);
}


//====================
function activateTabMenu(evt)
//====================
{
	// cancel event-bubbling
	evt = evt || window.event;
		if (evt && evt.cancelBubble) evt.cancelBubble = true;
		if (evt && evt.stopPropagation) evt.stopPropagation();

	var sHref = this.getAttribute('href');	// e.g., '#tabs-1'
	var sId = sHref.substr(1);	// delete the initial #
	
	// select the current tab block
	var iTabBlock = 1;
	var oTabBlock = oCalDoc.getElementById('tabs-' + iTabBlock);
	var rSearch = new RegExp('\\bselected\\b', 'g');
	
	while (oTabBlock)
	{
			if (oTabBlock.id == sId)
			{
				oTabBlock.className += ' selected';
			}
			else
			{
				oTabBlock.className = oTabBlock.className.replace(rSearch, '');
			}
		oTabBlock = oCalDoc.getElementById('tabs-' + ++iTabBlock);
	}

	// select the current tab menu item
	var oTabsMenu = oCalDoc.getElementById('tabs');
	var aItems = oTabsMenu.getElementsByTagName('LI');

	for (var iItem=0; iItem < aItems.length; iItem++)
	{
		aItems[iItem].className = aItems[iItem].className.replace(rSearch, '');
	}
	this.parentNode.className += ' selected';
}


//====================
function copyEventHandler(sDestinationElementId, sSourceContainerId, sSourceElementClass, iSourceElementIndex)
//====================
{
	// copy onclick event handler from old iframe element to new element
	// new elements have unique ids
	// old elements have class names                                                                                                                                                                          

	// validate elements
	var oDestination = oCalDoc.getElementById(sDestinationElementId);
		if (!oDestination) return "Cannot find parent element '" + sDestinationElementId + "'";

	var oSourceContainer = oCalDoc.getElementById(sSourceContainerId);
		if (!oSourceContainer) return "Cannot find child element '" + sSourceContainerId + "'";
	
	var aSourceElements = getElementsByClassName(sSourceElementClass, '', oSourceContainer);
		if (!aSourceElements || aSourceElements.length == 0)
			return "Cannot find child elements of class '" + sSourceElementClass + "'";

		// ensure a valid numeric index
		if (!iSourceElementIndex || iSourceElementIndex == '')
			iSourceElementIndex = 0;

		if (aSourceElements.length < (iSourceElementIndex - 1))
			return "Source element index " + iSourceElementIndex + " exceeds source elements length " + aSourceElements.length;

	// apply behavior
	oDestination.onclick = aSourceElements[iSourceElementIndex].onclick;

	// success = no error message	
	return '';
}


//====================
function applySimulateClickToIframeElementByClass(sParentPageElementId, sFrameElementId, sFrameElementClass, iFrameElementIndex)
//====================
{
	// transfer click event from a parent page element to the corresponding iframe element

	// validate elements	
	var oParent = document.getElementById(sParentPageElementId);
		if (!oParent) return "Cannot find parent element '" + sParentPageElementId + "'";

	var oChildElement = oCalDoc.getElementById(sFrameElementId);
		if (!oChildElement) return "Cannot find child element '" + sFrameElementId + "'";
	
	var aChildElements = oChildElement.getElementsByClassName(sFrameElementClass);
		if (!aChildElements || aChildElements.length < (iFrameElementIndex - 1)) return "Cannot find child elements of class '" + sFrameElementClass + "'";

		// ensure a numeric index
		if (!iFrameElementIndex || iFrameElementIndex == '') iFrameElementIndex = 0;

	// apply behavior
	oParent.onclick = function()
	{
		var aChildElements = oCalDoc.getElementById(sFrameElementId).getElementsByClassName(sFrameElementClass);
		simulateClick(aChildElements[iFrameElementIndex]);
	}

	// success = no error message	
	return '';
}


//====================
function morphCalendarHeading()
//====================
{
//alert('function morphCalendarHeading()');

	var sHTML = '';
	//sHTML += '\n						<div id="calendar-navigation">';
	sHTML += '\n';
	sHTML += '\n							<ul id="view-choices">';
	sHTML += '\n								<li id="agenda-view">';
	sHTML += '\n									<a href="#">Agenda View</a>';
	sHTML += '\n								</li>';
	sHTML += '\n								<li id="calendar-view" class="selected">';
	sHTML += '\n									<a href="#">Calendar View</a>';
	sHTML += '\n								</li>';
	sHTML += '\n							</ul>';
	sHTML += '\n';
	sHTML += '\n							<ul id="month-nav">';
	sHTML += '\n								<li class="first"><a href="#">Jan</a></li>';
	sHTML += '\n								<li><a href="#">Feb</a></li>';
	sHTML += '\n								<li><a href="#">Mar</a></li>';
	sHTML += '\n								<li><a href="#">Apr</a></li>';
	sHTML += '\n								<li><a href="#">May</a></li>';
	sHTML += '\n								<li><a href="#">Jun</a></li>';
	sHTML += '\n								<li><a href="#">Jul</a></li>';
	sHTML += '\n								<li><a href="#">Aug</a></li>';
	sHTML += '\n								<li><a href="#">Sep</a></li>';
	sHTML += '\n								<li><a href="#">Oct</a></li>';
	sHTML += '\n								<li><a href="#">Nov</a></li>';
	sHTML += '\n								<li class="last"><a href="#">Dec</a></li>';
	sHTML += '\n							</ul>';
	sHTML += '\n';
	sHTML += '\n							<div id="next-prev-month">';
	sHTML += '\n								<a id="previous-month" href="#">&lt;</a>';
	sHTML += '\n								<h2 id="current-month"></h2>';
	sHTML += '\n								<a id="next-month" href="#">&gt;</a>';
	sHTML += '\n							</div>';
	//sHTML += '\n						</div><!-- #calendar-navigation -->';
	sHTML += '\n';
	
	//alert(sHTML);

	var oDiv = oCalDoc.createElement('div');
	//alert('oDiv.nodeName = ' + oDiv.nodeName);
	oDiv.setAttribute('id', 'calendar-navigation');

	//var oTextNode = oCalDoc.createTextNode(aMonthNames[iCurrentMonth] + ' ' + iCurrentYear);
	//oLink.appendChild(oTextNode);

	oDiv.innerHTML = sHTML;
	//alert('oDiv.innerHTML = ' + oDiv.innerHTML);

	//var aContainer = getElementsByClassName('NCALmonthContainer', 'div', oCalDoc);
	var oContainer = oCalendarTable.parentNode;
	var insertedElement = oContainer.insertBefore(oDiv, oCalendarTable);
	
	//alert('oContainer:\n' + oContainer.innerHTML);

	//--------------------
	// assign events to new heading
	//--------------------

	// agenda & calendar views
	copyEventHandler('agenda-view', 'NCALmonth', 'NCALlinkWeekView');
	copyEventHandler('calendar-view', 'NCALmonth', 'NCALlinkMonthView');

	// previous | next month
	// NOTE: This is not a typo. The calendar markup assigns classname 'NCALlinkMonthNext' to the prev link and 'NCALlinkMonthPrev' to next.
	copyEventHandler('previous-month', 'NCALmonth', 'NCALlinkMonthNext');
	copyEventHandler('next-month', 'NCALmonth', 'NCALlinkMonthPrev');


	//--------------------
	// insert current month name		
	//--------------------
    debugger
	oLink = oCalDoc.getElementById('current-month');
	removeChildren(oLink);
	var oTextNode = oCalDoc.createTextNode(aMonthNames[iCurrentMonth] + " " + iCurrentYear);
	oLink.appendChild(oTextNode);

	//--------------------
	// select current month
	//--------------------
	var oElement = oCalDoc.getElementById('month-nav');
	var aLinks = oElement.getElementsByTagName('A');
		if (aLinks.length != 12) return alert('month-nav has ' + aLinks.length + ' items!');

	for (var i=0; i < aLinks.length; i++)
	{
		var iMo = i + 1;
		
			if (iMo == iCurrentMonth)
			{
				aLinks[i].className = 'selected';
				aLinks[i].onclick = '';
			}
			else
			{			
				aLinks[i].className = '';
			}

			if (iMo < iCurrentMonth)
			{
				aLinks[i].setAttribute('onclick', 'loadNCalendarMonth(-' + (iCurrentMonth - iMo) + ')');
/*
				aLinks[i].setAttribute('offset', '-' + (iCurrentMonth - iMo));
				aLinks[i].setAttribute('onclick', function()
				{
					loadNCalendarMonth(this.offset);
				});
*/
			}
			
			if (iMo > iCurrentMonth)
			{
				aLinks[i].setAttribute('onclick', 'loadNCalendarMonth(+' + (iMo - iCurrentMonth) + ')');
/*
				aLinks[i].setAttribute('offset', '+' + (iMo - iCurrentMonth));
				aLinks[i].setAttribute('onclick', function()
				{
					loadNCalendarMonth(this.offset);
				});
*/
			}			
	}
}


// from Mozilla.org:
//====================
function simulateClick(oObject)
//====================
{
  var evt = document.createEvent("MouseEvents");
  evt.initMouseEvent("click",	//  type
					 true,		//  canBubble 
					 true,		//  cancelable 
					 window,	//  view 
					 0,			//  detail 
					 0,			//  screenX 
					 0,			//  screenY 
					 0,			//  clientX 
					 0,			//  clientY 
					 false,		//  ctrlKey 
					 false,		//  altKey 
					 false,		//  shiftKey 
					 false,		//  metaKey 
					 0,			//  button 
					 null		//  relatedTarget 
					 );

  var canceled = !oObject.dispatchEvent(evt);
/*
	if (canceled)
	{
		// A handler called preventDefault
		alert("canceled");
	}
	else
	{
		// None of the handlers called preventDefault
		alert("not canceled");
	}
*/
}


//====================
function findNextChildElement(oParent, oCurrentChild)
//====================
{
	var oChildNode;
	
		if (arguments.length == 2 
		&& oCurrentChild 
		&& oCurrentChild.nodeType 
		&& aNodeTypes[oCurrentChild.nodeType] == 'element')
		{
			oChildNode = oCurrentChild.nextSibling;
		}
		else if (arguments.length >= 1
		&& oParent 
		&& oParent.nodeType 
		&& aNodeTypes[oParent.nodeType] == 'element')
		{
			// find first-child SPAN
			oChildNode = oParent.firstChild;
		}

		while (oChildNode)
		{
				if (aNodeTypes[oChildNode.nodeType] == 'element') break;
				
			oChildNode = oChildNode.nextSibling;
		}
	
	return oChildNode;
}


//====================
function findFirstChildWithTagName(oParent, sSoughtTagName)
//====================
{
	// find first-child SPAN
	var oChildNode = oParent.firstChild;

		while (oChildNode && oChildNode.nodeName != sSoughtTagName)
		{
			oChildNode = oChildNode.nextSibling;
		}
	
	return oChildNode;
}


//====================
function removeChildren(oParent)
//====================
{
		if (oParent && oParent.nodeType && aNodeTypes[oParent.nodeType] == 'element')
		{
			while (oParent.firstChild)
			{
				oParent.removeChild(oParent.firstChild);
			}
		}
}


var oCalendarToolTip;

//====================
function calendarToolTipErase(evt)
//====================
// Disappear a tooltip created by calendarToolTip()
{
	// cancel event-bubbling
	evt = evt || window.event;
		if (evt && evt.cancelBubble) evt.cancelBubble = true;
		if (evt && evt.stopPropagation) evt.stopPropagation();


		if (oCalendarToolTip 
		&& typeof(oCalendarToolTip) == "object" 
		&& oCalendarToolTip.nodeType
		&& aNodeTypes[oCalendarToolTip.nodeType] == 'element'
		&& oCalendarToolTip.parentNode)
		{
			oCalendarToolTip.parentNode.removeChild(oCalendarToolTip);
			oCalendarToolTip = null;
		}
	//removeChildren(oCalendarToolTip);
}


//====================
function calendarToolTip(evt)
//====================
// This event handler is applied to the LI element that contains (three) divs of content.
{
	// cancel event-bubbling
	evt = evt || window.event;
		if (evt && evt.cancelBubble) evt.cancelBubble = true;
		if (evt && evt.stopPropagation) evt.stopPropagation();

		// create the tooltip
		if (!oCalendarToolTip)
		{
			// copy event divs to tooltip
			var oDescription = this.nextSibling;
/*
			var oDescription = this;

			while (true)
			{
				oDescription = oDescription.nextSibling;
					if (!oDescription || oDescription.className == 'CalendarToolTip') break;
			}			
				if (!oDescription) return;
*/
			oCalendarToolTip = oDescription.cloneNode(true);
			oCalendarToolTip.setAttribute('id', 'CalendarToolTip'); // for CSS styling
			oCalendarToolTip.className = oDescription.className;

			oCalDoc.body.appendChild(oCalendarToolTip);
	
			// set position on screen
			var aPosition = findPos(this);
			var iX = aPosition[0];
			var iY = aPosition[1];
			oCalendarToolTip.style.left = iX + 'px';
			oCalendarToolTip.style.top = iY + 'px';

				// too wide? need to go left instead of right?
				if ((iX + oCalendarToolTip.offsetWidth + 100) > oCalDoc.body.offsetWidth)
				{
					oCalendarToolTip.className += ' left';
				}
//alert(oCalendarToolTip.innerHTML);
		}
}



// from Peter Paul Koch quirksmode.org/js/findpos.html
//====================
function findPos(obj)
//====================
{
	var curleft = curtop = 0;
		if (obj.offsetParent)
		{
			do
			{
				curleft += obj.offsetLeft;
				curtop += obj.offsetTop;
			}
			while (obj = obj.offsetParent);
		}

	return [curleft,curtop];
}


// add an event to an element
//====================
function addEvent(elem, evtType, func)
//====================
{
   if (elem && typeof(elem) == "object")
   {
      // first try the W3C DOM method
      if (elem.addEventListener)
      {
         elem.addEventListener(evtType, func, false);
      }
      // otherwise use the 'traditional' technique
      else
      {
         elem["on" + evtType] = func;
      }
   }
}


//====================
/*
	Developed by Robert Nyman, http://www.robertnyman.com
	Code/licensing: http://code.google.com/p/getelementsbyclassname/
*/	
//====================
var getElementsByClassName = function (className, tag, elm){
	if (document.getElementsByClassName) {
		getElementsByClassName = function (className, tag, elm) {
			elm = elm || document;
			var elements = elm.getElementsByClassName(className),
				nodeName = (tag)? new RegExp("\\b" + tag + "\\b", "i") : null,
				returnElements = [],
				current;
			for(var i=0, il=elements.length; i<il; i+=1){
				current = elements[i];
				if(!nodeName || nodeName.test(current.nodeName)) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}
	else if (document.evaluate) {
		getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = "",
				xhtmlNamespace = "http://www.w3.org/1999/xhtml",
				namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace : null,
				returnElements = [],
				elements,
				node;
			for(var j=0, jl=classes.length; j<jl; j+=1){
				classesToCheck += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]";
			}
			try	{
				elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null);
			}
			catch (e) {
				elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null);
			}
			while ((node = elements.iterateNext())) {
				returnElements.push(node);
			}
			return returnElements;
		};
	}
	else {
		getElementsByClassName = function (className, tag, elm) {
			tag = tag || "*";
			elm = elm || document;
			var classes = className.split(" "),
				classesToCheck = [],
				elements = (tag === "*" && elm.all)? elm.all : elm.getElementsByTagName(tag),
				current,
				returnElements = [],
				match;
			for(var k=0, kl=classes.length; k<kl; k+=1){
				classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
			}
			for(var l=0, ll=elements.length; l<ll; l+=1){
				current = elements[l];
				match = false;
				for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
					match = classesToCheck[m].test(current.className);
					if (!match) {
						break;
					}
				}
				if (match) {
					returnElements.push(current);
				}
			}
			return returnElements;
		};
	}
	return getElementsByClassName(className, tag, elm);
};


//====================
function resetIframeHeight()
//====================
{
//alert('resetIframeHeight()');
	// point to the iframe
	var oFrame = oCalendarFrame;	//document.getElementById('NCALiframe');

		// point to the frame's document object
		if (oFrame.contentDocument)
		{
			oFrameDoc = oFrame.contentDocument; //IE5.5+, Moz 1.0+, Opera
		}
		else if (oFrame.contentWindow)
		{
			oFrameDoc = oFrame.contentWindow.document;
		}
		else if (window.frames && window.frames['NCALiframe'])
		{
			oFrameDoc = window.frames['NCALiframe'].document; //IE5, Konq, Safari
		}
		else if (oFrame.document)
		{
			oFrameDoc = oFrame.document;
		}
		else
		{
			oFrameDoc = null;
		}

	// shrink the frame
	oFrame.style.height = '1px';

	// measure the frame's height with scrolling
	var iHeight = oFrameDoc.body.scrollHeight;
	//var iHeight = Math.max(oFrameDoc.body.offsetHeight, oFrameDoc.body.scrollHeight);
 
	// reset the height of the iframe
	oFrame.style.height = iHeight + 'px';

//alert('oFrame.style.height = ' + iHeight + 'px');

}

//====================
function setTextContent(oObject, sText)
//====================
{
		if (oObject.textContent)
		{
			oObject.textContent = sText;
		}
		else
		{
			oObject.innerHTML = sText;
		}
}
//====================
function getTextContent(oObject)
//====================
{
		if (oObject.textContent)
		{
			return oObject.textContent;
		}
		else
		{
			return oObject.innerHTML;
		}
}

function Tip(){}
function UnTip(){}


// start the ball rolling on page-load
addEvent(window, 'load', calendarMorphInit);

